pycdc 上手指南:从 .pyc 还原 Python 源码
有一次,我需要弄明白一个陈年 .pyc 里到底藏着什么逻辑,源代码早丢了。这种时候,pycdc 就能派上用场。
pycdc 是用 C++ 开发的 Python 字节码反编译工具,支持 1.0 到 3.13 全系版本。它包含两个核心部分:pycdas 负责反汇编,输出字节码指令;pycdc 则直接生成 Python 源码。我用过几个同类的工具,pycdc 在版本兼容和还原精度上做得最扎实,虽然偶尔也会有点小毛病。
编译安装
用 CMake 构建,没什么特别的:
git clone <project_repo>
cd pycdc
cmake -DCMAKE_BUILD_TYPE=Release .
make -j$(nproc)
Release 模式足够日常使用,需要调试时换成 Debug。
基本用法
最常见的场景:源码丢了,只剩 .pyc。直接反编译:
./pycdc important_script.pyc
如果你只关心字节码长什么样,想看看解释器怎么处理指令的,用 pycdas:
./pycdas third_party_module.pyc
不同 Python 版本的字节码格式差异很大,pycdc 能自动识别,但出错时最好用 -v 明确指定版本:
# Python 2.7 老代码
./pycdc -v 2.7 legacy_code.pyc
# 3.11 引入的新字节码特性
./pycdc -v 3.11 modern_feature.pyc
一次处理多个文件
真要批量搞一堆 .pyc,一个 for 循环就够了:
#!/bin/bash
for file in *.pyc; do
echo "处理文件:$file"
./pycdc "$file" > "${file%.pyc}.py"
done
我习惯把输出重定向到文件,然后手动检查一遍——自动反编译偶尔会在代码间插入奇怪的空行,或者缩进不太对。
验证反编译效果
项目自带了测试脚本,可以用来检验反编译的准确性:
# 跑指定测试用例
python tests/run_tests.py --filter test_functions
# 多进程并行,快很多
python tests/run_tests.py -j 4
测试覆盖了表达式、控制流、异常处理等场景,自己修改源码后跑一遍测试很有用。
内部工作流程
pycdc 背后大致是三个步骤:字节码解析 → 构建抽象语法树(AST)→ 生成源代码。解析部分由 pyc_code.cpp 和 bytecode.cpp 处理,AST 的构建在 ASTree.cpp 里,最后输出可读代码。分层的好处是,适配新 Python 版本时,只要在 bytes 目录下加对应的解析模块,不用大改其他逻辑。
常遇到的坑
- 反编译结果不完整:这时不能只看反编译输出,得结合 pycdas 的指令流一起分析。操作码的定义全在 bytecode_ops.inl 里,遇到陌生的可以查一下。
- 版本识别错乱:直接
-v强制指定。bytes 目录下面能看到该版本是否有对应的实现。 - 复杂结构还原不理想:碰到闭包、生成器、装饰器嵌套很深的代码,还原后的样子可能不太自然。我的办法是先理解大致流程,再分块细看,别指望一次输出完美源码。
pycdc 目前已经支持到 Python 3.13,更新节奏还算快。用它不光能应急恢复源码,顺带也能加深对 Python 底层执行机制的感觉。

