PyInstaller打包的exe被逆向?手把手教你用pyinstxtractor和uncompyle6找回Python源码(附版本差异处理)
2026/6/13 6:15:54 网站建设 项目流程

Python逆向工程实战:从PyInstaller打包的exe还原源码全流程指南

当你接手一个遗留项目或需要分析某个Python应用时,经常会遇到只有打包后的exe文件的情况。本文将带你深入探索如何系统性地解构PyInstaller打包的可执行文件,逐步还原出原始Python代码。不同于基础教程,我们会重点关注版本差异处理、加密文件解密等高级实战技巧。

1. 逆向工程前的准备工作

逆向PyInstaller打包的程序需要一套专门的工具链。以下是核心工具清单及其作用:

  • Detect It Easy (DIE):用于快速识别文件类型和打包方式
  • pyinstxtractor:解包PyInstaller生成的exe文件
  • uncompyle6:将pyc字节码反编译为Python源代码
  • 010 Editor:十六进制编辑器,用于分析文件头信息

建议环境配置

pip install uncompyle6==3.8.0 git clone https://github.com/extremecoders-re/pyinstxtractor.git

版本匹配原则

  1. 解包使用的Python版本必须与打包时一致
  2. uncompyle6需要匹配Python字节码版本
  3. 加密文件解密方式随PyInstaller版本变化

注意:实际操作前请确认你有权分析目标文件,避免法律风险

2. 解包PyInstaller生成的exe文件

使用pyinstxtractor进行解包的基本命令很简单:

python pyinstxtractor.py target.exe

但实际操作中会遇到几个关键问题:

2.1 解包后的文件结构分析

成功解包后会生成target.exe_extracted目录,包含以下重要文件:

文件类型说明处理方式
main.pyc主程序字节码直接反编译
PYZ-00.pyz依赖库集合需进一步解压
pyimod*.pycPyInstaller模块可能含加密密钥

2.2 版本兼容性问题处理

不同PyInstaller版本打包的文件结构差异:

  • 4.0之前版本

    • 使用PyCrypto进行CFB模式加密
    • 依赖库存储在PYZ-00.pyz中
  • 4.0及之后版本

    • 采用tinyaes进行CTR模式加密
    • 可能出现pyc.encrypted扩展名

识别版本技巧

# 检查pyimod01_archive.pyc是否引用tinyaes uncompyle6 pyimod01_archive.pyc | grep tinyaes

3. 处理加密的字节码文件

当发现PYZ-00.pyz_extracted目录下存在.pyc.encrypted文件时,需要先解密才能反编译。

3.1 获取加密密钥

密钥通常存储在pyimod00_crypto_key.pyc中:

uncompyle6 -o key.py pyimod00_crypto_key.pyc

3.2 解密脚本选择

根据PyInstaller版本选择对应的解密方案:

PyInstaller < 4.0 解密脚本

from Crypto.Cipher import AES import zlib def decrypt_legacy(input_path, output_path, key): with open(input_path, 'rb') as inf, open(output_path, 'wb') as outf: iv = inf.read(16) cipher = AES.new(key, AES.MODE_CFB, iv) plaintext = zlib.decompress(cipher.decrypt(inf.read())) outf.write(plaintext)

PyInstaller ≥ 4.0 解密脚本

import tinyaes import zlib def decrypt_new(input_path, output_path, key): with open(input_path, 'rb') as inf, open(output_path, 'wb') as outf: iv = inf.read(16) cipher = tinyaes.AES(key, iv) plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read())) outf.write(plaintext)

关键点:解密后需要手动添加正确的pyc文件头才能被uncompyle6识别

4. 反编译字节码为源代码

获得标准pyc文件后,使用uncompyle6进行反编译:

uncompyle6 -o output.py input.pyc

4.1 常见反编译问题解决

  1. Magic Number不匹配

    • 症状:ValueError: Bad magic number in...
    • 解决:使用正确版本的Python头信息
  2. 混淆代码处理

    • 症状:反编译后出现乱码或异常控制流
    • 应对:尝试使用decompyle3等替代工具
  3. 依赖缺失导致失败

    • 症状:ImportErrorduring decompilation
    • 解决:确保原环境依赖已安装

4.2 批量反编译技巧

对于大量pyc文件,可以使用脚本批量处理:

import os from pathlib import Path def batch_decompile(root_dir): for pyc in Path(root_dir).rglob('*.pyc'): try: os.system(f'uncompyle6 -o {pyc.with_suffix(".py")} {pyc}') except Exception as e: print(f"Failed on {pyc}: {str(e)}")

5. 高级技巧与疑难问题处理

5.1 Python版本头修复

当pyc文件头损坏时,需要手动修复。各版本Python的头信息:

Python版本魔数前缀
3.7\x42\x0d\x0d\x0a
3.8\x55\x0d\x0d\x0a
3.9\x61\x0d\x0d\x0a
3.10\x6f\x0d\x0d\x0a

修复示例:

def fix_pyc_header(pyc_path, version): headers = { '3.8': b'\x55\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0' } with open(pyc_path, 'rb+') as f: original = f.read() f.seek(0) f.write(headers[version] + original[16:])

5.2 依赖库路径修复

解包后的依赖可能路径混乱,需要调整:

  1. 检查PYZ-00.pyz_extracted内容
  2. 将需要的库复制到项目目录
  3. 修改导入语句匹配新路径

5.3 反编译失败后的备选方案

当标准工具失效时,可以尝试:

  1. pycdc:支持更多Python版本的字节码
  2. 手动分析dis模块输出
    import dis, marshal with open('file.pyc', 'rb') as f: f.seek(16) # Skip header dis.dis(marshal.load(f))
  3. 商业工具:如Decompyle++等

在实际项目中,我遇到过PyInstaller 5.7打包的程序,其加密方式又有变化。这时需要分析pyimod00_crypto_key.pyc的具体实现来调整解密逻辑。记住逆向工程是不断试错的过程,保持耐心是关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询