跳转至

使用 Ghidra 分析 DOS MZ 与 COM

REA 可通过自带 Ghidra 适配器分析 DOS MZ 可执行文件和显式选择的 COM 映像。 Linux x64 与 macOS arm64 验证通道会通过 CLI 和 stdio MCP 执行实际的 16 位反汇编与反编译。Provider 准入接受 Ghidra 12.1.x 及该安装所声明的 JDK 范围。验证通道使用 Ghidra 12.1.4 和 JDK 21;无需 DOS 模拟器或交叉编译器。

打开并检查

rea inspect /absolute/path/to/legacy.exe --provider ghidra --json
rea search /absolute/path/to/legacy.exe entry --kind procedures --provider ghidra --json
rea instructions /absolute/path/to/legacy.exe entry --provider ghidra --json
rea function /absolute/path/to/legacy.exe entry --provider ghidra --json
rea inspect-native-load-image /absolute/path/to/legacy.exe --provider ghidra --json
rea read-bytes /absolute/path/to/legacy.exe 0x10000 --length 16 --provider ghidra --json
rea address-to-file-offset /absolute/path/to/legacy.exe 0x10000 --provider ghidra --json

当目标未将其入口函数命名为 entry 时,请使用发现过程中观察到的过程名或地址。MCP 使用 open_binary、list_procedures、procedure_info、read_function_instructions 和 analyze_function,并采用相同的准入目标与 provider 语义。

DOS MZ 解析器会使用可用的结构字节和实际文件长度来校验页计数、初始化模块长度、头部与重定位表边界、重定位目标以及初始化入口点。它将这些 DOS 事实与 Windows 头证据分开报告。已识别的 PE 签名仍保持为 PE;已识别的 NE、LE 和 LX 头被明确标记为不受支持。即使 DOS 结构校验通过,非零的 Windows 头指针若无已识别签名,通常仍是未解析候选;自动目标选择会失败关闭(fail closed)并同时报告这两种观察结果。当非空重定位表在完整 DOS 头内部结束时,剩余的头字节属于 DOS 填充;该处的非零 dword 不是 Windows 声明。在应用该填充规则之前,会请求读取当前探针之外尚未读取的签名。这样既避免从原始 DOS 填充中制造出 Windows 头,同时保留了对损坏 Windows 声明的拒绝。

Inventory 分类是一个有界前缀格式提示;对于未解析候选,或当其前缀无法确定格式时,返回 unknown。该结果不同于完整的 DOS 校验,也不决定目标准入。

Ghidra 使用 MzLoader、语言 x86:LE:16:Real Mode 和编译器规范 default 导入私有目标快照。这些设置、固定的 Ghidra 加载段 0x1000 以及线性地址约定都已提交到分析配置文件中。握手过程会拒绝语言或编译器偏差。关闭会话会删除临时项目和快照。

该配置文件还提交了 function_body_evidence: complete-inclusive-ranges-v1,因此来自旧版仅长度 Ghidra 结果的快照不满足当前配置文件。

无头 COM 映像

COM 没有标识头。请显式选择其解释方式;.com 后缀或任意无法识别的字节都不足以使其获得准入。

rea inspect /absolute/path/to/legacy.com --target-format dos-com --provider ghidra --json
rea function /absolute/path/to/legacy.com 0x10100 --target-format dos-com --provider ghidra --json
rea inspect-native-load-image /absolute/path/to/legacy.com --target-format dos-com --provider ghidra --json
{
  "name": "open_binary",
  "arguments": {
    "path": "/absolute/path/to/legacy.com",
    "format": "dos-com",
    "provider_id": "ghidra"
  }
}

调用者的解释适用于整个文件,即使其文件名暗示另一种格式。文件必须包含 1..65280 字节:即 64 KiB COM 段减去 256 字节 PSP 前缀。Ghidra 使用 BinaryLoader 和实模式语言,并将文件偏移零映射到 1000:0100(线性地址 0x10100)。打包的预分析脚本会植入入口函数以及 CS/DS/ES/SS 上下文 0x1000。这些设置和准备策略已提交到分析配置文件中;准备失败会阻止常驻 bridge 提供结果。

加载映像验证会检查所有原始和修改后的文件字节、完整的源映射/内存摘要、不存在重定位、入口以及测得的寄存器上下文。该上下文是为分析而施加的,并非从执行中观察得到。PSP 内存、初始栈内容/SP、DOS 中断和 PC-98 设备均未建模。COM 没有 MZ 头、重定位表或附加覆盖模型;所有选中的字节都属于其分析模块。原始文件保持不变。

地址与函数范围

返回的默认空间地址是线性字节坐标。Ghidra 的 segment:offset 地址的线性值为 segment * 16 + offset;多个段别名可以标识同一字节。REA 接受返回的线性地址用于后续查找,并将指令解码上下文保留在 Ghidra 中。这些坐标是分析加载地址,而非文件偏移或对运行中 DOS 系统的观察。

在读取或标注函数之前,那些会被 Ghidra 截断为不同地址的偏移将被拒绝。这也适用于显式或编码的地址空间名称;前导零保留数值偏移。

procedure_info 和函数档案包含 body 证据:

{
  "available": true,
  "provenance": "ghidra-function-body-address-set",
  "ranges": [
    { "start": "0x10000", "end": "0x1000d" },
    { "start": "0x10060", "end": "0x10065" }
  ],
  "total_bytes": 20,
  "span_bytes": 102,
  "non_contiguous": true,
  "contains_entry": true
}

每个 end 都是包含式的。total_bytes 统计完整观察到的 AddressSet;span_bytes 测量其包围范围,跨地址空间时为 null。间隙和共享尾部不得被视为自有字节。这是 Ghidra 数据库的观察结果,并非原始源码所有权的证明,也不代表穷尽式代码发现。未报告完整范围的 provider 会返回 available: false 并附有明确原因。

已加载镜像的证据

inspect_native_load_image 独立地将不可变的会话快照与已加载的 Ghidra Program 进行核对。它测量完整的原始和修改后的 FileBytes 摘要、每个源映射及其内存摘要、重定位记录(包括多重性)以及外部入口点。它验证 MZ 头与模块分区、重定位修正和线性坐标,而不是仅依赖导入的可执行文件 SHA。

预期的 DOS 重定位记录保留每个修正项应用前的字节和已应用的值。它们的内存字节窗口来自最终重定位后的镜像,包括后续重叠修正项所做的更改。

结果为 verified、mismatch 或 unsupported。每项检查都保留其预期值和观测值。内存摘要不匹配会标识源范围的文件偏移和分析地址;它不会定位该范围内第一个不同的字节。映射端点是包含式的,不同于 list_segments 的排除式端点。追加的 overlay 保留在源文件标识中,并被排除在已初始化模块覆盖范围之外。未初始化的分配会被报告;其内容及 DOS 运行时语义不被验证。独立验证也涵盖显式的 raw-binary 导入;其他格式返回带实测观测值的 unsupported。

MZ 验证保留由头部页计数所描述的已初始化镜像,并将追加的 overlay 字节流式纳入完整的原始和修改后的源摘要。overlay 大小不会增加保留的镜像分配。

read_bytes 返回已初始化的 provider 内存,包括加载器修正;未映射或未初始化的字节会以显式返回长度和 complete: false 结束读取。address_to_file_offset 使用 provider 的源映射;未映射、未初始化及有歧义的映射会显式失败。即使源偏移已知,重定位后的字也可能与原始文件字不同。

覆盖边界

静态反编译不执行目标,也不模拟 BIOS、DOS 中断、设备端口或自修改代码。PC-98 硬件行为和恢复出的调用约定可能仍然不确定;provider 伪代码和诊断信息会保留以供分析师审查。

对于加壳的可执行文件,导入原始文件即可分析其解包存根。要检查原始程序恢复出的代码,请分析单独准备的脱壳副本。保留两个工件摘要和转换证据;REA 绝不会静默替换已接纳的目标。追加的 overlay 与已初始化的 MZ 模块是分开的。无头 COM 需要显式 COM 解释;REA 不会根据文件名或无法识别的字节进行猜测。未含已接纳 MZ 实模式入口的 DOS 扩展器仍处在此导入边界之外。

DOS MZ 可通过 Ghidra 获得,且不宣称支持 Hopper。它位于 Windows 原生 PE P0 边界之外。macOS x64 上的 DOS 验证仍未经验证。

真实验证

npm run verify:ghidra:dos

该验证通道会在运行时从源代码生成一个小型公共 fixture。它验证 16 位指令字节、近调用、段重定位远调用、实际反编译、不相交的函数范围、稳定的 CLI/MCP 观测值、未改变的输入字节,以及会话/进程清理。它不需要外部可执行 fixture、模拟器或现有分析数据库。

Provider 的 p-code 表示可能包含进程特定的地址空间选择器令牌。该验证通道保留这些报告值,并比较稳定的 dossier 观测值,而不是断言原始 p-code 的跨进程同一性。

函数注解

annotate_native_function 会在一个确切的本地函数入口处更改名称和/或注释,验证回读,并返回刷新后的 dossier。这在记录恢复出的 DOS 函数角色时很有用;它并不声称这些名称来自原始源代码。名称写入会保留现有命名空间;回读返回函数清单所使用的完全限定名称。普通注释映射到 Ghidra PRE 注释,行内注释映射到 EOL 注释。空文本清除注释;省略字段则保留注释。如果 setter、回读或分析失败,整个编辑会回滚。

{
  "procedure": "0x10100",
  "name": "entry",
  "comment": "Recovered startup role",
  "inline_comment": "Inspect segment setup"
}
rea annotate-native-function /local/program.com 0x10100 --target-format dos-com --provider ghidra --name entry --comment 'Recovered startup role' --json

CLI 在关闭其隔离会话之前返回更新后的分析。在 close_binary 之前,MCP 编辑对后续读取保持可见。原始字节保持不变,数据库编辑在关闭时被丢弃,不可变分析快照在编辑后失效。此操作不会保存 Ghidra 项目、更改 ABI/类型/函数体范围、修补指令,或模拟 DOS 运行时。Windows P0 不接纳此操作。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 rea2/rea