跳转到主要内容

Python 3反编译器

项目描述

Codecov PyPI Tests Code style: black License: MIT

pygetsource

pygetsource 是一个Python 3的反编译器,旨在将编译后的字节码指令转换回Python代码。

概述

当Python读取代码时,它首先将指令转换为字节码。例如

a = 2

转换为

LOAD_CONST 1
STORE_FAST 0

后者通常存储在 .pyc 文件中以及函数对象的 __code__ 属性中。 pygetsource 的目标是反转此过程。

该项目名称来源于 inspect.getsource 函数,该函数返回函数的源代码,但如上所述,它并不总是适用。

pygetsource 仍在开发中。它应该能够从Python 3.7到Python 3.11的各种程序中恢复简单函数的源代码。它尚不能恢复类的源代码、导入语句、try/except/match/with块,并且不支持Python 2。虽然功能齐全,但代码库尚未优化,需要进行重大重构。

最后,此软件在MIT许可证下分发。

安装

使用pip安装包

pip install pygetsource

用法

import pygetsource


def func():
    a = 5

    while i < 10:
        if a == 2:
            break
        elif a == 4:
            return 3

        a = i // 5

    d = 3
    e = 4
    return e + d


print(pygetsource.getsource(func.__code__))

产生以下输出

a = 5
while i < 10:
    if a == 2:
        break
    elif a == 4:
        return 3
    else:
        a = i // 5
d = 3
e = 4
return e + d

注意如何将 else 语句添加到 elif 语句中,但两个程序在功能上是等效的。

什么时候这很有用?

pygetsource 在需要从 .pyc 文件恢复源代码,或者想要获取通过 eval 语句或 lambda 语法创建的函数的源代码时很有用。实际上,在后者的情况下,由于函数的原始文件不可用,或者 Python 不提供所需的精确边界,运行 inspect.getsource 会失败,尤其是在 lambda 函数的情况下。

替代方案

uncompyle6 是一个支持 Python 2 和 3(至 Python 3.8)的 Python 反编译器。它使用基于语法的途径通过字节码模式重建代码。对于引入了各种字节码优化(特别是关于复杂控制结构,如循环或上述示例)的高级版本,这种方法效果较差。目前,它支持更广泛的 Python 语法(如 with 块或 try/except)。它还根据 copyleft GPL 许可证授权,使得它对于具有宽松许可证的大型项目不太合适。

decompyle++ (pycdc) 使用状态机方法通过处理字节码指令迭代地构建 AST。它是用 C++ 编写的,支持比 uncompyle6 更多的 Python 版本,但在反编译复杂控制结构(如嵌套循环、break 模式、列表推导或上述示例)方面有更多问题。它还使用 copyleft GPL 许可证。

它是如何工作的?

pygetsource 使用一种独特的方法。首先将字节码指令转换为表示程序流程的有向图。然后迭代地减少此图,根据其操作码、参数和位置处理每个节点,并在过程中生成 AST。这种方法允许我们在重新创建复杂结构(如嵌套循环或 break/return 语句)时更多地依赖于高级模式,而较少依赖于 Python 的特性,并能够使用相同的代码库处理从 3.7 到 3.11 的 Python 版本。

uncompyle6pycdc 不同,pygetsource 使用 astastunparse 库从生成的 AST 生成源代码。

以下是一个图被减少的示例

Graph reduction

反编译何时成功?

由于编译过程是注入的,因此无法恢复确切的原始源代码。多个 Python 程序可以产生相同的字节码指令。此外,原始源代码通常不可用于比较(否则你会使用这个软件做什么?)。

如果我们重新编译生成的程序,我们可以比较两组字节码指令以确保功能等效。然而,Python 可能会引入无操作码(如 'NOP'),这可能导致验证失败,尽管两个代码对象在功能上是等效的。

相反,pygetsource 在修剪步骤后比较原始代码对象的图与恢复的代码对象的图。在此步骤中,删除了无操作码,修剪了跳转指令(同时保持源节点和目标节点之间的边),并消除了死代码。

贡献

欢迎贡献。请随意打开一个问题或提交一个 pull 请求。与反编译过程相关的任何问题都应包括生成字节码所使用的 Python 解释器的版本、函数的源代码以及由 dis.dis(code) 打印的字节码指令。

要在开发模式下安装项目,请克隆仓库并在根目录下运行 pip install -e '.[dev]'。然后,您可以使用 pytest 运行测试。请确保首先安装了 graphviz。如果您在 MacOS 上遇到 Python 的 pygraphviz 包问题,请尝试使用以下命令安装:

pip install \
    --global-option=build_ext \
    --global-option="-I$(brew --prefix graphviz)/include/" \
    --global-option="-L$(brew --prefix graphviz)/lib/" \
    pygraphviz

要检查失败案例,请使用 getsource 函数的 debug=True 参数。这将显示在缩减过程的各个阶段中的图,以及各种调试信息。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分发

pygetsource-0.3.0.tar.gz (29.9 kB 查看哈希值)

上传时间

构建分发

pygetsource-0.3.0-py3-none-any.whl (24.6 kB 查看哈希值)

上传时间 Python 3

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面