第 78 章 Python 字节码逆向专项
Python 应用(尤其 PyInstaller 打包的)是现代恶意软件和商业软件的常见形态。Python 字节码逆向的核心:pyc 文件结构、反编译工具、版本差异、代码混淆(Pyarmor 等)。掌握后,绝大多数 Python 目标等于"读源码"。
📍 知识点地图 | 主题:Python字节码 | 前置:第14章 | 后续:— | 核心概念:pyc结构、uncompyle6、PyInstaller、Pyarmor
78.1 Python 程序形态(先分类)
形态 1: .py 源码(直接读)
形态 2: .pyc 字节码(编译产物)→ 反编译
形态 3: PyInstaller 打包(exe)→ 解包 → pyc
形态 4: Pyarmor 混淆 → 特殊处理
形态 5: 嵌入式(游戏/固件里的 Python)→ 提取后同 2
识别: 文件头特征/字符串(pyinstaller: "PyInstaller" 字符串)
78.2 pyc 文件结构(懂格式才能处理)
pyc 头(Python 3.7+):
magic(4 字节: 版本标识)
flags(4 字节)
timestamp(4 字节)/ hash-based(可选)
大小(4 字节)
之后: code object(marshal 序列化的代码对象)
code object 结构:
co_code(字节码指令)
co_consts(常量元组: 数字/字符串/嵌套 code)
co_names(名称元组: 变量名/函数名)
co_varnames(局部变量)
co_freevars/co_cellvars(闭包变量)
反编译 = 从 code object 还原 Python 源码
78.3 反编译工具链
# 1. uncompyle6(经典,支持到 3.8)
uncompyle6 -o out_dir app.pyc
# 2. decompyle3(3.7-3.8 改进版)
# 3. pycdc(C++ 实现,3.9+ 可用,需编译)
git clone https://github.com/zrax/pycdc && cd pycdc && cmake . && make
./pycdc app.pyc
# 4. 新版 Python(3.9-3.13+): pycdc 仍是主力
# 5. 查看字节码(工具不行时手动):
python3 -m dis app.pyc # dis 模块反汇编
版本不匹配的处理
pyc 的 magic 对应 Python 版本(不同版本字节码不同)
工具报"unsupported version" → 换对应版本工具/编译 pycdc 支持
# 识别版本: magic 值对照表 / 前 4 字节
78.4 手动字节码分析(工具失败时)
# 读取并反汇编 code object
import marshal, dis, struct
with open("app.pyc", "rb") as f:
f.read(16) # 跳过 pyc 头
code = marshal.load(f) # 反序列化 code object
dis.dis(code) # 打印字节码
# 递归: code.co_consts 里嵌套的 code object
for c in code.co_consts:
if hasattr(c, "co_code"):
dis.dis(c)
常见字节码模式(对照源码)
# 模式 1: 比较(XOR 校验的常见形态)
# 字节码: LOAD_FAST/LOAD_CONST/BINARY_XOR/COMPARE_OP
# 源码: if a[i] ^ key[i] != expected[i]:
# 模式 2: 字符串处理
# LOAD_CONST 字符串 → 拼接/比较
# 模式 3: 条件分支
# POP_JUMP_IF_FALSE/POP_JUMP_IF_TRUE → if/循环
# 反推思路:
# 从常量表(co_consts)直接拿字符串/数字
# 从字节码恢复逻辑顺序
78.5 PyInstaller 解包
# 1. 识别: 文件里有 "PyInstaller" / "pyi-"
# 2. 解包工具(pyinstxtractor):
git clone https://github.com/extremecoders-re/pyinstxtractor
python3 pyinstxtractor.py app.exe
# 输出: app.exe_extracted/
# PYZ-00.pyz(压缩的 pyc 库)
# 主脚本 pyc(如 app.pyc)
# struct 等内置模块
# 3. 解 PYZ(uncompyle6 或 pyinstxtractor 附带功能)
# 4. 反编译 pyc(78.3)
# 5. 修复 pyc 头(magic 不匹配时从 struct 模块复制)
78.6 Pyarmor 混淆处理
识别: 字符串 "pyarmor" / PyArmor 相关导入 / 特殊运行时
Pyarmor 原理:
□ 字节码加密 + 运行时解密(不是简单混淆)
□ 核心代码被加密,运行时才解密执行
处理思路:
1. 运行时 dump(最有效):
Frida/调试器断在解密后 → dump 内存中的 code object
或修改运行时(hook marshal.loads)dump 解密结果
2. 静态: 找到解密密钥 → 手动解密(较难)
3. 工具: PyArmor 相关的社区脱壳工具(更新快,按版本搜)
4. 行为级: 动态观察(第 42 章方法)当兜底
78.7 实战工作流(端到端)
目标: 分析一个 PyInstaller 打包的恶意软件
1. 识别: file/strings → PyInstaller 特征
2. 解包: pyinstxtractor → 提取主脚本 pyc
3. 反编译: pycdc/uncompyle6 → 还原源码
4. 分析: 源码直接读(找 C2/持久化/窃取逻辑)
5. 混淆? Pyarmor → 运行时 dump
6. 动态验证: 沙箱行为对照源码
7. 产出: 源码级理解 + IOC
78.8 检查清单
□ 形态识别(源码/pyc/PyInstaller/Pyarmor)
□ pyc 版本识别(magic)
□ 工具反编译(uncompyle6/pycdc)
□ 失败 → 手动 dis 分析
□ PyInstaller 解包(pyinstxtractor)
□ Pyarmor → 运行时 dump
□ 嵌套/闭包 code object 完整提取
□ 常量表/字符串提取(co_consts)
□ 报告: 还原的源码+行为
动手练习
- 写一个 Python 程序编译成 pyc,用 uncompyle6 和 pycdc 反编译,对比还原质量。
- 用 PyInstaller 打包一个测试程序,pyinstxtractor 解包并还原源码。
- 用 dis 手动分析一个含 XOR 校验的 pyc,从字节码反推源码逻辑。
- 尝试对 Pyarmor 混淆的测试样本做运行时 dump(隔离环境)。
深入阅读
- 仓库:
skills/reverse-engineering/languages.md(Python 字节码章节:opcode 重映射/Pyarmor 静态解包/版本专项) - 仓库:
skills/reverse-engineering/tools.md(Python 字节码/PyInstaller 章节) - 仓库:
skills/reverse-engineering/patterns*.md(Python 相关 CTF 模式) - 仓库:
skills/malware-analysis/SKILL.md(Python 恶意软件分析衔接)