c0rner/ghidra_wpc_loader
GitHub: c0rner/ghidra_wpc_loader
Ghidra 扩展,用于加载和分析 Williams 弹珠机 WPC 系统 ROM,自动构建体切换内存映射、I/O 寄存器标签和操作系统级符号。
Stars: 2 | Forks: 0
# WPC Loader — 用于 Williams Pinball Controller 的 Ghidra ROM 加载器
这是一个 [Ghidra](https://ghidra-sre.org/) 扩展,用于加载 Williams Pinball Controller (WPC)
游戏 ROM,并构建一个完整、可导航的内存映射——包括体切换 (banked) ROM 覆盖块、
ASIC I/O 寄存器、中断向量和操作系统级别的 RAM 符号。
## 概述
WPC 弹珠机 (1990–1999) 使用运行频率为 2 MHz 的 **Motorola 68B09E** CPU,具有
64 KB 的地址空间。游戏 ROM 有四种大小:128 KiB、256 KiB、512 KiB 或 1 MiB,
划分为 16 KiB 的页面,并通过 `WPC_ROM_BANK` 硬件寄存器 (`0x3FFC`)
体切换到位于 `0x4000–0x7FFF` 的 CPU 窗口中。
该加载器涵盖了所有六代 WPC 硬件:
| 世代 | 年份 | 关键硬件 |
|---|---|---|
| WPC 字母数字 | 1990–1991 | 14 段显示器,独立的声音板 |
| WPC DMD | 1991–1993 | 128×32 点阵显示器 |
| WPC Fliptronic | 1993–1994 | 专用拨动板 |
| WPC DCS | 1993–1995 | 高保真 DCS 音频板 |
| WPC Security | 1994–1996 | 开关矩阵路径中的 Security PIC 芯片 |
| WPC-95 | 1995–1999 | FPGA 整合板,额外的 RAM 体切换 |
## 内存映射
加载器会创建以下 Ghidra 内存块:
| 内存块 | 地址范围 | 大小 | 权限 | 易失性 | 描述 |
|---|---|---|---|---|---|
| `RAM` | `0x0000–0x1FFF` | 8 KiB | R/W | 否 | 电池供电的 SRAM |
| `RAM_EXT` | `0x2000–0x2FFF` | 4 KiB | R/W | 否 | 额外的 RAM — DCS / WPC-95 *(可选)* |
| `DMD` | `0x3000–0x3BFF` | 3 KiB | R/W | **否** | DMD 显示器 SRAM 窗口 — 见说明 |
| `IO` | `0x3C00–0x3FFF` | 1 KiB | R/W | **是** | WPC ASIC 硬件寄存器 |
| `ROM_PAGE_XX` | `0x4000–0x7FFF` | 16 KiB | R/W/X | 否 | 体切换 ROM 覆盖块 — 见说明 |
| `ROM_SYSTEM` | `0x8000–0xFFFF` | 32 KiB | R/W/X | 否 | 固定系统 ROM — 见说明 |
### 内存块说明
**`DMD` — R/W,非易失性**
DMD 控制板具有 8 KiB 的 SRAM,最多可容纳 16 个位平面(每个 512 字节)。
CPU 最多将两个页面同时映射到 `0x3000–0x3BFF` 窗口中,并对其进行完全的 R/W
访问。至关重要的是,DMD 控制器**无法自主写入**这些 RAM
页面——它仅从中读取数据以刷新显示。这意味着从 Ghidra 的角度来看,该块*不是*
易失性的:CPU 是唯一的写入者,写入的值在 CPU 再次更改之前,
读取时将保持不变。
**`ROM_PAGE_XX` — R/W/X,非易失性覆盖块**
WPC ASIC 在物理上并未对 ROM 窗口进行写保护。CPU 可以(并且确实会)
写入 `0x4000–0x7FFF`,通常用于修补自修改的跳板或就地更新代码向量。
然而,这些写入仅影响 ASIC 总线逻辑中当前映射的页面——它们**不会持久化到 ROM 芯片中**。
当切换到不同的页面并且随后恢复原始页面时,所有写入的数据都会消失。
每个 Ghidra 覆盖块仅模拟单个体切换的快照;跨体的写入效果不会被表示出来。
**`ROM_SYSTEM` — R/W/X**
位于 `0x8000–0xFFFF` 的系统 ROM 反映了硬件的行为:WPC ASIC
未对固定的 ROM 窗口强制执行只读保护,因此 CPU 可以向该区域发出写周期。
实际上,这些写入不会产生任何持久的影响(ROM 芯片会忽略它们),但将该块标记为可写可以防止 Ghidra 将合法的写入指令标记为分析错误。
### 体切换 ROM 页码编号
WPC 硬件页码从地址空间的**顶部**向下计算。
最后两个页面(`0x3E`、`0x3F`)在系统 ROM 窗口(`0x8000–0xFFFF`)
中永久可见,并且未被覆盖。对于总共有 *N* 个 16 KiB 页面的 ROM,体切换页面索引 *b* 将获得硬件页码 `0x3F − (N − 1 − b)`:
| ROM 大小 | 总页数 | 体切换覆盖范围 |
|---|---|---|
| 128 KiB | 8 | `ROM_PAGE_38` – `ROM_PAGE_3D` (6 个页面) |
| 256 KiB | 16 | `ROM_PAGE_30` – `ROM_PAGE_3D` (14 个页面) |
| 512 KiB | 32 | `ROM_PAGE_20` – `ROM_PAGE_3D` (30 个页面) |
| 1 MiB | 64 | `ROM_PAGE_00` – `ROM_PAGE_3D` (62 个页面) |
## 应用的标签
### I/O 寄存器 (`0x3FB8–0x3FFF`)
所有已记录的 WPC ASIC 和外设寄存器均已标记,包括:
- `WPC_ROM_BANK` — 体切换寄存器
- `WPC_ZEROCROSS_IRQ_CLEAR` — IRQ 源 / 看门狗踢除 (watchdog kick)
- `WPC_LEDS` — 诊断 LED
- `WPC_SOL_*` — 螺线管驱动输出
- `WPC_LAMP_*` — 灯矩阵行/列
- `WPC_SW_*` / `WPCS_PIC_*` — 开关矩阵输入 (Security 之前 / WPC-S)
- `WPC_DMD_*` — DMD 页面选择和扫描线寄存器
- `WPC_SHIFTADDR` / `WPC_SHIFTBIT` — 硬件位移位器
- `WPC_RAM_LOCK` / `WPC_RAM_LOCKSIZE` — RAM 写保护
- `WPC_CLK_*` — 实时时钟
- `WPCS_DATA` / `WPCS_CONTROL_STATUS` — 声音板
- `WPC_FLIPTRONIC_PORT_A` — 拨动线圈/开关 I/O
- `WPC95_FLIPPER_*` — WPC-95 拨动器寄存器
- `WPC_ROM_CHECKSUM` / `WPC_ROM_CHECKSUM_DELTA` — 校验和字段
### 中断向量 (`0xFFF0–0xFFFE`)
每个 6809 向量槽被定义为 `Pointer16` 类型,并进行标记(`VEC_RESET`、`VEC_NMI`、
`VEC_SWI`、`VEC_IRQ`、`VEC_FIRQ`、`VEC_SWI2`、`VEC_SWI3`、`VEC_RESERVED`),同时
在其目标地址处创建一个 Ghidra 函数(`_ISR`)。
复位向量目标也被注册为程序的**入口点**。
### RAM 符号 (`0x0000–0x03FF`)
包含来自 WPC 逆向工程工作的大约 50 个操作系统级符号,包括线程
变量、灯矩阵缓冲区、螺线管启用影子、IRQ 计数器和 DMD ISR 状态。
## 加载器选项
Ghidra 的导入对话框中提供两个选项:
| 选项 | 默认值 | 描述 |
|---|---|---|
| **创建体切换 ROM 覆盖块** | ✅ 开启 | 为每个体切换页面创建一个 `ROM_PAGE_XX` 覆盖块 |
| **创建扩展 RAM 块 (DCS/WPC-95)** | ✅ 开启 | 在 `0x2000` 处创建 4 KiB 的 `RAM_EXT` 块 |
如果只需快速分析固定的 ROM,请禁用覆盖块。
## ROM 有效性
加载器接受文件大小恰好为四种有效 WPC ROM 大小之一
(128 KiB、256 KiB、512 KiB、1 MiB)的文件。加载后,消息日志会报告 ROM
版本号(从位于 `0xFFEE` 的校验和字的低字节解码)以及
校验和是否被禁用(`delta = 0x00FF`,开发模式)。
## 环境要求
| 要求 | 版本 |
|---|---|
| [Ghidra](https://ghidra-sre.org/) | **12.0.4** |
| Java | 21(随 Ghidra 捆绑提供) |
## 构建
使用随 Ghidra 捆绑提供的 gradle
**1. 设置您的 Ghidra 安装路径**
```
export GHIDRA_INSTALL_DIR=/path/to/ghidra_12.0.4_PUBLIC
```
**2. 编译:**
```
/path/to/ghidra_12.0.4_PUBLIC/support/gradle/gradlew
# → dist/ghidra_12.0.4_PUBLIC__WPCLoader.zip
```
**3. 在 Ghidra 中安装:**
在 Ghidra 的项目管理器中:*文件 → 安装扩展* → 从 `dist/` 中选择该 zip 文件。
出现提示时重启 Ghidra。
## 用法
1. 打开 Ghidra 并创建或打开一个项目。
2. 将 WPC ROM 文件(`.bin`、`.rom` 或任何扩展名)拖到项目窗口中,
或使用 *文件 → 导入文件*。
3. Ghidra 将通过文件大小检查自动检测 WPC 格式,并建议使用
**WPC ROM Loader** 以及 `6809:BE:16:default`(Motorola 6809,大端序)。
4. 根据需要调整导入选项(覆盖块、扩展 RAM),然后点击 **OK**。
5. 在 CodeBrowser 中打开导入的程序并运行 **自动分析** 以反汇编
系统 ROM。每个体切换的覆盖块都可以单独进行分析。
标签:Ghidra, JS文件枚举, Loader插件, 云资产清单, 后台面板检测, 固件分析, 域名枚举, 嵌入式系统, 游戏模拟器, 逆向工程