本文档描述了 Ghidra 中二进制代码反编译的基本流程。它涵盖了机器代码如何转换为中间表示、进行分析,并最终转换为类似 C 语言的源代码。有关类型传播和数据流等具体方面的信息,请参见函数分析和类型传播以及SSA 形式构建和数据流。
Ghidra 中的反编译过程遵循一个多阶段管道,逐步将二进制机器代码转换为可读的类似 C 语言的代码。以下是该过程的高级概述
来源:Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh45-55 Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh33-86
P-code 作为 Ghidra 在机器代码和反编译源代码之间的中间表示。P-code 将不同处理器架构的细节抽象为一组通用操作。
来源:Ghidra/Features/Decompiler/src/decompile/cpp/op.hh Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh
P-code 的主要特征
Funcdata 类是用于反编译函数的所有数据结构的主要容器。
来源:Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh42-205
反编译过程始于初始化操作,为函数分析做准备。
ActionStart → ActionStartTypes → ActionStackPtrFlow
来源:Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh33-86
机器代码指令通过处理器特定模块转换为 P-code 操作。这创建了函数的初始表示。
来源:Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc
Heritage 系统将 P-code 转换为静态单赋值 (SSA) 形式,确保每个变量只被赋值一次。这简化了各种分析。
来源:Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc
SSA 特性
应用多条转换规则来简化和规范代码。这些规则定义为从 Rule 基类派生的类。
转换示例
来源:Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc24-252 Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh85-252
SubvariableFlow 系统识别何时将小型逻辑变量存储在较大的容器中并将其提取。
来源:Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc26-67 Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh42-52
类型信息通过以下方式在函数中推断和传播:
非结构化控制流(跳转和分支)被转换为结构化控制流(if-else 语句,循环)。
最后,PrintC 系统从转换后的 P-code 表示生成人类可读的类似 C 语言的代码。
反编译过程由一组转换代码的操作和规则驱动。以下是关键操作类的表格:
| 操作 | 目的 |
|---|---|
| ActionStart | 初始化反编译过程 |
| ActionStartTypes | 开始类型分析 |
| ActionStackPtrFlow | 分析函数调用中栈指针的变化 |
| ActionLaneDivide | 将大寄存器拆分为逻辑通道 |
| ActionSegmentize | 处理分段内存地址 |
| ActionForceGoto | 应用来自用户覆盖的 goto 约束 |
| ActionConstbase | 初始化上下文寄存器 |
| ActionMultiCse | 消除公共子表达式 |
| ActionShadowVar | 处理影子变量 |
| ActionConstantPtr | 将原始地址转换为指针 |
规则是较小、有针对性的转换,重复应用直到无法进行进一步更改。
| 规则 | 转换 |
|---|---|
| RuleEarlyRemoval | 移除未使用的操作 |
| RuleCollectTerms | 组合算术项:V * c + V * d => V * (c + d) |
| RulePiece2Zext | 将连接转换为零扩展:concat(#0,W) => zext(W) |
| RulePiece2Sext | 将连接转换为符号扩展:concat(V s>> #0x1f, V) => sext(V) |
| RuleBxor2NotEqual | 将布尔异或转换为不等于:V ^^ W => V != W |
| RuleOrMask | 简化与全掩码的 OR 操作:V = W | 0xffff => V = W |
来源:Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc24-252 Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh85-252
以下是反编译如何将机器代码转换为类似 C 语言代码的示例:
Ghidra 的核心反编译过程是一个多阶段管道,将机器代码转换为可读的类似 C 语言的源代码。该过程包括:
每个阶段都建立在前一个阶段的基础上,逐步从低级二进制表示中重建高级编程结构。