pyinstxtractor 实战教程:一条命令拆开 PyInstaller 打包的 exe,把源码找回来
【免费下载链接】pyinstxtractorPyInstaller Extractor项目地址: https://gitcode.com/gh_mirrors/py/pyinstxtractor
凌晨一点,群里甩过来一个 exe:"这程序好像在往我们内网偷偷传东西,帮我看看它到底干了什么。"文件不大,双击却只能看到黑框一闪。它用 PyInstaller 打包过——Python 源码被塞进了二进制壳子里,普通手段根本打不开。而你手边只需要一个脚本:pyinstxtractor。它能完整拆开 PyInstaller 生成的可执行文件,把里面的 Python 字节码(pyc)全部提出来,顺带修好文件头,让反编译器一认就认出来。下面这一路,我们边走边拆。
先搞明白:它撬开的到底是什么黑盒
PyInstaller 是 Python 圈最常用的打包工具,把你的.py脚本和一堆依赖一起编译进一个独立可执行文件。对拥有源码的开发者来说,这是"一键分发";但对只拿到成品的人而言,这等于一个焊死的铁盒子——你要看的东西全在里头,却一个字符都读不出来。
pyinstxtractor 就是那把开盒工具。它本身就是单个 Python 脚本,不用装 PyInstaller,不用配环境,下载即用。兼容 Python 2.x 和 3.x,覆盖 PyInstaller 从 2.0 一路到 6.19.0 的所有主流版本。这意味着你在现实中遇到的绝大多数打包程序,它都能啃动。
把它请回家,只需要一条命令
git clone https://gitcode.com/gh_mirrors/py/pyinstxtractor克隆完你会发现,整个项目就俩文件:一个README.md,一个pyinstxtractor.py。核心能力全在这一个脚本里,这正是它讨人喜欢的地方——没有复杂的安装步骤,没有依赖地狱,拿到就能干活。
拆包现场:看它在命令行里表演
把脚本和待拆的 exe 放在一起,然后执行:
python pyinstxtractor.py 你的程序.exe运行时会打出一串带[+]的日志,每一行都是关键情报:
[+] Processing dist\test.exe [+] Pyinstaller version: 2.1+ [+] Python version: 36 [+] Length of package: 5612452 bytes [+] Found 59 files in CArchive [+] Beginning extraction...please standby [+] Possible entry point: pyiboot01_bootstrap.pyc [+] Possible entry point: test.pyc [+] Found 133 files in PYZ archive [+] Successfully extracted pyinstaller archive: dist\test.exe逐行翻译给你听:工具先报告它检测到的 PyInstaller 版本和打包时的 Python 版本(这里 36 就是 3.6),再告诉你整个包多大、CArchive 里塞了多少文件。接着重点来了——Possible entry point标记出最可能是主程序入口的 pyc 文件,这就是你反编译时优先下手的目标。最后统计 PYZ 归档里的文件数并宣告成功。
拆出来的东西,长什么样
执行完,当前目录会多出一个你的程序名_extracted文件夹,里面就是战利品:
- 入口点 pyc 文件,比如
test.pyc,通常是主逻辑所在; PYZ-00.pyz_extracted/子目录,装着程序引用的第三方库字节码;- 各类资源文件,原样还原,目录结构保持完整。
关键一步在这里:pyinstxtractor 已经自动修好了 pyc 的文件头(魔术数字、时间戳这些),所以你可以直接丢给反编译器读:
uncompyle6 你的程序.exe_extracted/test.pyc uncompyle6 你的程序.exe_extracted/PYZ-00.pyz_extracted/__future__.pyc主流的 uncompyle6、pycdc(Decompyle++)都能直接消化这些修复过的文件。到这一步,一个"焊死的盒子"就变成了一地摊开的零件,代码逻辑近在眼前。
哪些时刻,它最能帮你省时间
应急响应,查可疑程序。安全人员拿到来路不明的 exe,最想知道它有没有后门、有没有偷偷收集数据。拆开看字节码,比瞎猜快一百倍。这类程序有时还会被恶意篡改解压数据,脚本里那句 sanity check 就是为了揪出这种猫腻。
祖传项目源码丢失后的抢救。老项目只剩发布版 exe,源码早不知道丢哪儿了。拆出 pyc 再反编译,业务逻辑能还原个七七八八,维护老系统不至于抓瞎。
学习优秀实现的内部构造。想研究某个开源项目被打包后的真实依赖结构?拆开一目了然:它引用了哪些库、入口怎么写、资源怎么组织,全摆在面前,是最好的活教材。
打包质量与敏感信息审计。开发团队自己也能拿它反向检查:有没有把不该带的东西(密钥、内网地址、多余依赖)打包进去?有没有版本兼容隐患?发布前自查一遍,比上线后翻车强。
老手都在意的几个细节
环境版本最好对齐。官方反复强调:尽量用与打包时相同版本的 Python 运行 pyinstxtractor,尤其是在解压 PYZ 归档时,版本不匹配可能触发解组(unmarshalling)错误。运气好能跳过,运气差就卡壳。
加密的归档会原样转储。如果程序对 PYZ 做了加密,工具不会硬解,而是把原始数据照原样导出来,留给你后续用对应手段解密。这属于"先保住现场"的务实策略。
没名字的文件会被随机命名。打包产物里偶尔会混入无名字节或非法编码的文件名,脚本会用 UUID 随机起名并打印警告,保证不丢数据、不打断流程。重名文件也会自动加后缀避开冲突。
别小看路径安全处理。恶意包可能用../之类的手法试图把文件写到提取目录之外,脚本会把路径里的..替换掉、剥掉开头的/,把一切圈在提取目录里。
它背后到底干了什么
原理其实不玄乎,五步走完:
- 找签名:在文件尾部搜索 PyInstaller 特有的魔数 cookie(
MEI开头那串),找不到就直接报"不是 PyInstaller 归档"。 - 判版本:读 cookie 后面的字段,判断是 2.0 老格式还是 2.1+ 新格式,顺带拿到打包时的 Python 版本号。
- 定位目录表:根据 cookie 里的偏移算出 CArchive 的 overlay 位置,解析出每个条目的名称、偏移、压缩标志。
- 逐条解压:按目录表把每条数据读出来,带压缩标志的用 zlib 解压,遇到依赖项和运行时选项这类非文件条目直接跳过。
- 拆 PYZ + 修头:碰到
PYZ\0开头的归档再解一层,把里面的 pyc 逐个取出;最后统一把正确的魔术数字补回缺失的 pyc 头部,兼容到 Python 3.7+ 的 PEP 552 格式。
一句话总结:找钥匙 → 开目录 → 取货 → 贴标签,思路清晰得可以直接当教学案例。
你可能踩过的坑,以及怎么绕开
报 "Missing cookie"。说明文件里找不到 PyInstaller 签名——要么根本不是它打包的,要么版本太老(早于 2.0)。先确认文件来源,别硬拆。
报 "Failed to decompress"。某条数据解压失败,常见于文件损坏或被恶意篡改。重新拿一份原文件试试,或者看看是否涉及加密归档。
反编译器读不出来。先确认 pyc 头部是否完整、是否加密。修复过的文件一般没问题,加密的则需先解密再反编译。
解组错误一片红。九成是 Python 版本不匹配。查一下打包方用的版本,切换到对应环境重跑,通常立刻药到病除。
别只当手动工具,把它塞进流水线
单文件脚本最大的好处是好自动化。写个循环就能批量拆一整个目录的 exe;配合 CI 流程,每次构建后自动拆包自检,敏感信息泄漏当场就能拦下;再串上反编译器,一条命令完成"拆包 → 反编译 → 出报告"的整条链路。
如果还嫌不够,这个生态里还有两个衍生项目值得留意:pyinstxtractor-ng是免 Python 环境的独立二进制版本,连加密文件也能处理;pyinstxtractor-web把拆包能力搬到了浏览器里跑。一条链路从命令行到网页全覆盖。
现在就去试一把
下次再遇到一个"打不开的 Python 程序",别急着放弃,也别瞎猜。clone 下来、一条命令、读日志、反编译,四步走完,黑盒变透明。PyInstaller 的壳从来不是密不透风的墙——pyinstxtractor 就是那把现成的钥匙,而且它一直免费躺在那里,等着你用。
去找个你手上现成的 PyInstaller 打包程序练练手吧。拆开第一个的那一刻,你会觉得这比看十篇教程都值。
【免费下载链接】pyinstxtractorPyInstaller Extractor项目地址: https://gitcode.com/gh_mirrors/py/pyinstxtractor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考