ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ghidra 9.0.2安装:64位JDK 11与headless验证

Ghidra 9.0.2安装:64位JDK 11与headless验证

简介:Ghidra 9.0.2 是一款面向逆向工程与网络安全分析的开源软件逆向工具,适合安全研究人员、漏洞分析人员及希望提升二进制分析技能的学习者使用。其核心价值在于通过图形化控制流浏览、多架构反汇编、数据类型管理、函数边界识别与调用关系分析,帮助读者理解已编译程序的内部逻辑;同时支持 Python/Java 脚本、插件扩展、版本控制集成及反混淆等能力,可用于恶意软件分析、漏洞发现和软件安全评估。资源以 7z 压缩包分发,体量约 217.69MB,文件总数显示为 0,类型明细暂无有效数据,具体清单需以上游页面为准。页面显示已有 801 人学习或浏览,说明该版本在逆向分析入门与实战场景中具有一定关注度。读者可借助其完整功能体系搭建分析环境,围绕反汇编、函数分析和脚本自动化开展练习,并为后续漏洞排查与样本分析积累可复用的方法。

1. Ghidra 9.0.2 安装的第一道关,卡在 JDK 版本上

A同学第一次做 Ghidra 逆向分析,拿到的是 9.0.2 的 PUBLIC 压缩包。他在 Windows 上解压、双击ghidraRun.bat,黑框一闪就没了;换到 Linux 敲./ghidraRun,终端只吐一行提示说需要 Java 11,然后退出。他机器上java -version是 1.8.0_2xx——为了跑一堆老工具,JDK 8 早被设成默认。这种情况我一个月能碰上三四回。Ghidra 9.0.2 安装没有向导、没有依赖安装器,所有门槛都压在「机器上有没有一个 64 位 JDK 11」上:装对了解压即用,装错了连主界面都看不到,还容易误判成压缩包损坏、反复重下。

2. 环境清单:Ghidra 9.0.2 只认 64 位 JDK 11

2.1 JDK 8、11、17 在 9.0.2 上分别是什么下场

先把版本关系说清楚。Ghidra 9.0.2 的启动脚本在拉起 JVM 前会读取版本,低于 11 直接拒绝启动,连错误画面都不给;装的是 JDK 17 或更高版本的话,启动能过去,但用着用着会抛IllegalAccessError、NoClassDefFoundError这类异常,尤其是模块化和内部 API 反射相关的部分。这是因为 9.0.2 的代码和打包方式是按 JDK 11 来的,没有做高版本 JDK 的模块化适配。

JDK 版本9.0.2 上的实际结果说明
JDK 8启动器直接拦,提示需要 Java 11缺java.net.http、var之类 11 才有的东西
JDK 11(64 位)唯一推荐官方 Release Notes 里的目标版本
JDK 17 / 21能启动,运行中抛异常内部 API 被封,扩展脚本也容易崩
32 位 JDK 11能开,稍大点的样本就 OOM堆上限被锁死,改-Xmx没用

提示:9.0.2 之后的 10.x 系列依然吃 JDK 11,真正把门槛抬到 JDK 17 是更后面的版本线。也就是说,如果你手上有别的工具链锁在 JDK 11,9.0.2 反而好伺候;以你下载页的 Release Notes 为准,别看两年前的博客抄版本号。

另一个容易忽略的点:必须 64 位。Ghidra 反编译一个几 MB 的固件,堆随随便便就要 3~4GB,32 位 JVM 的地址空间根本不够,表现出来就是分析到一半卡死或者直接OutOfMemoryError。判断方法很简单,java -version输出的第三行会带64-Bit Server VM,没有这行就是 32 位。

2.2 一份能直接对照的软硬件清单

装之前先对一遍,比装完再回头查要省事得多。下面这行是我自己机器和给同事配的机器都用过的配置,能跑通 9.0.2 的完整流程(GUI + headless 脚本):

项目最低我的选择说明
系统Win10 64 位 / 主流 Linux x86_64 / macOS 10.15+Ubuntu 20.04 x86_64ARM 平台要谨慎,见第 5 章
内存8GB16GB分析大文件时-Xmx至少给 4G
磁盘5GB 空闲20GB解压后 1GB 上下,项目文件和缓存会持续长
JDK64 位 JDK 11OpenJDK 11(压缩包版)别用 JRE,启动器要javac之外的工具
显示1920×10802K + 缩放 1.25高 DPI 要加uiScale参数
权限普通用户普通用户用 root 跑会在配置目录里写出属主混乱的文件

注意:解压目录和项目目录全程用纯英文、不带空格。这一条看着像老生常谈,但 Ghidra 的项目是.gpr文件加一个同名.rep目录,路径里只要有空格或中文,脚本参数拼串、项目重定位都可能出问题,后面第 5 章有一条专门讲这个。

2.3 装 JDK 11:包管理器、压缩包、与老 JDK 共存

三条路我都走过,按场景选。图省事、机器上没别的 Java 依赖,用包管理器最稳:

# Debian / Ubuntu 系,装完系统里就有一份 JDK 11 sudo apt-get update sudo apt-get install -y openjdk-11-jdk # 验证:三行输出里必须有 11 和 64-Bit java -version # openjdk version "11.0.x" ... # OpenJDK 64-Bit Server VM ...

openjdk-11-jdk会把java、javac、jmap一起装上,Ghidra 启动器在自检时会用到其中几个。装完如果java -version还是显示 1.8,说明update-alternatives里默认项没切,用sudo update-alternatives --config java手动选 11,或者干脆走第二条路。

机器上已经有一份 JDK 8 且不能动(老项目、老构建脚本依赖它),就别碰全局JAVA_HOME,解压一份独立的 JDK 11 放到/opt下,让 Ghidra 单独指过去:

sudo mkdir -p /opt/jdk # 压缩包从你惯用的 JDK 发行版下载页取,64 位 Linux 版 sudo tar -xzf OpenJDK11U-jdk_x64_linux_hotspot_11.0.9_11.tar.gz -C /opt/jdk # 只在这个 shell 里临时生效,验证一下版本对不对 export JAVA_HOME=/opt/jdk/jdk-11.0.9+11 export PATH=$JAVA_HOME/bin:$PATH java -version

JAVA_HOME要指到 JDK 根目录(里面直接有bin、lib的那层),不是jre子目录。指到jre是最常见的错,启动器报的错还很含糊,只说找不到 Java 相关组件。Windows 上同理:装完 JDK 11 后在系统环境变量里把JAVA_HOME设成C:\Program Files\Java\jdk-11,Path里加%JAVA_HOME%\bin并挪到最前面。

第三条路是不改任何全局变量,直接让 Ghidra 自己找 JDK——这个做法我个人最推荐,下一章展开。

3. 落地安装:解压、指 JDK、第一次启动

3.1 解压位置和目录结构:先认清这几个文件

Ghidra 9.0.2 是绿色包,没有安装程序。解压出来一个ghidra_9.0.2_PUBLIC目录,底下这些文件决定了后面所有操作:

# 统一放 /opt,路径纯英文无空格 sudo mkdir -p /opt/ghidra sudo unzip ghidra_9.0.2_PUBLIC_<构建日期>.zip -d /opt/ghidra cd /opt/ghidra/ghidra_9.0.2_PUBLIC # zip 在 Linux 上解出来常丢执行位,三个入口脚本补一下 chmod +x ghidraRun support/analyzeHeadless support/ghidraSvr ls
路径作用什么时候会用到
ghidraRun/ghidraRun.batGUI 启动入口日常反编译
support/launch.properties启动参数配置指 JDK、改堆内存、加 JVM 参数
support/analyzeHeadless无界面批处理入口批量分析、脚本验证、CI
support/ghidraSvr共享项目服务端团队协作时才需要
os/各平台的原生二进制(含反编译器)反编译报错时先看这里
Ghidra/Extensions官方扩展需要额外分析能力时放插件
Ghidra/Features/Base/ghidra_scripts内置脚本示例写脚本时抄写法

os/这个目录值得单独说一句:反编译器不是纯 Java 实现的,而是一个独立的原生进程。Ghidra 启动时会到os/<平台>/下找decompile可执行文件,找不到或者它跑不起来,界面能开、文件能导入,但一打开反编译窗口就报错。所以「能不能反编译」这件事,本质上是两套环境:JVM 环境和原生二进制环境,都要对。

3.2 让 9.0.2 用指定 JDK:JAVA_HOME_OVERRIDE 比改全局变量稳

Ghidra 的启动器会按顺序找 Java:先看support/launch.properties里有没有覆盖项,再看JAVA_HOME,最后才用PATH里的java。覆盖项的优先级最高,这就是不改全局环境的关键:

# 文件:support/launch.properties # JDK 根目录,Linux 写法;Windows 用 C:\\Program Files\\Java\\jdk-11 这种形式 JAVA_HOME_OVERRIDE=/opt/jdk/jdk-11.0.9+11 # 堆上限和界面缩放,同一行里用空格隔开多个 JVM 参数 VMARGS=-Xmx4g -Dsun.java2d.uiScale=1.25

两行都是「改了立刻生效、不需要重装」的类型。JAVA_HOME_OVERRIDE只影响 Ghidra 自己拉起的那个 JVM,机器上别的工具该用 JDK 8 继续用 JDK 8,互不干扰。VMARGS是透传给 JVM 的参数串,-Xmx4g给堆上限,-Dsun.java2d.uiScale=1.25处理高分屏。默认值按你解压出来的文件为准,不同构建之间可能略有出入,但键名就这两个。

写完先不急着开界面,跑一次启动器的自检:

# 加 -debug 之类参数不通用,直接看启动日志更靠谱 ./ghidraRun 2>&1 | head -40

启动器在打印日志时会带上它找到的 Java 路径和版本号,对一遍就知道覆盖有没有生效。如果日志里还是你系统默认的那个 JDK 8,多半是这一行被前面的#注释掉了,或者路径写成了jre子目录。

3.3 首次启动:建项目、导文件、自动分析要不要全开

第一次进主界面会弹项目选择框,两种项目:本地非共享项目,和连到ghidraSvr的共享项目。个人用一律选前者,项目位置放在你的工作目录下,别放在 Ghidra 安装目录里——升级或者重装时整个安装目录可能被删掉。

导入二进制的流程很短:File > Import File(快捷键I),选中目标文件,格式一般能自动识别;识别不出来就在 Format 下拉里手动指定(raw binary 要额外给基地址和处理器架构)。导完会问要不要分析,这一步的取舍是:

  • 小样本、只要看几个函数:全开默认分析,等一两分钟。
  • 大固件、只想快速定位字符串:先关掉部分耗时项(Decompiler Parameter ID 之类),跑完再按需补。
  • 批量场景:干脆别在 GUI 里点,直接用第 6 章的analyzeHeadless,-noanalysis参数可以先导入后分析。

分析进度条卡在 90% 以上很久是正常现象,某些分析器在收尾阶段要遍历全部函数。真卡死了看日志,日志在用户配置目录下,第 4 章讲路径。

4. 参数调优:内存、字体和配置目录

4.1 堆内存:VMARGS 里 -Xmx 给多少合适

-Xmx给多少,取决于你要分析的样本大小和机器内存。给太小,表现是分析中途OutOfMemoryError或者界面越来越卡最后无响应;给太大,JVM 的 GC 停顿变长,操作反而迟钝。我的经验值:

物理内存建议-Xmx适用场景
8GB3g普通 PE/ELF 样本,几 MB 级
16GB6g~8g大固件、几十 MB 的库文件
32GB 以上12g~16g批量批处理、共享项目服务端

改完必须重启 Ghidra 才生效。判断有没有生效,在 GUI 里看Help > About,或者干脆在 headless 日志里搜 JVM 参数行,那里会原样打出来。这里有一个血泪教训:如果机器上只有 32 位 JDK,-Xmx4g写了也白写,JVM 会警告后回退到 1.5G 左右,你以为调了其实没调。

# 大文件分析的常用组合:堆给够,同时指定 GC VMARGS=-Xmx8g -XX:+UseG1GC -Dsun.java2d.uiScale=1.25

-XX:+UseG1GC不是必须的,JDK 11 默认就是 G1,写出来只是让配置文件自解释、以后换 JDK 时不容易忘。真要调优 GC,先把-Xlog:gc加上看实际停顿,别凭感觉加参数。

4.2 中文注释变方块、界面发虚:字体和缩放两个参数

在 Linux 上给函数写中文注释,注释显示成一排方块,这不是编码问题,是字体问题——Ghidra 默认字体里没有中文字形。系统层面装一套中文字体,再在Edit > Tool Options里把字体族换成它:

# Debian / Ubuntu 系,装一套即可 sudo apt-get install -y fonts-wqy-microhei # 刷新字体缓存 fc-cache -fv

装完重启 Ghidra,在Edit > Tool Options > Listing Fields里把Font换掉。这个设置是按工具(Tool)保存的,存在用户配置目录里,重装 Ghidra 不会丢,但换机器要重新设。

高 DPI 屏幕发虚是另一回事,靠-Dsun.java2d.uiScale=1.25解决,取值按你系统缩放比例来:Windows 125% 就写 1.25,150% 写 1.5。写大了界面元素会溢出,写小了字糊。这个参数和字体问题是两件事,别混着调,先缩放后字体,一次只改一个变量。

4.3 配置目录和项目文件:重装前要备份什么

Ghidra 把用户级的东西(工具布局、字体、最近项目、脚本路径、日志)放在用户配置目录里,路径带版本号:

系统用户配置目录
Linux~/.ghidra/.ghidra_9.0.2_PUBLIC
Windows%APPDATA%\ghidra\.ghidra_9.0.2_PUBLIC

macOS 上在用户库目录下,具体路径启动日志里会打出来,照抄就行。这个目录可以直接删——删掉等于恢复出厂设置,是配置搞乱了之后最省事的后悔药。但删之前先把application.log拷出来,排错全靠它。

项目文件是另一套东西:一个项目名.gpr加一个项目名.rep目录,两者必须一起移动,少一个项目就打不开。备份就这么写:

# 停掉 Ghidra 再备,运行中拷 .rep 可能拿到半截数据 tar -czf ghidra_backup_$(date +%F).tar.gz \ ~/.ghidra/.ghidra_9.0.2_PUBLIC \ /data/ghidra_proj/Demo.gpr \ /data/ghidra_proj/Demo.rep

$(date +%F)拼日期后缀,避免覆盖;三个路径分别是配置、项目索引、项目数据。恢复时反向解压回原位,路径不要变——.gpr里记的是相对位置,挪了目录要重新定位。

5. 排查:Ghidra 9.0.2 安装路上五个翻车现场

按我处理过的顺序排,从「根本起不来」到「起来了但用不了」。遇到问题建议按这个顺序走:先看能不能起来,再看反编译能不能用,最后看批量脚本能不能跑。跳步排查最容易在错误的方向上耗半天。

5.1 双击启动一闪而过,命令行只留一行提示

现象:Windows 上双击ghidraRun.bat黑框一闪就关;Linux 上./ghidraRun打印一两行关于 Java 版本的提示后退出。

原因:启动器没找到合格的 JDK 11。三种具体情形——JAVA_HOME指向jre子目录(没有javac)、JAVA_HOME指向 JDK 8、PATH里的java是某个工具自带的精简运行时。Windows 上黑框一闪就关是因为脚本退出得太快,看不到错误。

解决:开一个终端手动跑ghidraRun,或者cmd里cd到目录再执行 bat,把错误留下来。然后按 3.2 在support/launch.properties里写死JAVA_HOME_OVERRIDE,绕开所有环境变量歧义。写完再跑一次,日志里那行 Java 路径应该变成你指定的 JDK。

5.2 界面能开,一开反编译窗口就报 Decompiler process died

现象:导入文件、函数列表都正常,点开 Decompiler 窗口显示进程已退出,或者一直转圈。

原因:反编译器的原生进程起不来。三种情形——os/<平台>/下没有对应架构的二进制(ARM 机器上跑 x86_64 包最常见);二进制丢了执行位;/tmp或安装目录挂载时带了noexec,进程根本没法执行。

解决:先确认二进制在不在、能不能跑:

# 看架构对不对:x86-64 的机器上应该是 ELF 64-bit LSB file os/linux_x86_64/decompile # 补执行位 chmod +x os/linux_x86_64/decompile # 直接跑一下,缺库会在这里报出来 ldd os/linux_x86_64/decompile | grep "not found"

file输出显示是 ARM 或者not found,说明这个包在你的平台上没有原生支持,换 x86_64 机器或者换版本线,改配置没用。Apple Silicon 上跑 9.0.2 就是这一类,只能用 x86_64 的 JDK 走转译,速度和稳定性都要打折。

5.3 分析大文件卡死、进度条长时间不动

现象:导入一个几十 MB 的固件,自动分析跑到某个百分比后界面无响应,日志里出现OutOfMemoryError或频繁 GC。

原因:堆不够。默认堆是按小样本设的,大文件光加载的字节数组就吃满了;另一个隐性原因是拿 32 位 JDK 在跑,-Xmx改到多大都无效。

解决:按 4.1 把VMARGS里的-Xmx提到 6g~8g,同时确认java -version里有64-Bit Server VM。改完还不行就分批处理:先用-noanalysis只导入,再用 headless 模式单独跑某几个分析器,把内存峰值摊开。

5.4 路径带空格或中文,项目打不开、脚本找不到

现象:项目从桌面临时目录挪到别处后打不开;headless 传-scriptPath时脚本加载失败,提示找不到文件。

原因:.gpr里存的是项目位置信息,路径一挪就失效;带空格的路径在脚本参数拼串时会从中间断开,被当成两个参数。

解决:安装目录、项目目录、脚本目录三条路径全部改成纯英文无空格,这次改完以后别再三心二意地挪。已经挪过的项目,删掉.gpr用 Ghidra 的「导入已有项目」重新指一次,.rep里的数据不会丢。脚本路径用绝对路径传,别指望相对路径能稳定工作。

5.5 脚本跑出 SyntaxError,明明本地 Python 能跑

现象:写了个脚本想批量导出函数名,headless 一跑就报SyntaxError: invalid syntax,指到print(...)或者 f-string 那一行。

原因:Ghidra 9.0.2 内置的脚本引擎是 Jython 2.7,不是 CPython 3。f-string、print()里的关键字参数、类型注解全都不认。

解决:脚本按 Python 2 语法写——字符串格式化用%,打印用print "..."语句式(Jython 2.7 对print()单个参数是兼容的,但别带end=),字典方法用iteritems()。这一条在换到更高版本线之后会变,但 9.0.2 上就得认。

6. 装完怎么验证:用 analyzeHeadless 跑一遍才算数

GUI 里能双击打开、能看反编译结果,只能证明一半——JVM 环境对了。真正的验证要走无界面批处理,因为它是所有自动化场景的基础:批量分析、CI 里跑脚本、团队共享项目的预处理,全靠它。headless 跑通,说明 JDK 版本、反编译器原生进程、脚本引擎三件事同时正常,这才是「装完了」。

先跑一个最小闭环,只导入不分析,验证启动链路:

cd /opt/ghidra/ghidra_9.0.2_PUBLIC # 项目位置、项目名、要导入的文件,三个位置参数不能换顺序 ./support/analyzeHeadless /data/ghidra_proj Demo \ -import /data/samples/target.bin \ -overwrite \ -noanalysis \ -deleteProject

/data/ghidra_proj是项目存放目录,目录不存在会自动建;Demo是项目名,同名项目会被复用。-overwrite允许覆盖同名导入文件,不加这个参数重复导入会报错。-noanalysis跳过自动分析,这条命令几秒内就该结束,用来验证「能不能起来」。-deleteProject跑完删项目,做冒烟测试时用,正式批量别加。

第二步把脚本引擎也验了,写个最小脚本,数一数函数:

# 文件:/data/ghidra_scripts/DumpFuncs.py # Ghidra 9.0.2 走 Jython 2.7,别写 f-string #@category Demo fm = currentProgram.getFunctionManager() out = open("/tmp/funcs.txt", "w") count = 0 for f in fm.getFunctions(True): out.write("%s\t%s\n" % (f.getEntryPoint(), f.getName())) count += 1 out.close() print "exported %d functions" % count

#@category那行是脚本元数据,不写会在 Ghidra 的脚本管理器里归到未分类;currentProgram是 headless 引擎注入的当前程序对象,不用自己初始化;getFunctions(True)的True表示按地址正向遍历,给False是反向。写到/tmp是为了避开权限问题,正式用改到你自己的输出目录。

带脚本跑一遍:

./support/analyzeHeadless /data/ghidra_proj Demo \ -import /data/samples/target.bin \ -overwrite \ -scriptPath /data/ghidra_scripts \ -postScript DumpFuncs.py

-scriptPath指向脚本目录,-postScript在分析结束后执行,两者要成对出现——只给-postScript不给路径,脚本管理器会去内置目录里找,找不到就报错。这一步跑完,终端里应该能看到exported N functions,/tmp/funcs.txt有内容且行数对得上,就算整套环境通了。

我的验证清单,装完每台机器都过一遍:

检查项命令 / 位置期望结果
JDK 版本java -version11.x + 64-Bit Server VM
启动器认的 JDKghidraRun启动日志路径指向JAVA_HOME_OVERRIDE
原生反编译器file os/linux_x86_64/decompileELF 64-bit,同架构
GUI打开一个样本看 Decompiler出伪代码,无进程退出提示
headless 导入上面第一条命令正常结束,无异常栈
脚本引擎上面第三条命令输出函数数量,文件有内容
日志用户配置目录下application.log没有ERROR/WARN扎堆

从那以后我每次装完 Ghidra,不管多急,都强制把最后那段 headless 加脚本的流程跑一遍——GUI 能开是最容易骗人的假象,真正会在第二天坑你的,永远是脚本引擎版本和原生反编译器这两个黑匣子。希望这套流程能帮你少熬一个晚上。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进