常用工具
反编译工具
pylingual
https://www.pylingual.io/
uncompyle6
最常用的 Python 2/3 字节码反编译器,支持多种 Python 版本。
1 2 3 4 5 6
| pip install uncompyle6
uncompyle6 file.pyc > file.py uncompyle6 -o output_dir/ file.pyc
|
decompyle3
专门用于 Python 3.7+ 的反编译器。
1 2 3 4 5 6
| pip install decompyle3
decompyle3 file.pyc decompyle3 file.pyc -o output.py
|
pycdc
C++ 编写的 Python 字节码反编译器,速度快,支持 Python 3.7-3.11。
1 2 3 4 5 6 7 8
| git clone https://github.com/zrax/pycdc.git cd pycdc cmake . make
./pycdc file.pyc > file.py
|
uncompyle2
专门用于 Python 2.x 的反编译器。
1 2 3 4 5
| pip install uncompyle2
uncompyle2 file.pyc > file.py
|
打包程序提取工具
pyinstxtractor
用于提取 PyInstaller 打包的程序中的 Python 文件。
1 2 3 4 5 6
| wget https://raw.githubusercontent.com/extremecoders-re/pyinstxtractor/master/pyinstxtractor.py
python pyinstxtractor.py packed_file.exe
|
py2exe 提取
py2exe 打包的程序通常包含 Python 字节码,可以使用反编译工具。
字节码分析工具
dis
Python 标准库,用于反汇编 Python 字节码。
1 2 3 4 5 6 7 8
| import dis import marshal
with open('file.pyc', 'rb') as f: f.read(16) code = marshal.load(f) dis.dis(code)
|
uncompyle6 (交互模式)
可以查看字节码而不完全反编译。
1
| uncompyle6 --show-asm file.pyc
|
其他工具
strings
提取文件中的字符串,用于快速定位关键信息。
1 2
| strings file.pyc | grep -i flag strings file.exe | grep -i python
|
file
识别文件类型。
hexdump / xxd
查看文件的十六进制内容,分析文件结构。
1 2
| hexdump -C file.pyc | head -20 xxd file.pyc | head -20
|
Python 版本检测工具
检测 .pyc 文件的 Python 版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import struct import sys
def get_python_version(pyc_file): with open(pyc_file, 'rb') as f: magic = f.read(4) magic_map = { b'\x03\xf3\r\n': 'Python 2.7', b'\x42\r\r\n': 'Python 3.2', b'\x33\r\r\n': 'Python 3.3', b'\x41\r\r\n': 'Python 3.4', b'\x55\r\r\n': 'Python 3.5', b'\x16\r\r\n': 'Python 3.6', b'\x33\x0d\r\n': 'Python 3.7', b'\x42\x0d\r\n': 'Python 3.8', b'\x55\x0d\r\n': 'Python 3.9', b'\x6f\x0d\r\n': 'Python 3.10', b'\x6f\x0d\r\n': 'Python 3.11', } return magic_map.get(magic, 'Unknown')
|
逆向步骤
Python 逆向的一般流程:识别文件类型 → 提取 Python 代码 → 反编译 → 分析逻辑 → 获取 flag。
步骤1:识别文件类型
首先确定目标文件的类型和打包方式。
1 2 3 4 5 6 7 8 9 10
| file target_file
|
判断打包方式:
步骤2:提取 Python 代码/字节码
根据文件类型选择不同的提取方法。
情况1:.pyc 文件
直接使用反编译工具,无需额外提取。
1 2 3
| uncompyle6 file.pyc > file.py
pycdc file.pyc > file.py
|
情况2:PyInstaller 打包的程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| python pyinstxtractor.py packed_file.exe
python -c " import struct import sys
# 读取提取的 pyc 文件 with open('extracted_file.pyc', 'rb') as f: data = f.read()
# 添加 magic number(根据 Python 版本选择) magic = b'\x55\x0d\r\n' # Python 3.9 示例 timestamp = b'\x00' * 4 size = b'\x00' * 4
with open('fixed_file.pyc', 'wb') as f: f.write(magic + timestamp + size + data) "
uncompyle6 fixed_file.pyc > output.py
|
情况3:py2exe 打包的程序
1 2 3 4 5 6 7
|
uncompyle6 file.pyc > file.py
|
情况4:源代码混淆/加密
步骤3:反编译字节码
使用合适的反编译工具将字节码转换为源代码。
1 2 3 4 5 6 7 8 9 10 11 12 13
| uncompyle6 file.pyc > file.py
./pycdc file.pyc > file.py
decompyle3 file.pyc -o file.py
|
处理反编译错误:
-
某些代码可能无法完全反编译
-
查看错误信息,手动修复
-
使用 dis 模块分析字节码作为补充
步骤4:分析代码逻辑
理解代码的功能和逻辑流程。
常见分析点:
步骤5:动态调试(可选)
如果静态分析困难,可以尝试动态调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| print("Debug:", variable_name)
import pdb pdb.set_trace()
from IPython import embed embed()
import logging logging.basicConfig(level=logging.DEBUG)
|
常见问题处理
问题1:无法识别 Python 版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| magic_numbers = [ b'\x03\xf3\r\n', b'\x42\r\r\n', b'\x33\r\r\n', b'\x41\r\r\n', b'\x55\r\r\n', b'\x16\r\r\n', b'\x33\x0d\r\n', b'\x42\x0d\r\n', b'\x55\x0d\r\n', b'\x6f\x0d\r\n', ]
for magic in magic_numbers: try: pass except: continue
|
问题2:PyInstaller 提取的文件缺少头部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def fix_pyc_header(pyc_file, output_file, python_version='3.9'): magic_map = { '3.9': b'\x55\x0d\r\n', '3.8': b'\x42\x0d\r\n', '3.7': b'\x33\x0d\r\n', } with open(pyc_file, 'rb') as f: data = f.read() magic = magic_map.get(python_version, b'\x55\x0d\r\n') timestamp = b'\x00' * 4 size = b'\x00' * 4 with open(output_file, 'wb') as f: f.write(magic + timestamp + size + data)
|
问题3:反编译结果不完整
1 2 3 4 5 6 7 8
| import dis import marshal
with open('file.pyc', 'rb') as f: f.read(16) code = marshal.load(f) dis.dis(code)
|