☰
pycdc实战教程:把Python 1.0到3.13的.pyc字节码还原成源代码
2026/10/17 2:23:17 网站建设 项目流程

pycdc实战教程:把Python 1.0到3.13的.pyc字节码还原成源代码

【免费下载链接】pycdcC++ python bytecode disassembler and decompiler项目地址: https://gitcode.com/GitHub_Trending/py/pycdc

手上有个f-string.3.7.pyc——Python 3.7 编译出来的字节码文件,不反编译就看不到里面写了什么。pycdc 就是干这件事的工具:一个用 C++ 编写的 Python 字节码反汇编器和反编译器(项目名 Decompyle++),包含pycdas(输出字节码指令流)和pycdc(直接输出还原后的源码)两个可执行程序,覆盖 Python 1.0 到 3.13 共 28 个版本。本文跟着仓库里的一份真实 .pyc,把编译、反编译、验证、排错的完整流程走一遍。

用 CMake 把 pycdc 和 pycdas 编译出来

源码基于 C++11,构建系统要求 CMake 3.12 以上,具体见 CMakeLists.txt。克隆仓库后在根目录执行:

git clone https://gitcode.com/GitHub_Trending/py/pycdc cd pycdc cmake . make

完成后根目录会多出pycdc和pycdas两个可执行文件。需要排查内部状态时,可以在 CMake 阶段追加开关:-DENABLE_BLOCK_DEBUG=ON打开代码块调试输出,-DENABLE_STACK_DEBUG=ON打开栈调试输出,-DCMAKE_BUILD_TYPE=Debug则生成调试符号,三者默认都是关闭的。✅

跟一份真实 .pyc 走完整流程

仓库自带测试素材:tests/compiled/ 下有 190 份由不同 Python 版本编译的 .pyc 文件,从 1.5 到 3.12 都有对应样本。挑一份直接反编译:

./pycdc tests/compiled/f-string.3.7.pyc

结果直接打印到标准输出,开头两行是文件头——声明由 Decompyle++ 生成,并标明来源文件名和 Python 版本,后面就是还原出的源代码。🎯 要把结果落盘而不是打到终端,用-o指定输出文件:

./pycdc -o out.py tests/compiled/f-string.3.7.pyc

想看执行逻辑而不是源码,换pycdas🔍:

./pycdas tests/compiled/f-string.3.7.pyc

它按 Code 对象逐个展开:文件名、对象名、参数个数、名字表([Names])、常量表([Constants]),最后是[Disassembly]指令序列。两个实用开关:--pycode-extra显示 PyCode 对象的额外字段(如行号表),--show-caches显示 Python 3.11+ 反汇编中被默认隐藏的 CACHE 指令。

处理 marshal 导出的代码对象

如果拿到的不是 .pyc,而是marshal.dumps(compile(...))导出的二进制代码对象,两个工具同样支持,但必须显式指定版本——marshal 对象本身不带版本元数据:

./pycdc -c -v 3.7 code_object.bin

-c表示按 marshal 代码对象加载,-v后面的版本号必须是x.y这种格式,少一段会直接报错退出。

用仓库自带测试批量验证还原质量 🧪

README 提供了make check入口,底层调用 tests/run_tests.py,要求本机装有 Python 3.6+:

make check JOBS=4

JOBS=4控制并行测试进程数,FILTER=xxxx可以只跑名字匹配的部分用例。也可以绕过 make 直接调脚本:

python3 tests/run_tests.py -j 8 --filter f-string

它的判定方式值得留意:对每个测试,先反编译tests/compiled/下的 .pyc,再用scripts/token_dump把还原出的源码转成 token 流,与tests/tokenized/里的预期结果逐行比对。比较的是 token 而非原文,空格、换行这类无关差异不会造成误报;tests/xfail/目录则存放已知还原失败、暂不要求通过的用例。

反编译失败时按这个顺序排查

⚠️ 先分清是"加载不了"还是"还原不了":

  • 报错Could not load file:文件头与版本不匹配、文件损坏,或它其实是 marshal 对象而非 .pyc——后者要改用-c -v加载。
  • -v报Unable to parse version string:版本号必须写成3.7这种x.y格式,不能是37。
  • stdout 空、只有 stderr 有输出:pycdc 把还原的源码写 stdout、错误信息写 stderr,排查时先分开看。
  • 部分逻辑缺失:先用 pycdas 查看指令流和常量表,确认是字节码结构复杂,还是反编译中途失败。
  • 老版本或新版本失败:对照 bytes/ 目录确认目标版本有对应实现文件,目前覆盖 1.0–3.13 共 28 个版本。

仓库的scripts/目录里还有两个面向维护者的工具可以顺便认识:pymultic用于跨多版本 Python 批量编译测试用例,Dockerfile.pybuild则用来构建旧版 Python 的容器环境——190 份测试 .pyc 就是靠它们产出的。

写在最后

pycdc 的价值在于它是少数同时覆盖 Python 1.0 到 3.13 的开源反编译工具链,GPLv3 许可,结构清晰:版本差异解析在 bytes/、语法树构建在 ASTree.cpp、反编译入口在 pycdc.cpp。做代码审计、逆向学习和字节码机制研究的读者都适合上手;拿到一份来路不明的 .pyc 时,先跑 pycdas 摸清指令流,再跑 pycdc 还原源码,基本能覆盖多数排查场景。

【免费下载链接】pycdcC++ python bytecode disassembler and decompiler项目地址: https://gitcode.com/GitHub_Trending/py/pycdc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询