jguida941/binary_to_assembly
GitHub: jguida941/binary_to_assembly
面向初学者的二进制逆向分析教学项目,结合 GNU binutils 工具使用指南与自动化 Python CLI,帮助用户学习拆解 x86-64 ELF 文件并阅读汇编代码。
Stars: 2 | Forks: 0
# 二进制到汇编
拆解已编译的程序:找出其中包含的函数,将机器码还原为汇编,并解释其功能。
编写这份资料是因为布置该任务的课程从未解释过具体操作机制。即该输入哪条命令、在哪里输入以及为什么这样做。
## 从这里开始
### **[GUIDE.md](GUIDE.md)**:从头到尾的完整指南
每一条命令、它的作用、为什么运行它、以及输出的含义。这些都是在实际操作过程中记录的,而非事后补充。**这是本仓库的核心内容。**
## 环境设置
你只需要 Docker,不需要其他任何东西。分析工具是 GNU binutils,它们存在于 Linux 上但不在 macOS 上,因此所有内容都在一个与课程环境匹配的固定版本容器中运行:Ubuntu 18.04,gcc 7.5.0,binutils 2.30。
```
git clone https://github.com/jguida941/binary_to_assembly.git
cd binary_to_assembly
make build # builds the analysis image, prints its digest. Takes a few minutes once.
make examples # compiles the five example programs, three ways each
```
然后拆解一个程序:
```
make functions FILE=examples/05_functions/build/05_functions
make disassemble FILE=examples/05_functions/build/05_functions FUNCTION=report
make strings FILE=examples/05_functions/build/05_functions
```
`make help` 会列出所有内容,并且**每个目标在运行前都会打印出它要执行的原始命令**,因此你可以直接复制该命令,随时停止使用 `make`。
### 如果你正在 Codio 中做作业
你不需要上述任何内容。Codio 已经安装了 `file`、`readelf`、`nm`、`objdump` 和 Bless,并且 [GUIDE.md](GUIDE.md) 正是基于此编写的。该容器是专门用于在你自己的机器上拆解自行编译的程序。
### 不使用 make 运行 CLI
```
docker run --rm --platform linux/amd64 -v "$PWD:/work" -w /work \
-e PYTHONPATH=/work/src binary-assembly-guide:local \
python3.8 -m binary_assembly_guide.cli functions BINARY
```
是 `python3.8`,而不是 `python3`:Ubuntu 18.04 的默认版本是 3.6.9,版本太旧,无法解析 CLI。
[`environment/README.md`](environment/README.md) 解释了为什么 image 要固定在旧版 toolchain 上,以及测量了哪些数据来证明它与 Codio 匹配。
### 运行测试
在宿主机上运行,而不是在容器中,因为它们需要 git,而容器中没有安装。
```
python3 -m pytest -q # everything
python3 -m pytest tests/privacy -q # the guardrail that matters most
```
## 相关工具及其用途
由四个程序来完成这项工作。每个程序读取同一个文件的不同部分,这就是为什么只用一个工具是不够的。
| 工具 | 它是什么 | 它能告诉你什么 |
| --- | --- | --- |
| `file` | 根据内容识别文件类型 | 这是否是一个程序,适用于什么 CPU,以及名称是否仍保留在其中 |
| `readelf` | 读取 ELF 结构 | `file` 正在汇总的那些 header 字段:类型、entry point |
| `nm` | 列出符号表 | 每一个名称,并用一个字母标明它是代码还是数据 |
| `objdump` | 反汇编器 | 将机器码解码还原为汇编,以及原始 section 内容 |
| Bless | 十六进制编辑器 | 字节本身,以及作为纯文本嵌入其中的名称 |
十六进制编辑器向你展示了函数名*作为文本切实存在于文件中*。但它无法告诉你这些名称中哪个是函数,哪个是变量,哪个是源文件名。`nm` 读取的正是旁边记录这些确切信息的表。`objdump` 读取的则是可执行代码本身。
## CLI
包含四个命令,分别对应指南所描述工作流程的每一步。
```
binary-assembly-guide inspect BINARY
binary-assembly-guide functions BINARY [--compare OTHER ...]
binary-assembly-guide disassemble BINARY FUNCTION [--resolve]
binary-assembly-guide strings BINARY [--section .rodata]
```
**`inspect`** 展示文件是什么,以及它是否可以被分析。它会拒绝任何非 x86-64 ELF 的文件并说明原因,而不是盲目给出荒谬的结论。
**`functions`** 列出每一个函数,以及每个函数的编写者。如果通过 `--compare` 提供了其他不相关的二进制文件,它会根据证据而不是记忆的名称列表进行分类:在所有文件中都存在的内容,不是为其中任何一个专门编写的。
**`disassemble --resolve`** 是最值得拥有的功能。它会打印出函数的汇编代码,*并且*查找代码引用的每个地址中实际包含的内容:
```
400556: lea 0xf7(%rip),%rdi # 400654
400562: callq 4003f0
Addresses this function references:
0x400654 "%s: %d"
```
手动操作的话,这相当于执行 `objdump -s -j .rodata`,找到正确的行,逐个计算字节偏移,解码十六进制,并在遇到第一个 `00` 时停止。而且要针对每个地址重复此过程。跳过这一步,就会把对程序功能的解释变成干巴巴的机制描述,而永远不提程序到底做了什么。
**`strings`** 展示数据段中的字符串字面量,并进行了正确的分割。objdump 自带的 ASCII 列会将每个不可打印的字节渲染为 `.`,导致字符串连在一起且边界丢失。
[`scripts/`](scripts/) 中的 Shell wrapper 调用的是相同的命令,因此底层工具保持可见,而不是被隐藏在某个程序背后。
**[完整 CLI 指南](docs/using-the-cli.md)**:介绍每一个命令和每一个 flag,并附带各自的实际输出,包括在 stripped binary、Mach-O 文件以及不存在的名称上会发生什么。
## 练习
五个小型的 C 程序,每个都单独剥离出一种代码结构,并且旁边配有一道书面练习。你拥有这些源代码,因此你可以用已知答案的程序来验证你的解读是否正确。
| 示例 | 结构 |
| --- | --- |
| [`01_simple_return`](examples/01_simple_return/exercise.md) | prologue、epilogue 和返回值 |
| [`02_arithmetic`](examples/02_arithmetic/exercise.md) | 操作数存在于何处,以及哪个 register 携带结果 |
| [`03_condition`](examples/03_condition/exercise.md) | `cmp` 加上条件跳转,以及为什么要反转条件 |
| [`04_loop`](examples/04_loop/exercise.md) | 向后跳转,以及计数器保存在哪里 |
| [`05_functions`](examples/05_functions/exercise.md) | 四个函数、参数 register 以及 call graph |
每个示例提供三种构建方式:`make` 用于生成类似于课程文件的 debug build;`make stripped` 用于查看符号表消失后会发生什么;`make optimized` 用于查看 `-O2` 会对你刚学会阅读的代码产生什么影响。
## 其他内容
| | |
| --- | --- |
| [`docs/`](docs/index.md) | 五篇背景知识页面,以及 [故障排除](docs/troubleshooting.md),记录的每一个错误都是真实发生过的 |
| [`images/`](images/) | 指南中使用的截图。添加截图前请阅读其 README |
| [`environment/`](environment/README.md) | 测量 Codio 与固定版本容器之间的 toolchain 一致性 |
| [`dev/PLAN.md`](dev/PLAN.md) | 架构说明,以及构建过程中被证明有误的假设 |
## 状态
对于其所涵盖的内容,已全部完成且可正常运行。指南演示了完整的工作流程,CLI 已针对三个构建变体中的每个示例二进制文件进行了测试,五个示例均可干净地编译,并根据捕获的输出进行了文档记录。
范围特意收窄:**仅限 x86-64 ELF**,如果未 stripped 则会直接告知无法识别任何内容。没有 JSON 输出,也没有 GUI。这里的一切都是我亲自运行并验证过的。
## 未包含的内容
不包含任何已完成的课程作业,也不提供二进制文件。`inputs/` 和 `outputs/` 已被 gitignore 并保留在本地,且有测试套件强制执行此规则,而不是仅仅依赖 `.gitignore`。这个仓库提供的是方法论,而不是答案。
如果你正在修读这门课程:阅读别人做好的练习手册学不到任何东西,而且你的指导老师一眼就能看出来。提供这些示例是为了让你可以在拥有源代码的程序上进行练习,然后独立完成你自己的文件分析。
标签:Docker, ELF, Python, 二进制分析, 云安全运维, 安全规则引擎, 安全防御评估, 无后门, 汇编, 请求拦截, 逆向分析, 逆向工具