常用工具

反编译工具

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 字节码,可以使用反编译工具。

1
2
3
# 先提取 py2exe 打包的文件
# 通常需要解压或使用工具提取
# 然后使用 uncompyle6 反编译

字节码分析工具

dis

Python 标准库,用于反汇编 Python 字节码。

1
2
3
4
5
6
7
8
import dis
import marshal

# 反汇编 .pyc 文件
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

识别文件类型。

1
2
3
file target_file
# 输出:Python script, ASCII text executable
# 或:PE32 executable (console) Intel 80386, for MS Windows

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)
# Python 版本对应的 magic number
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 命令识别
file target_file

# 常见类型:
# - .py:Python 源代码(直接查看)
# - .pyc:Python 字节码文件
# - .pyo:优化的 Python 字节码文件
# - .exe:可能是 PyInstaller、py2exe 等打包的程序
# - .pyd:Python 扩展模块(Windows)
# - .so:Python 扩展模块(Linux)

判断打包方式

  • PyInstaller:文件开头有 MEI 标识,可以使用 pyinstxtractor 提取

  • py2exe:Windows 下打包,通常包含 Python DLL

  • cx_Freeze:类似 PyInstaller

  • Nuitka:编译为机器码,较难逆向

步骤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
# 1. 使用 pyinstxtractor 提取
python pyinstxtractor.py packed_file.exe

# 2. 在提取的目录中找到 .pyc 文件
# 通常文件名类似:packed_file.exe_extracted/xxx.pyc

# 3. 修复 .pyc 文件头部(如果需要)
# PyInstaller 提取的 .pyc 可能缺少 magic number
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)
"

# 4. 反编译修复后的文件
uncompyle6 fixed_file.pyc > output.py

情况3:py2exe 打包的程序

1
2
3
4
5
6
7
# 1. 解压或提取(可能需要使用 7zip 或其他工具)
# py2exe 打包的文件通常在 _internal 目录中

# 2. 查找 .pyc 或 .pyo 文件

# 3. 直接反编译
uncompyle6 file.pyc > file.py

情况4:源代码混淆/加密

1
2
3
4
5
6
7
8
9
# 1. 如果代码被混淆,尝试:
# - 使用 strings 提取字符串
# - 分析混淆逻辑
# - 动态调试

# 2. 如果代码被加密:
# - 查找密钥
# - 分析加密算法
# - 编写解密脚本

步骤3:反编译字节码

使用合适的反编译工具将字节码转换为源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 方法1:uncompyle6(推荐,支持版本多)
uncompyle6 file.pyc > file.py

# 方法2:pycdc(速度快,支持新版本)
./pycdc file.pyc > file.py

# 方法3:decompyle3(Python 3.7+)
decompyle3 file.pyc -o file.py

# 如果反编译失败,尝试:
# - 检查 Python 版本是否匹配
# - 尝试其他反编译工具
# - 直接分析字节码(使用 dis)

处理反编译错误

  • 某些代码可能无法完全反编译

  • 查看错误信息,手动修复

  • 使用 dis 模块分析字节码作为补充

步骤4:分析代码逻辑

理解代码的功能和逻辑流程。

1
2
3
4
5
6
# 分析要点:
# 1. 查找关键函数
# 2. 理解输入输出
# 3. 追踪 flag 生成逻辑
# 4. 识别加密/编码算法
# 5. 查找硬编码的密钥或常量

常见分析点

  • 输入验证:检查用户输入的验证逻辑

  • 加密算法:识别使用的加密方法(AES、RSA、XOR 等)

  • 编码方式:Base64、Hex、ROT13 等

  • 字符串操作:字符串拼接、替换等

  • 条件判断:if/else 分支逻辑

  • 循环结构:for/while 循环的处理

步骤5:动态调试(可选)

如果静态分析困难,可以尝试动态调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 方法1:修改代码添加调试输出
print("Debug:", variable_name)

# 方法2:使用 pdb 调试器
import pdb
pdb.set_trace()

# 方法3:使用 IPython
from IPython import embed
embed()

# 方法4:使用 logging
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 number
magic_numbers = [
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/3.11
]

# 逐个尝试
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
# 修复 .pyc 文件头部
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
# 使用 dis 模块分析字节码作为补充
import dis
import marshal

with open('file.pyc', 'rb') as f:
f.read(16) # 跳过头部
code = marshal.load(f)
dis.dis(code) # 显示字节码