8、静态分析工具(下):Ghidra 基础使用、反编译、脚本编写入门

上一章我们聊了 IDA Pro,今天来聊聊 Ghidra。说实话,我第一次接触 Ghidra 是抱着怀疑态度的——NSA 开源的工具,能好用吗?结果用了半小时,我就真香了。它免费、开源、功能强大,尤其反编译引擎,我个人觉得在某些场景下比 IDA 还顺手。

8.1 Ghidra 的界面与项目管理

Ghidra 启动后,你会看到一个 Java 风格的界面。嗯,别被它略显复古的 UI 吓到,内核很强大。

核心概念:

  • 项目(Project):每个分析任务对应一个项目,类似 IDA 的 .idb 文件
  • 工具(Tool):默认的 CodeBrowser 是最常用的工具
  • 程序(Program):被加载的二进制文件

我习惯的做法是:每个样本单独建一个项目,命名规则用「样本名_日期」。这样方便后期回溯,尤其是当你同时分析十几个样本时,项目管理混乱会让人崩溃。

8.2 反编译引擎的使用

Ghidra 的反编译引擎叫 Decompiler,它能把汇编代码转换成类似 C 语言的伪代码。这是 Ghidra 最吸引我的地方。

基本操作:

  1. 在 Listing 窗口选中一个函数
  2. F5 或右键选择 Decompile
  3. Decompiler 窗口会显示伪代码

实战经验:我曾经分析过一个混淆严重的恶意软件,IDA 的 F5 直接罢工了。但 Ghidra 的 Decompiler 居然能输出可读的伪代码,虽然有些变量名是乱的,但逻辑清晰多了。后来我查了资料,Ghidra 的 Decompiler 对控制流平坦化(Control Flow Flattening)的还原能力确实比 IDA 强一些。

反编译窗口的常用功能:

  • 重命名变量:双击变量名,改成有意义的名称
  • 添加注释:选中代码行,按 ;
  • 设置数据类型:右键变量 → Set Data Type
  • 导航到引用:右键 → References → Show References to

8.3 脚本编写入门

Ghidra 支持多种脚本语言:Python(Jython)、Java、JavaScript。我个人推荐用 Python,因为生态好,而且你可以在脚本里直接调用 Ghidra 的 API。

脚本环境:

  • 菜单栏 → Window → Script Manager
  • 点击 Manage Script Directories 添加你的脚本目录
  • 点击 New 创建新脚本

下面是一个简单的脚本示例,用于遍历所有函数并打印它们的名称和地址:

# 获取当前程序
from ghidra.program.model.listing import Function
from ghidra.util.task import ConsoleTaskMonitor

program = getCurrentProgram()
listing = program.getListing()
functions = listing.getFunctions(True)

monitor = ConsoleTaskMonitor()

print("函数列表:")
for func in functions:
    name = func.getName()
    addr = func.getEntryPoint()
    print(f"0x{addr} : {name}")

脚本运行:

  1. 在 Script Manager 中找到你的脚本
  2. 双击运行,或选中后点击 Run
  3. 输出会显示在 Console 窗口

小技巧:写脚本时,我习惯先打开 Ghidra 的 API 文档(Help → Ghidra API Help)。里面每个类和方法都有详细说明,还有示例代码。别自己硬记 API,查文档效率高得多。

8.4 实战:自动化分析恶意软件

我曾经遇到一个样本,里面有 2000 多个函数,手动分析根本不可能。于是我写了个脚本,自动标记所有调用 strcpymemcpy 的函数,并输出它们的调用路径。

脚本核心逻辑:

# 查找所有调用特定函数的引用
from ghidra.program.model.symbol import RefType

target_funcs = ["strcpy", "memcpy", "sprintf"]
program = getCurrentProgram()
listing = program.getListing()
func_manager = program.getFunctionManager()

for target in target_funcs:
    symbols = program.getSymbolTable().getSymbols(target, None)
    for sym in symbols:
        refs = sym.getReferences()
        for ref in refs:
            if ref.getReferenceType() == RefType.DATA_IND:
                continue
            caller_func = func_manager.getFunctionContaining(ref.getFromAddress())
            if caller_func:
                print(f"[危险调用] {caller_func.getName()} 调用了 {target} 在 {ref.getFromAddress()}")

这个脚本帮我快速定位了所有危险函数调用,节省了至少 3 小时的手动分析时间。

8.5 Ghidra 脚本的进阶技巧

1. 使用 FlatProgramAPI

Ghidra 提供了 FlatProgramAPI,它封装了常用操作,让脚本更简洁。比如上面的脚本可以用 FlatProgramAPI 改写:

from ghidra.app.script import GhidraScript
from ghidra.program.model.symbol import RefType

class FindDangerousCalls(GhidraScript):
    def run(self):
        target_funcs = ["strcpy", "memcpy", "sprintf"]
        for target in target_funcs:
            symbols = self.getSymbols(target, None)
            for sym in symbols:
                refs = sym.getReferences()
                for ref in refs:
                    if ref.getReferenceType() == RefType.DATA_IND:
                        continue
                    caller_func = self.getFunctionContaining(ref.getFromAddress())
                    if caller_func:
                        self.println(f"[危险调用] {caller_func.getName()} 调用了 {target}")

2. 脚本参数化

你可以在脚本中定义参数,运行时弹出对话框让用户输入。比如:

from ghidra.app.script import GhidraScript

class ParamExample(GhidraScript):
    def run(self):
        min_addr = self.askAddress("请输入起始地址", "0x1000")
        max_addr = self.askAddress("请输入结束地址", "0x2000")
        self.println(f"分析范围: {min_addr} - {max_addr}")

注意:Ghidra 的脚本 API 版本更新较快,不同版本之间可能有细微差异。我建议你始终使用当前版本对应的 API 文档。另外,Jython 的性能不如原生 Python,如果脚本处理大量数据,考虑用 Java 重写。

8.6 本章知识体系

下面这张图总结了 Ghidra 的核心使用流程和脚本编写路径:

Ghidra 核心知识体系 界面与项目管理 反编译引擎 脚本编写 项目创建与导入 CodeBrowser 工具 程序管理与导航 F5 反编译操作 变量重命名与注释 数据类型设置 引用导航 Python/Java 脚本 FlatProgramAPI 脚本参数化 自动化分析 三者结合:高效逆向分析工作流

8.7 避坑指南

最后分享几个我踩过的坑:

  • 中文路径问题:Ghidra 对中文路径支持不好,项目路径和文件路径最好都用英文。我曾经因为路径里有中文,导致反编译结果乱码,排查了半天才发现是路径问题。
  • 内存不足:分析大文件时,Ghidra 默认内存可能不够。在启动脚本里加上 -Xmx4G 参数,分配 4GB 内存会稳很多。
  • 脚本调试:Ghidra 的脚本调试功能比较弱,我习惯在脚本里加大量 println 输出,配合 Console 窗口看日志。
  • 版本兼容:不同版本的 Ghidra 保存的项目文件可能不兼容。升级前最好备份项目,或者导出为 .gzf 格式。

好了,Ghidra 的基础使用就聊到这里。工具只是手段,关键还是你的分析思路。多写脚本,多实战,慢慢你就会发现 Ghidra 的强大之处。


公众号:蓝海资料掘金营,微信deep3321