内容
原生Python跨版本反编译器和片段反编译器。 decompyle、uncompyle 和 uncompyle2 的后继者。
uncompyle6将 Python 字节码转换回等效的 Python 源代码。它接受从 Python 版本 1.0 到版本 3.8 的字节码,涵盖了超过 24 年的 Python 版本。我们包含 Dropbox 的 Python 2.5 字节码和一些 PyPy 字节码。
好吧,我会说:这个软件太棒了。它不仅仅是普通的 hacky 反编译器。使用编译器技术,程序根据指令创建程序的解析树;上层的节点看起来有点像来自 Python AST 的节点。因此我们可以真正分类并理解 Python 字节码各部分中发生的情况。
在此基础上,与其他 CPython 字节码反编译器不同的另一件事是能够仅解析源代码片段并提供给定字节码偏移量周围的源代码信息。
我使用树片段在运行时在我的 Trepan 调试器中解析代码片段。为此,字节码偏移量被记录并与源代码片段相关联。这个目的虽然符合初衷,但还是有些不同。请参阅此了解更多信息。
给定指令偏移量的 Python 片段解析对于显示堆栈跟踪很有用,并且可以合并到任何想要在运行时更详细地显示位置而不仅仅是行号的程序中。当源代码信息不存在并且只有字节码时也可以使用此代码。我的调试器再次利用了这一点。
曾经(现在仍然)有许多 decompyle、uncompyle、uncompyle2、uncompyle3 分支。其中许多基本上来自相同的代码库,并且(几乎?)所有这些都不再被积极维护。其中一个非常擅长反编译 Python 1.5-2.3,另一个非常擅长反编译 Python 2.7,但仅此而已。另一个只处理 Python 3.2;另一个修补了该问题并仅处理 3.3。你明白了。这段代码将所有这些分叉拉到一起并向前推进。与那些旧分支相比,此代码库中进行了一些认真的重构和清理。 decompyle3 中正在进行更多的实验性重构。
这显然在所有 Python 版本中反编译 Python 方面表现最好。即使有另一个项目只提供 Python 版本子集的反编译,我们通常也会对这些版本做得更好。
我们怎么知道?通过获取随该版本的 Python 分发的 Python 字节码并反编译它们。在成功反编译的程序中,我们可以通过运行该字节码版本的 Python 解释器来确保生成的程序在语法上正确。最后,如果程序本身有测试,我们可以对反编译的代码进行检查。
我们使用自动化流程来查找错误。在其他反编译器的问题跟踪器中,您会发现我们一路上发现的许多错误。其他反编译器中修复的问题很少甚至没有。
git 存储库中的代码可以从 Python 2.4 到最新的 Python 版本运行,但 Python 3.0 到 3.2 除外。如果有意愿的话,欢迎志愿者来解决这些缺陷。
它的实现方式是将连续的 Python 版本分离到 git 分支中:
PyPy 3-2.4 及更高版本也可以工作。
它可以读取的字节码文件已经在 1.4、2.1-2.7 和 3.0-3.8 以及更高版本的 PyPy 版本的 Python 字节码上进行了测试。
您可以使用名称uncompyle6
从 PyPI 安装:
pip 安装 uncompyle6
为了从源代码安装,该项目使用 setup.py,因此它遵循标准的 Python 例程:
$ pip install -e 。 # 设置从源代码树运行
或者:
$ python setup.py install # 可能需要 sudo
还提供了 GNU Makefile,因此make install
(可能以 root 或 sudo 身份)将执行上述步骤。
进行检查
添加了 GNU makefile,以平滑设置运行正确的命令,并从最快到最慢运行测试。
如果您安装了 remake,您可以通过remake --tasks
查看所有任务的列表,包括测试
跑步
$ uncompyle6 *编译的python文件-pyc-or-pyo*
如需使用帮助:
$ uncompyle6 -h
在旧版本的 Python 中,可以通过反编译字节码,然后使用该字节码版本的 Python 解释器进行编译来验证字节码。完成此操作后,可以将生成的字节码与原始字节码进行比较。然而,随着 Python 的代码生成变得更好,这不再可行。
如果你想对反编译过程的正确性进行Python语法验证,请添加--syntax-verify
选项。然而,由于 Python 语法发生了变化,如果字节码对于将检查语法的 Python 解释器来说是正确的字节码,则应该使用此选项。
您还可以将结果与uncompyle6的另一个版本进行交叉比较,因为随着整体质量的提高,反编译特定字节码有时会出现回归。
对于Python 3.7和3.8,decompyle3中的代码通常更好。
或者尝试特定的另一个 python 反编译器,如 uncompyle2、unpyc37 或 pycdc。由于后两者的工作方式不同,因此这里的错误通常不在其中,反之亦然。
其中有一类有趣的程序可以随时提供更强的验证:那些在运行时测试自身的程序。我们的测试套件包括这些。
Python 还附带了另一组这样的程序:它的标准库测试套件。我们在test/stdlib
中有一些代码也可以促进这种检查。
最大的已知且可能可修复(但很难)的问题与处理控制流有关。 (Python 可能是我见过的最多样化、最复杂的复合语句;循环和 try 块上有“else”子句,我怀疑许多程序员都不知道。)
我见过的所有 Python 反编译器在反编译 Python 的控制流时都存在问题。在某些情况下,我们可以检测到错误的反编译并报告该错误。
Python 对 Python 2 的支持非常好
在 Python 版本的低端,反编译似乎相当不错,尽管我们没有针对 Python 的分布式测试进行任何自动化测试。另外,我们没有 1.6 和 2.0 版本的 Python 解释器。
在 Python 3 系列中,Python 支持在 3.4 或 3.3 左右最强,并且随着远离这些版本而逐渐减弱。 Python 3.0 很奇怪,因为它在某些方面比 3.1 或 2.7 更类似于 2.6。 Python 3.6 通过使用字代码而不是字节代码来彻底改变事情。因此,跳转指令参数中的跳转偏移字段已减少。这使得EXTENDED_ARG
指令现在在跳转指令中更加普遍;以前它们很少见。也许为了补偿额外的EXTENDED_ARG
指令,添加了额外的跳转优化。因此总而言之,通过临时手段处理控制流(如当前所做的那样)更糟糕。
在 Python 3.5、3.6、3.7 之间, MAKE_FUNCTION
和CALL_FUNCTION
指令发生了重大变化。
Python 3.8 删除了SETUP_LOOP
、 SETUP_EXCEPT
、 BREAK_LOOP
和CONTINUE_LOOP
指令,这些指令可能会使控制流检测变得更加困难,缺乏计划中的更复杂的控制流分析。我们拭目以待。
目前,并非所有 Python 幻数都受支持。特别是在 Python 的某些版本中,特别是 Python 3.6,幻数在一个版本内发生了多次变化。
我们仅支持已发布版本,而不支持候选版本。但请注意,发布版本的魔力通常与发布前的最后一个候选版本相同。
还有定制的 Python 解释器,特别是 Dropbox,它们使用自己的魔法并加密字节码。除了 Dropbox 的旧 Python 2.5 解释器之外,此类事情不被处理。
我们也不处理 PJOrion 或其他混淆的代码。对于 PJOrion,请尝试使用 PJOrion Deobfuscator 在尝试此工具之前对字节码进行解密以获得有效的字节码; pydecipher 可能会对此有所帮助。
该程序无法反编译 Py2EXE 创建的 Microsoft Windows EXE 文件,尽管在正确提取字节码后我们可能可以反编译代码。 Pydeinstaller 可能有助于解压 Pyinstaller 捆绑程序。
处理病态的长表达式或语句列表的速度很慢。我们不处理不使用字节码的 Cython 或 MicroPython。
反编译有很多bug。对于我遇到的所有其他 CPython 反编译器来说都是如此,甚至是那些声称在某些特定版本(如 2.4)上“完美”的反编译器。
随着 Python 的进步,反编译也变得越来越困难,因为编译更加复杂,而且语言本身也更加复杂。我怀疑像 unpyc37(基于 3.3 反编译器)这样的临时尝试会更少,因为这样做更困难。至少从我的角度来看,好消息是我认为我了解以更强有力的方式解决问题需要什么。但现在,直到项目得到更好的资助时,我不打算做出任何认真的努力来支持 Python 版本 3.8 或 3.9,包括可能出现的错误。我想在某个时候我可能会对它感兴趣。
通过针对 Python 用于检查自身的标准测试套件运行测试,您可以轻松找到错误。在任何给定时间,都有数十个已知问题被很好地隔离,如果人们花时间这样做,这些问题就可以得到解决。问题是没有那么多人致力于修复错误。
3.7 和 3.8 中的一些错误只是向后移植decompyle3中的修复程序的问题。有志愿者吗?
您可能会遇到想要报告的错误。请阅读如何报告错误后执行此操作,并在打开问题时按照说明进行操作。
请注意,它可能暂时不会引起我的注意。如果您以某种方式赞助或支持该项目,我将优先考虑您的问题,而不是我可能正在做的其他事情。在极少数情况下,我可以付费手动反编译字节码。然而,这是一个巨大的支出,通常超出了大多数人愿意花的钱。
uncompyle6
decompyle3
-- BlackHat 2024 Asia(视频)。非常感谢组织者和审稿人让我发言。这种事情鼓励我从事这样的项目。uncompyle6
结果不正确,而uncompyle2
结果不正确,但更常见的是,uncompyle6 正确,而 uncompyle2 不正确。由于uncompyle6
比惯用的 Python 更注重准确性,因此uncompyle2
在正确时可以生成看起来更自然的代码。目前uncompyle2
维护得很少。有关更多详细信息,请参阅其问题跟踪器。