2. 遍历加载命令

任务说明

本案例目标是在Ghidra中编写Java脚本,遍历当前程序中所有Mach-O加载命令(Load Command),输出每条命令的类型、在文件中的偏移量以及命令大小。脚本需兼容Ghidra对Mach-O文件的解析接口,可直接在Ghidra脚本管理器中运行。

实现原理

Ghidra通过MachoHeader和LoadCommand接口暴露Mach-O解析结果。每个LoadCommand对象提供getCommandType()获取命令类型枚举、getCommandOffset()获取命令起始偏移、getCommandLength()获取命令总长度(含cmd和cmdsize字段)。脚本只需遍历header返回的命令列表即可完成目标。

完整代码

import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.format.macho.MachoHeader;
import ghidra.app.util.bin.format.macho.commands.LoadCommand;
import ghidra.program.model.listing.Program;

public class ListLoadCommands extends GhidraScript {

    @Override
    public void run() throws Exception {
        Program program = getCurrentProgram();
        if (program == null) {
            println("请先打开一个Mach-O二进制文件");
            return;
        }

        // 获取Mach-O头信息(Ghidra解析后缓存于program中)
        MachoHeader header = MachoHeader.getMachoHeader(program);
        if (header == null) {
            println("当前程序不是有效的Mach-O文件,或Ghidra未能解析其头部");
            return;
        }

        LoadCommand[] commands = header.getLoadCommands();
        if (commands == null || commands.length == 0) {
            println("未找到任何加载命令");
            return;
        }

        println("=== Mach-O加载命令列表 ===");
        println(String.format("%-30s %-12s %-10s", "命令类型", "偏移(hex)", "大小"));
        println("------------------------------------------------------");

        for (LoadCommand cmd : commands) {
            String typeName = cmd.getCommandType().toString();
            long offset = cmd.getCommandOffset();
            int length = cmd.getCommandLength();
            println(String.format("%-30s 0x%08x    %d", typeName, offset, length));
        }
    }
}

运行方式

  1. 在Ghidra中打开一个Mach-O格式的二进制文件(如iOS应用的可执行文件或macOS命令行工具),等待自动分析完成。
  2. 点击 Window → Script Manager,在脚本目录中新建Java脚本,将上述代码粘贴保存为ListLoadCommands.java。
  3. 在脚本管理器中选中该脚本,点击Run执行。

验证方法

脚本执行后,控制台输出应满足以下条件即判定通过:

  • 输出以=== Mach-O加载命令列表 ===开头,且包含表头行。
  • 每一行对应一条加载命令,包含命令类型名称、十六进制偏移和十进制大小。
  • 命令类型名称与Ghidra中LoadCommandType枚举一致(如LC_SEGMENT_64、LC_LOAD_DYLIB等)。
  • 偏移值递增,且每条命令的偏移+大小等于下一条命令的偏移(或等于文件末尾),验证遍历完整性。

若使用系统自带的/bin/ls(Mach-O格式)作为测试对象,预期输出中应包含LC_SEGMENT_64、LC_LOAD_DYLIB、LC_MAIN等常见命令类型,且首条命令偏移通常为0x0(对应LC_SEGMENT_64)。

常见问题

  • 脚本报错MachoHeader.getMachoHeader返回null:确认当前程序确为Mach-O格式,且Ghidra已完成解析(可查看Program Properties中的格式信息)。若仍失败,尝试重新分析文件(Analyze → Auto Analyze)。
  • 输出为空:部分瘦二进制(FAT)包含多个架构,Ghidra默认解析当前选中的架构。若需查看其他架构,需在导入时选择具体架构切片。