4、Ghidra脚本入门:Python脚本插件安装、Headless模式、基础API调用、脚本调试
说实话,Ghidra 最让我着迷的地方,就是它的脚本系统。你想想看,一个逆向工具再强大,如果只能点点鼠标,那跟用计算器做微积分有什么区别?真正的高手,都是用脚本把重复劳动自动化,把复杂逻辑封装起来。这一章,我就带你走一遍 Ghidra 脚本的入门之路。
4.1 Python脚本插件安装
Ghidra 默认支持 Java 和 Python 两种脚本语言。Java 是亲儿子,性能好,API 最全。但 Python 呢?上手快,生态好,我个人的主力语言就是 Python。所以咱们先从 Python 说起。
Ghidra 的 Python 支持,靠的是一个叫 Jython 的东西。Jython 是 Java 平台上的 Python 实现,说白了,就是用 Java 写了一个 Python 解释器。所以你的 Python 脚本,本质上还是在 JVM 里跑,能直接调用 Ghidra 的 Java API。
安装步骤其实很简单:
- 打开 Ghidra,进入 File → Configure → Experimental
- 勾选 Python 插件(如果没看到,可能需要手动下载 jython-standalone.jar)
- 重启 Ghidra
- 点击 Window → Script Manager,就能看到 Python 脚本了
我个人习惯,装好之后先跑一个 Hello World 试试:
# hello.py
print("Hello from Ghidra Python!")
print("Current program: ", getCurrentProgram().getName())
点一下运行,如果控制台输出了信息,那就成了。
4.2 Headless模式:让脚本飞起来
GUI 模式虽然方便,但你要批量分析几百个二进制文件呢?一个个打开、运行脚本、关闭?那不得累死。这时候就需要 Headless 模式了。
Headless 模式,就是不带界面的 Ghidra。它可以在命令行里运行,适合自动化、服务器部署、CI/CD 流程。
启动命令长这样:
./analyzeHeadless /path/to/project TempProject \
-import /path/to/binary \
-postScript /path/to/script.py \
-deleteProject
参数解释一下:
/path/to/project:Ghidra 项目存放目录TempProject:临时项目名,用完就删-import:要分析的二进制文件-postScript:分析完成后运行的脚本-deleteProject:跑完就删项目,省空间
-preScript 和 -postScript 组合。preScript 做初始化,postScript 做分析。比如 preScript 里设置分析选项,postScript 里导出结果。
Headless 模式还有一个好处:可以并行跑。我曾在一次项目中,用 8 个进程同时分析 200 个固件,一晚上全搞定。要是手动操作,估计得一周。
4.3 基础API调用:你得知道这些
Ghidra 的 API 非常庞大,但常用的其实就那么几个。我帮你梳理一下最核心的:
| API 类别 | 常用函数/类 | 作用 |
|---|---|---|
| 程序信息 | getCurrentProgram() |
获取当前分析的程序对象 |
| 函数操作 | getFunctionManager() |
获取函数管理器,遍历所有函数 |
| 指令遍历 | getListing() |
获取指令列表,逐条分析 |
| 内存访问 | getMemory() |
读取/写入内存数据 |
| 符号/引用 | getReferenceManager() |
查找交叉引用、字符串引用 |
举个例子,我想遍历所有函数,打印函数名和地址:
from ghidra.program.model.listing import Function
fm = getCurrentProgram().getFunctionManager()
functions = fm.getFunctions(True)
for func in functions:
name = func.getName()
addr = func.getEntryPoint()
print(f"Function: {name} @ {addr}")
嗯,这里要注意:getFunctions(True) 里的 True 表示正向遍历。如果你传 False,那就是反向遍历,从地址高的往低的走。
再比如,我想找所有对某个字符串的引用:
from ghidra.program.model.symbol import RefType
target_str = "password"
data_mgr = getCurrentProgram().getListing()
data = data_mgr.getDefinedData(True)
for d in data:
if d.isString() and str(d.getValue()) == target_str:
refs = getCurrentProgram().getReferenceManager().getReferencesTo(d.getAddress())
for ref in refs:
print(f"Reference from: {ref.getFromAddress()}")
这段代码我实际用过。当时分析一个恶意软件,它把 C2 服务器地址藏在字符串表里,我就是用这个脚本把所有引用点找出来的。
4.4 脚本调试:别怕,有办法
写脚本哪有不 debug 的?Ghidra 的脚本调试,说实话,没有 IDE 那么方便。但有几个技巧能帮你省不少时间。
第一招:print 大法
最简单也最有效。在关键位置加 print(),看变量值、看执行流程。Ghidra 的控制台会输出所有 print 内容。
print("DEBUG: current address = ", addr)
print("DEBUG: function count = ", len(list(functions)))
第二招:使用 monitor 对象
Ghidra 的脚本 API 里有一个 monitor 对象,可以用来检查用户是否取消了操作,也可以输出进度。
from ghidra.util.task import TaskMonitor
# 检查是否被取消
if monitor.isCancelled():
print("User cancelled!")
return
# 更新进度
monitor.setMessage("Processing function " + str(i))
monitor.setProgress(i)
第三招:异常捕获
脚本跑着跑着崩了,最烦人。我习惯在关键代码段加 try-except:
try:
# 你的分析逻辑
result = do_something()
except Exception as e:
print("Error at address", addr, ":", str(e))
# 记录错误,继续执行
pass
第四招:分段测试
别一次性写几百行。写一个小功能,跑一下,确认没问题,再往下写。我一般把脚本拆成几个函数,每个函数单独测试。
4.5 知识体系总览
说了这么多,我画了一张图,帮你把这一章的核心逻辑串起来:
这张图把四个核心模块串起来了。你从安装 Python 插件开始,学会在 GUI 里写脚本。然后掌握 Headless 模式,让脚本脱离界面运行。接着熟悉基础 API,知道怎么操作函数、指令、内存。最后学会调试技巧,遇到问题不慌。
说白了,脚本就是你的武器。用得好了,别人还在手动翻汇编,你已经把结果导出成 CSV 了。我见过太多人,花大量时间做重复劳动,其实写个脚本十分钟就能搞定。嗯,这一章的内容就这些,去试试吧。