Python 跨版本字节码反编译器
项目描述
解压缩6
一个原生的 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 片段解析对于显示堆栈跟踪很有用,并且可以合并到任何想要在运行时更详细地显示位置的程序中,而不仅仅是行号。当源代码信息不存在而只有字节码时,也可以使用此代码。同样,我的调试器利用了这一点。
有(并且仍然有)许多反编译、非编译、非编译2、非编译3叉。它们中的许多基本上来自相同的代码库,并且(几乎?)它们都不再被积极维护。一个非常擅长反编译 Python 1.5-2.3,另一个非常擅长 Python 2.7,但仅此而已。另一个只处理 Python 3.2;另一个修补了它,只处理了 3.3。你明白了。这段代码将所有这些分支拉到一起并向前移动。在这个代码库中对那些旧的分支进行了一些严重的重构和清理。decompyle3中正在进行更多的实验性重构。
这显然在所有 Python 版本中反编译 Python 方面做得最好。即使有另一个项目只为 Python 版本的子集提供反编译,我们通常也会为这些版本做得更好。
我们怎么知道?通过获取随 Python 版本一起分发的 Python 字节码并反编译这些字节码。在那些成功反编译的程序中,我们可以通过为该字节码版本运行 Python 解释器来确保生成的程序在语法上是正确的。最后,如果程序有自己的测试,我们可以对反编译的代码进行检查。
我们使用自动化流程来查找错误。在其他反编译器的问题跟踪器中,您会发现我们在此过程中发现的许多错误。在其他反编译器中很少或没有一个是固定的。
要求
此处的代码可以在 Python 2.6 或更高版本、PyPy 3-2.4 及更高版本上运行。python-2.4 分支支持 Python 版本 2.4-2.7。它可以读取的字节码文件已经在 Python 字节码 1.4、2.1-2.7 和 3.0-3.8 及更高版本的 PyPy 上进行了测试。
安装
这使用 setup.py,因此它遵循标准 Python 例程:
$ pip install -e . # set up to run from source tree, or... $ python setup.py install # may need sudo
还提供了一个 GNU makefile,因此make install
(可能作为 root 或 sudo)将执行上述步骤。
运行测试
$ make check
添加了一个 GNU makefile 以平滑设置运行正确的命令,以及从最快到最慢运行测试。
如果您安装了remake,您可以通过以下方式查看包括测试在内的所有任务的列表remake --tasks
用法
跑
$ uncompyle6 *compiled-python-file-pyc-or-pyo*
使用帮助:
$ uncompyle6 -h
确认
在旧版本的 Python 中,可以通过反编译字节码来验证字节码,然后使用该字节码版本的 Python 解释器进行编译。完成此操作后,生成的字节码可以与原始字节码进行比较。然而,随着 Python 的代码生成变得更好,这不再可行。
如果要 Python 语法验证反编译过程的正确性,添加--syntax-verify
选项。但是,由于 Python 语法发生了变化,如果字节码是用于检查语法的 Python 解释器的正确字节码,则应使用此选项。
您还可以将结果与 uncompyle6的另一个版本进行交叉比较,因为随着整体质量的提高,有时在反编译特定字节码时会出现回归。
对于 Python 3.7 及以上版本,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在尝试此工具之前对字节码进行解密以获得有效的字节码。该程序无法反编译由Py2EXE创建的 Microsoft Windows EXE 文件,尽管我们可以在正确提取字节码后反编译代码。处理病态长的表达式或语句列表很慢。我们不处理不使用字节码的Cython或 MicroPython。
反编译中存在许多错误。对于我遇到的所有其他 CPython 反编译器来说都是如此,即使是那些声称在某些特定版本(如 2.4)上“完美”的反编译器也是如此。
随着 Python 的进步,反编译也变得更加困难,因为编译更加复杂,语言本身也更加复杂。我怀疑像unpyc37(基于 3.3 反编译器)这样的临时尝试会更少,因为这样做更难。好消息是,至少从我的角度来看,我认为我了解以更稳健的方式解决问题所需的条件。但是现在直到项目得到更好的资金,我不打算做出任何认真的努力来支持 Python 3.8 或 3.9 版本,包括可能出现的错误。我想在某些时候我可能会对它感兴趣。
您可以通过针对 Python 用来检查自身的标准测试套件运行测试来轻松找到错误。在任何给定的时间,有几十个已知的问题是相当孤立的,如果花时间解决这些问题是可以解决的。问题是没有那么多人一直致力于修复错误。
3.7 和 3.8 中的一些错误只是将 decompyle3 中的修复向后移植的问题。
您可能会遇到要报告的错误。请这样做。但请注意,它可能暂时不会引起我的注意。如果您以某种方式赞助或支持该项目,我会将您的问题优先于我可能正在做的其他事情的队列之上。
也可以看看
https://github.com/rocky/python-decompile3:更小更现代的代码,专注于 3.7+。其中的更改将迁移回此处。
https://code.google.com/archive/p/unpyc3/:仅支持 Python 3.2。上述项目使用的反编译技术与此处使用的不同。目前无人维护。
https://github.com/figment/unpyc3/:上面的分支,但仅支持 Python 3.3。包括一些修复,例如支持函数注释。目前无人维护。
https://github.com/wibiti/uncompyle2:仅支持 Python 2.7,但做得相当好。在某些情况下,
uncompyle6
结果不正确而uncompyle2
结果不正确,但更多情况下 uncompyle6 是正确的,而 uncompyle2 不正确。因为uncompyle6
坚持使用惯用 Python 的准确性,uncompyle2
所以在正确的情况下可以生成更自然的代码。目前uncompyle2
被轻微维护。有关更多详细信息,请参阅其问题跟踪器历史文件。
https://github.com/rocky/python-xdis : 跨 Python 版本反汇编器
https://github.com/rocky/python-xasm : 跨 Python 版本的汇编器
https://github.com/rocky/python-uncompyle6/wiki:Wiki 文档,更详细地描述了代码及其各个方面
https://github.com/zrax/pycdc:此 C++ 代码的 README 表示它旨在支持所有版本的 Python。最初开发代码时,最好使用 2.7 和 3.3 左右的 Python 版本。当前版本的 Python3 和早期版本的 Python 缺乏准确性。如果不付出很大的努力,它就不可能支持当前的 Python 3。有关详细信息,请参阅其问题跟踪器。目前轻微维护。