Skip to main content

Python 跨版本字节码反编译器

项目描述

构建状态 Pypi 安装 最新版本 支持的 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 反编译器,如uncompyle2unpyc37pycdc。由于后两者的工作方式不同,因此这里的错误通常不在其中,反之亦然。

这些程序中有一类有趣的现成可用,可提供更强的验证:那些在运行时自行测试的程序。我们的测试套件包括这些。

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_FUNCTIONCALL_FUNCTION指令发生了重大变化。

Python 3.8 删除了SETUP_LOOPSETUP_EXCEPTBREAK_LOOPCONTINUE_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 中的修复向后移植的问题。

您可能会遇到要报告的错误。请这样做。但请注意,它可能暂时不会引起我的注意。如果您以某种方式赞助或支持该项目,我会将您的问题优先于我可能正在做的其他事情的队列之上。

也可以看看