# moonpe 与既有 MoonBit 库的关系说明

## 1. 结论

MoonBit 生态里已有 `xmyd0915/moonar`（0.6.1，2026-10-21 发布）。它和 moonpe 都属于
"PE/COFF 家族"，但处理的是**不同的文件类型**：

- **moonar** 处理 **ar 归档与 COFF 对象**：`.a`、GNU thin archive、`.lib` 静态库/导入库、
  `.obj` 及其符号表。
- **moonpe** 处理 **PE 映像**：`.exe`、`.dll`、`.sys`，即已经链接完成、由 Windows 加载器装载的文件。

两者唯一共享的是 **PE/COFF 格式自身规定的那 20 字节文件头与 40 字节节头**——这是格式，
不是实现选择。PE 映像独有的部分（DOS 桩、可选头、数据目录、导入/导出表、RVA 映射），
moonar 完全没有；归档容器（`!<arch>\n`、长文件名表、符号索引），moonpe 完全没有。

## 2. 实测对照

两台工具对同一个输入的实际输出：

| 输入 | moonar 0.6.1 | moonpe 0.1.0 |
| --- | --- | --- |
| `System32\kernel32.dll` | `AR001 at byte 0: expected ar archive magic` | PE64 / AMD64 / 8 节 / 104 导入库 / **3 延迟加载库** / 1697 导出 / PDB `kernel32.pdb` / 版本 `10.0.26100.9444` |
| `SysWOW64\kernel32.dll` | 同上（不是归档） | **PE32 / i386** / 102 导入库 / 3 延迟加载库 / 1669 导出 / PDB `wkernel32.pdb` |
| `System32\shell32.dll` | 同上（不是归档） | PE64 / 79 导入库 / **138 延迟加载库** / 843 导出 |
| `System32\notepad.exe` | 同上（不是归档） | PE64 / 50 导入库 / 6 延迟加载库 / 0 导出 |
| `demo.a`（llvm-ar 打包 `bcheck.o`） | `0  118560  "bcheck.o"` | `moonpe: bad signature at 0x0: expected the 'MZ' DOS signature` |

复现命令：

```
moonar list C:/Windows/System32/kernel32.dll
moonar list demo.a
moon run cmd/moonpe --target js -- info C:/Windows/System32/kernel32.dll
moon run cmd/moonpe --target js -- info demo.a
```

## 3. 使用者据此能作出的判断

拿到一个来路不明的 `kernel32.dll` 副本，需要判断"这个文件是不是正版系统组件、能不能安全放进发布包"：

- **能不能打开**：moonar 直接拒绝（它不是归档），所以后续所有判断都无从谈起。
- **位数与架构**：moonpe 报告 PE64 / AMD64，可以确认它不会被误装进 32 位进程。
- **依赖面**：moonpe 报告 104 个静态导入库 + 3 个延迟加载库。更极端的例子是 shell32.dll：
  静态导入 79 个、延迟加载 138 个。只看静态导入会漏掉 64% 的依赖，发布的包在首次调用相关 API 时才会缺 DLL。
- **可调试性**：moonpe 报出 PDB 路径 `kernel32.pdb`，可以据此去符号服务器匹配。
- **版本**：moonpe 从 VERSIONINFO 读出 `10.0.26100.9444`，可以和目标系统的期望版本对比。
- **是否被改过**：moonpe 报告 7 个以上重定位块、绝大部分是 DIR64；如果与官方版本差异很大，
  说明文件可能被重打包过。

反过来，拿到一个 `.lib` 导入库需要查成员和符号时，moonpe 会明确拒绝（不是 PE 映像），
此时用 moonar 才是正确工具。

## 4. 复用关系

- moonpe **不依赖** moonar，也没有复制其代码。
- 两者共享的 COFF 头/节头解析是 PE 规范强制的，不是重复劳动；moonpe 的解析只服务于
  PE 映像的内存映射语义（RVA、数据目录、节填充）。
- 如果未来需要读 `.lib` 成员列表，正确做法是调用 moonar，而不是在 moonpe 里再写一遍归档解析。

## 5. PE32 / PE32+ 的规范覆盖范围

| 区域 | 覆盖 |
| --- | --- |
| DOS 头、`e_lfanew` | 读取并校验 |
| `PE\0\0` 签名 | 校验 |
| COFF 文件头（machine / 节数 / 时间戳 / 特征） | 全字段 |
| 可选头 PE32 (`0x10B`) | 全部字段（含 32 位 image base、4 字节栈堆、4 字节 thunk） |
| 可选头 PE32+ (`0x20B`) | 全部字段（含 64 位 image base、8 字节栈堆、8 字节 thunk） |
| 数据目录 | 16 个槽位全部命名并给出地址范围与文件映射 |
| 节表 | 名称、虚拟/原始大小、地址、权限 |
| RVA → 文件偏移 | 支持，且拒绝节内零填充尾部 |
| 导入表 | 按名字 + 按序号 |
| **延迟导入表** | **按名字 + 按序号；RVA 描述符与 VA 描述符两种形式** |
| 导出表 | 命名导出 + 仅序号导出 |
| 调试目录 | 类型、时间戳、CodeView `RSDS`/`NB10`、PDB 路径与 age |
| 基址重定位 | 按块、按类型统计（DIR64/HIGHLOW/…） |
| 资源目录 | 三级树枚举（类型/名称/语言） |
| VERSIONINFO | 固定版本号 + 字符串表（产品名、公司、文件描述、原始文件名） |
| LoadConfig | 安全 Cookie、SafeSEH 表与计数、CFG 表与 GuardFlags、堆标志、代码完整性 |
| Security（证书表） | `WIN_CERTIFICATE` 条目：长度、版本、类型（不做验签） |
| Exception | 运行时函数表：条目数与 RVA 跨度 |
| IAT | 由导入表的 `FirstThunk` 提供，与导入符号一一对应 |
| Architecture / GlobalPtr / Reserved | PE 规范保留，未定义语义，只报告位置 |


### 5.1 LoadConfig / SafeSEH 实例

同一台机器上两个真实文件的实测解析结果：

| 字段 | `SysWOW64\kernel32.dll`（PE32） | `System32\kernel32.dll`（PE64） |
| --- | --- | --- |
| LoadConfig Size | `0xc4` | `0x148` |
| SecurityCookie | `0x100b0170` | `0x1800c1230` |
| SafeSEH 处理器数 | **3** | 不适用（x64 无 SafeSEH） |
| SafeSEH 首个处理器 RVA | `0x1dfb0` | — |
| GuardFlags | `0x10017500` | `0x10417500` |
| CFG 函数表条目 | 1531 | 1535 |
| 证书表条目 | 1 | 1 |

**预期解析结果与使用者判断**：

- 看到 PE32 且 SafeSEH 处理器数为 3 → 该 32 位镜像启用了 SafeSEH，异常处理链会被校验；
  如果同一镜像 DEP 已开启但 SafeSEH 为 0，`check` 会给出 `NO_SAFESEH` 警告。
- 看到 PE64 → SafeSEH 不适用，`check` 明确报告 "not applicable"，而不是伪装成 0 个处理器。
- 看到 CFG 函数表 1531/1535 项 → 控制流保护已启用，可用作"是否被重打包"的对照基线。
- 看到证书表 1 条 → 镜像内嵌了 Authenticode 签名（是否有效需另行验签，本库不做）。## 6. 延迟导入的边界

延迟导入不是"可选功能"，而是**依赖检查必须覆盖的一类标准依赖**：

- 描述符位于数据目录槽位 13，每个 32 字节；
- 第一个字段 `Attributes` 的 bit 0 为 `dlattrRva`：置位时后续地址字段是 RVA，
  清零时是虚拟地址，需要减去 image base；
- thunk 表宽度随 PE32/PE32+ 变化（4 字节 / 8 字节），序号标志位分别在 bit 31 / bit 63；
- 终止条件是 8 个字段全为 0 的描述符。

moonpe 对这两种描述符形式都有实现和测试；`kernel32.dll` 上实测报出 3 个延迟加载库。

## 7. "完整格式支持"的验收终点

本项目把 v1.0 定义为下面这份清单全部满足，而不是"能做多少算多少"：

1. PE32 与 PE32+ 的可选头字段全部可读，且有跨位宽对照测试；
2. 16 个数据目录全部解析到"名称 + 地址 + 文件映射状态"；
3. 依赖类：导入、**延迟导入**、导出、IAT 全部可读；
4. 结构类：节表、基址重定位、调试目录（PDB）、资源树、VERSIONINFO、LoadConfig/SafeSEH、证书表全部可读；
5. 任意截断/畸形输入只产生 `PeError`，不崩溃；
6. 至少 3 个真实系统二进制（含 PE32 与 PE32+）的输出与系统工具抽样一致；
7. README 为 16 个目录逐项记录语义支持级别（解析 / 摘要 / 规范保留）。

第 1–7 项全部完成。第 6 项覆盖 `System32\kernel32.dll`（PE64）、
`SysWOW64\kernel32.dll`（PE32）、`System32\shell32.dll`、`System32\notepad.exe`，输出见第 2 节。
