1. 解析Mach-O文件头
任务说明
本案例目标:使用Ghidra加载一个简单的iOS Mach-O二进制文件,通过Ghidra的Mach-O解析能力识别文件头中的魔数(magic)、CPU类型(cputype)、文件类型(filetype)等关键字段,并验证解析结果与otool命令行输出一致。
环境准备
- Ghidra 11.x(已安装并完成首次初始化)
- macOS或Linux主机,已安装Xcode Command Line Tools(提供
otool) - 示例文件:使用Xcode编译一个最小iOS静态库(或直接使用任意现有iOS .a文件)
生成示例Mach-O文件
若手头没有现成的iOS Mach-O文件,可执行以下命令生成一个最小静态库(包含arm64架构):
# 创建最小C源文件
echo 'int dummy_symbol(void) { return 42; }' > dummy.c
# 使用iOS SDK交叉编译为arm64目标文件
xcrun -sdk iphoneos clang -arch arm64 -c dummy.c -o dummy.o
# 打包为静态库(.a本质上是Mach-O格式的归档文件)
ar rcs libdummy.a dummy.o
# 查看文件头信息(验证用)
otool -hv libdummy.a
Ghidra导入与解析步骤
- 启动Ghidra,创建新项目(Non-Shared项目即可)。
- 将
libdummy.a(或dummy.o)拖入Ghidra项目窗口。 - 双击文件,Ghidra自动识别格式为
Mach-O,弹出导入对话框,保持默认选项点击OK。 - 在
Analyze窗口中点击Yes执行自动分析(对于静态库,分析很快完成)。 - 分析完成后,在代码浏览器中打开
Program Tree窗口,展开Headers目录。
关键字段识别与验证
在Ghidra的Headers目录下,双击mach_header结构体,即可在Listing窗口中看到解析后的文件头字段。对照下表确认关键字段值:
| 字段 | 偏移 | 预期值(arm64静态库) | 说明 |
|---|---|---|---|
| magic | 0x00 | 0xfeedfacf | MH_MAGIC_64(小端) |
| cputype | 0x04 | 0x0100000c | CPU_TYPE_ARM64 |
| cpusubtype | 0x08 | 0x00000000 | CPU_SUBTYPE_ARM64_ALL |
| filetype | 0x0c | 0x00000002 | MH_OBJECT(.o文件)或0x00000006(静态库成员) |
| ncmds | 0x10 | ≥1 | Load commands数量 |
| sizeofcmds | 0x14 | ≥0 | 所有Load commands总字节数 |
| flags | 0x18 | 0x00200085 | 包含MH_SUBSECTIONS_VIA_SYMBOLS等标志 |
验证方法与判定标准
- Ghidra内部验证:在Listing窗口确认
magic字段显示为feedfacf,cputype显示为0100000c,filetype显示为2(MH_OBJECT)或6(MH_DYLIB_STUB等,取决于文件类型)。 - 与otool交叉验证:在终端执行
otool -hv libdummy.a,观察输出中的Magic、Cputype、Filetype列,与Ghidra中解析出的值逐一比对,必须完全一致。 - 判定通过条件:Ghidra中识别出的魔数、CPU类型、文件类型与
otool输出一致,且能正确区分64位(feedfacf)与32位(feedface)Mach-O文件。
常见问题
- Ghidra无法识别文件:确认文件确实是Mach-O格式(可用
file libdummy.a检查),且未损坏。 - 字段显示为原始字节而非解析值:在Listing窗口右键选择
Data→Structure,确保已应用mach_header_64结构体模板。 - 静态库与目标文件差异:
.a归档文件包含多个成员,每个成员都有独立的Mach-O头;Ghidra会分别列出。若直接导入.o文件则只有一个头。