diff --git a/.vscode/settings.json b/.vscode/settings.json index 01e17c9..37ff610 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,11 @@ { "cSpell.words": [ + "pycdas", "Pycdc", + "Pydumpck", "Pynosaur", - "Uncompyle" + "Uncompyle", + "xdis" ], "[python]": { "editor.defaultFormatter": "ms-python.black-formatter" diff --git a/README.md b/README.md index a979ba0..1bde2d3 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,68 @@ 简体中文 | [English](README.en.md) - +[👉 跳转到安装与用法小节](#安装) ## 功能特性 +### 高版本 CPython 字节码 +### 是命令行程序,也是 Python 库 + +### 推测 Python 版本 + +如果传入 Marshal 产生的代码对象(Code Object)字节串,Pynosaur 可以尽可能根据字节码指令推测出其对应的 Python 版本。这一特性对于 pyc 头丢失、只有反汇编文本等情况非常有用。 + +### 人为构造的代码对象 + +对于每个新的 Python 版本,Pynosaur 首先尽可能实现对 CPython 编译器生成的字节码序列的支持。在此基础上,我们尝试在遇到 CPython 编译器通常情况下不会产生的字节码序列时,产生尽可能有用的结果。这样,就不会因为恶意构造的字节码而导致 Pynosaur 反编译失败。以下是一个仅用于描述的恶意构造的例子(其中的注释不是 Pynosaur 生成的,只是对例子的说明): + +```plaintext + LOAD_CONST 1 (1) + LOAD_CONST 2 (2) + COMPARE_OP 148 (bool(>)) + POP_JUMP_IF_FALSE 10 (to L1) # 下一条指令代表 1 大于 2 的分支 + LOAD_GLOBAL 5678 (??? + NULL) # globals 的第五千多项?越界! + CALL 1234 # 从栈上弹出一千多个参数? + POP_TOP +``` + +一种可能的反编译输出是 `pass` 并在后面的注释中提供无法处理的字节码指令。 + +### 文本格式 + +除 pyc 文件等二进制格式外,Pynosaur 也接受以下输入形式或其片段: + +- 标准库 `dis.dis` 输出的反汇编字节码文本 +- 标准库 `ast.dump` 输出的抽象语法树文本 +- 标准库 `tokenize` 输出的词法单元文本 +- [pycdas]() 输出的反汇编字节码文本 +- [xdis]() 输出的反汇编字节码文本 + +### 版本转换 + +Pynosaur 还支持不同版本之间的字节码转换。例如,通过适当的命令行参数或脚本,可以用 Python 3.12 运行 Pynosaur 本身,传入文本格式的 Python 3.13 的字节码,输出可用于的 Python 3.11 的 pyc 文件。 + +### 与运行 Pynosaur 的 CPython 标准库或原生类型互操作 + +### 可转换的形式小结 + +### 衍生产品 + +- **Pynosaur UI**: 一个可视化的字节码反编译、原地编辑、跨版本回编译工具。由 Pynosaur 开发者维护。 +- **Pynosaur Duck**: 只需一条命令,解包并递归反编译 PyInstaller 和 Py2exe 打包的可执行文件。由 Pynosaur 开发者维护。灵感来源于 [Pydumpck]()。 ## 设计 +### 没有历史包袱 +不同于其他支持早期版本(甚至 2.x, 1.x)的反编译器,Pynosaur 从一开始就被设计为只支持最近几个 CPython 版本,目前支持 CPython 3.9 至 3.14 的字节码。 + +这样的好处在于可以专注于现代 Python 语言的特性和优化,而不必考虑向后兼容性的问题。例如,从 Python 3.6 开始,CPython 字节码指令是定长的。由于不需要考虑旧版本的字节码,Pynosaur 可以安全地假定字节码指令的长度是固定的,这使得反编译器的实现更简单、更高效。 + +新的 Python 版本发布时,针对不再受支持版本的反编译逻辑可能会被 Pynosaur 移除。如果发生这种情况,Pynosaur 会发布新的主要版本。旧的主要版本仍然可用,但不再维护。这使得 Pynosaur 代码库可以保持简洁、灵活,在实现新的字节码时不需考虑过多兼容问题。 + +如果你需要处理 3.8 及更低版本的字节码,可以选择其他工具,见下文对比章节。 ## 工作原理 diff --git a/docs/zh/docs/devguide/1-intro.md b/docs/zh/docs/devguide/1-intro.md new file mode 100644 index 0000000..a13c80b --- /dev/null +++ b/docs/zh/docs/devguide/1-intro.md @@ -0,0 +1,18 @@ +欢迎来到 Pynosaur 的开发指南!本指南旨在帮助开发者理解和参与 Pynosaur 的开发工作,涵盖项目的设计理念、开发工作流、实现细节等。 + +你正在阅读版本 0.0.1 的文档。文档 MarkDown 源文件位于代码仓库 `docs/zh/docs/devguide/` 目录下,切换到另一个版本的分支或标签可以查看其他版本的文档。 + +
+历史版本 + +
+ +开发文档假定读者已经熟悉 Python 编程语言和基本的编程概念,例如变量、函数、类和模块等。 + +还假定读者学习过编译原理课程,或至少了解编译器前端的基本工作原理。读者应当了解词法分析、语法分析、语义分析、中间代码生成的主要任务和算法。 + +# 术语约定 + +# 项目组成 + +# 目录结构 diff --git a/docs/zh/docs/devguide/2-workflow.md b/docs/zh/docs/devguide/2-workflow.md new file mode 100644 index 0000000..3f0f052 --- /dev/null +++ b/docs/zh/docs/devguide/2-workflow.md @@ -0,0 +1,23 @@ + + +# 工作语言 + +目前项目的主要开发者母语为中文。为了便于更多人参与,建议遵循以下语言使用规范: + +- 通常情况下由主要开发者编写的文档首先使用中文,然后翻译成英文。也就是说,中文文档是原始准确的,英文文档可能存在偏差。 +- 为了术语翻译清楚明白,可能会在中文后面用括号标注英文原文。 +- 文件名、代码注释、议题(issue)标题、拉取请求(pull request)标题、拉取请求描述、拉取请求评论、提交消息(commit message)**总是**使用英文。议题内容、议题回复或评论可以使用中文或英文。 + +# Git 工作流 + +# 版本发布策略 + +# 贡献 + +## 代码贡献 + +### 代码规范 + +## 文档贡献 + +### 翻译 diff --git a/docs/zh/docs/devguide/3-cpython-internal.md b/docs/zh/docs/devguide/3-cpython-internal.md new file mode 100644 index 0000000..8fd0602 --- /dev/null +++ b/docs/zh/docs/devguide/3-cpython-internal.md @@ -0,0 +1,11 @@ +# 字节码是什么 + +# 从源代码到字节码 + +# PYC 文件的生成 + +# PYC 文件的结构 + +# 标准库语言服务 + +# CPython 源码和相关资料 diff --git a/docs/zh/docs/devguide/4-design-and-mechanism.md b/docs/zh/docs/devguide/4-design-and-mechanism.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/zh/docs/devguide/5-example.md b/docs/zh/docs/devguide/5-example.md new file mode 100644 index 0000000..8922b3c --- /dev/null +++ b/docs/zh/docs/devguide/5-example.md @@ -0,0 +1,44 @@ +本文通过一个完全虚构的小的例子,引导读者初步了解项目代码结构和原理,帮助读者快速上手开发工作。 + +假设下一个版本 CPython 添加了一条 `RETURN_INT` 字节码指令,指令编号是 [TODO] ,取代 CPython 3.14 中的 [TODO] 指令。我们要将它添加到已经实现的字节码指令中。该指令的操作数是一个整数值,作用是将该整数值作为函数的返回值。该指令没有栈副作用。 + +# 保留中间结果 + +# 建立一个新的分支 + +# 确认调试环境与工作区相同 + +# 了解待添加的指令 + +首先我们通常会观察 CPython 在什么情况下会生成 `RETURN_INT` 指令。CPython 的代码生成器源代码位于 [TODO] 文件中。我们可以搜索 `RETURN_INT` 来找到相关代码。由于本例是完全虚构的,我们假设对于以下源代码,会生成该指令: + +```python +def foo(): + return 42 +``` + +`dis.dis(foo)` 产生以下输出: + +```plaintext + 1 RESUME 0 + RETURN_INT 42 +``` + +相应的,如果使用现有的 Python 版本,产生以下输出: + +```plaintext + 1 RESUME 0 + RETURN_CONST 1 (42) +``` + +`RETURN_CONST` 的语义没有改变,我们不需要修改它。 + +# 添加指令到字节码指令列表 + +# 对 CFG 的影响 + +# 从 CFG 到 AST + +# 从 AST 到 Python 代码 + +# 测试 diff --git a/docs/zh/docs/devguide/6-tests.md b/docs/zh/docs/devguide/6-tests.md new file mode 100644 index 0000000..e69de29