---
title: DX-Ball 逆向工程：声像计算 — REA
url: https://rea.tools/zh-CN/showcase/dx-ball/
---

[首页](https://rea.tools/zh-CN/) / [案例展示](https://rea.tools/zh-CN/showcase/) / DX-Ball

案例 · DX-Ball 1.07

# 从可执行文件 重建 DX-Ball。

目标是为这款 1996 年的 Windows 游戏重建一份可维护的 C 代码。REA 帮助编程助手逐个分析原版函数。

Windows i386 · C · REA

![用 REA 检查 DXBALL.EXE 中的函数 0x00406400。砖块碰撞调用方传入 20 + 30 × tile_x。指令加载 x，乘以 1.5625，减去 500.0，再乘以 pan_scale，最后返回整数。重建的 C 代码通过 3,205 个用例与原始 x86 函数比较，并用 VC4.0 编译结果匹配全部 63 字节。](https://rea.tools/assets/localized/zh-CN/figures/dx-ball-sound-pan-investigation.svg)

从可执行文件中的一个函数，到经过验证的 C 代码：REA 提供指令、调用方和读取的数据，重建项目据此实现并测试函数。[查看大图](https://rea.tools/assets/localized/zh-CN/figures/dx-ball-sound-pan-investigation.svg)

55
:   个可维护的 C 函数

45,380
:   与原始代码的比较

33
:   个编译后字节匹配的函数

项目进度截至 2026 年 10 月 7 日。[进度记录](https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/docs/PROGRESS.md) · [测试覆盖情况](https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/docs/ORACLES.md)

## 当前进度

目前已还原关卡与资源处理、砖块碰撞、动画、粒子和奖励生成。当前构建提供检查工具与分析库。可玩的游戏版本还在开发，后续工作包括球和挡板的物理逻辑、道具处理及 Windows 集成。

## 找出声音的左右位置如何计算

砖块被击中时，它的横坐标会传给声音处理代码。这次调查要找出：屏幕坐标如何转换为声音的左右声像值？

你的编程助手

用 REA 找出 DX-Ball 如何计算声音的左右位置。解释计算过程，并展示代码。

把本地可执行文件交给编程助手，再使用这个示例 prompt。下面展示助手为回答问题发起的 REA 查询，以及得到的证据。

助手用 REA 检查函数、追踪调用它的砖块碰撞函数，再从可执行文件中读取常量。这些结果确定了输入和计算步骤，才有了下面的 C 函数。

REA 最初返回的 `0x406400` 伪代码声明了 `FUN_00406400(void)`，并调用 `__ftol()`。但指令中还有来自栈的输入和浮点运算。这个差异提示助手：还需要继续查。

最初的反编译结果

REA结果 · 伪代码摘录

```
longlong FUN_00406400(void)
{
  longlong lVar1;

  lVar1 = __ftol();
  return lVar1;
}
```

结果里能看到一次转换调用。继续阅读下面的指令，就能找出输入，以及传入这个调用前的计算。

### 用 REA 回答三个问题

1. #### 找回缺失的输入

   编程助手→ REA · `analyze_function`

   ```
   {"procedure": "0x406400"}
   ```

   REA →编程助手·指令摘录

   ```
   0x406409: MOV EAX, dword ptr [EBP + 0x8]
   0x40640c: MOV dword ptr [EBP + -0xc], EAX
   0x40640f: FILD dword ptr [EBP + -0xc]
   ```

   函数结果同时提供伪代码、指令和调用方。这里的 `[EBP+8]` 显示了反编译结果中缺失的整数输入。
2. #### 追踪调用方，理解输入含义

   编程助手→ REA · `analyze_function`

   ```
   {"procedure": "0x411f40"}
   ```

   REA →编程助手·调用方摘录

   ```
   0x411f5b: ADD EAX, 0x14
   0x411f5e: PUSH EAX
   0x411f5f: CALL 0x00406400
   ```

   REA 在第一次结果中就给出了这个调用方。调用前的指令将 `tile_x` 乘以 30，再加上 20，得到砖块的屏幕坐标 `20 + 30 × tile_x`。
3. #### 读取内存地址后面的值

   助手 → REA · 两次 `read_bytes` 请求

   ```
   {"address": "0x420068", "length": 16}
   {"address": "0x4210a0", "length": 8}
   ```

   REA 字节结果 · 双精度浮点数解码

   ```
   000000000000f93f → 1.5625
   0000000000407f40 → 500.0
   000000000000f03f → 1.0
   ```

   返回的字节确定了两个运算常量，以及声像缩放系数的初始值。助手由此得到编写 C 表达式所需的数值。

### 从 REA 分析结果到 C 代码

下面分五步展示计算。点击每一步，原始指令和对应的 C 代码行会同时高亮。

原始 x86 · 节选

```
0x406409: MOV EAX, dword ptr [EBP + 0x8]
0x40640c: MOV dword ptr [EBP + -0xc], EAX
0x40640f: FILD dword ptr [EBP + -0xc]
0x406415: FMUL double ptr [0x00420068]
0x40641e: FSUB double ptr [0x00420070]
0x406427: FMUL double ptr [0x004210a0]
0x406430: CALL 0x0041678c
0x40643e: RET
```

重建的 C 代码

```
DxBallInt dxball_screen_pan(DxBallInt x)
{
    double pan;
    pan = (double)x;
    pan = pan * 1.5625;
    pan = pan - 500.0;
    pan = pan * dxball_pan_scale;
    return (DxBallInt)pan;
}
```

**01 · 读取输入。** REA 返回的指令读取 `[EBP+8]`，再通过 `FILD` 加载这个整数。用 REA 追踪调用方，就能确定 C 参数 `x` 表示屏幕坐标。

**02 · 乘以 1.5625。** `FMUL` 读取 `0x420068` 处的双精度浮点数。REA 的 `read_bytes` 返回的字节解码后是 `1.5625`，C 代码使用相同常量。

**03 · 减去 500.0。** `FSUB` 读取 `0x420070` 处的双精度浮点数。REA 返回的字节解码后是 `500.0`，让计算的中心对应屏幕坐标 `320`。

**04 · 乘以存储的缩放系数。** 第二条 `FMUL` 读取 `0x4210a0`。这个全局变量在重建源码中名为 `dxball_pan_scale`。REA 的第二次字节读取确认它的初始值是 `1.0`。

**05 · 返回整数。** `0x406430` 处的调用进入 `__ftol`，REA 返回的被调用函数信息确认了这一点。C 代码把结果转换为 `DxBallInt`，再与原始 x86 函数比较，验证返回值。

这段汇编节选包含输入、运算和返回指令。下面的完整列表还保留了中间的存储操作，以及函数开头和结尾的处理。

完整汇编与相关证据

REA结果 · 0x406400–0x40643e

```
0x406400: PUSH EBP
0x406401: MOV EBP, ESP
0x406403: SUB ESP, 0xc
0x406406: PUSH EBX
0x406407: PUSH ESI
0x406408: PUSH EDI
0x406409: MOV EAX, dword ptr [EBP + 0x8]
0x40640c: MOV dword ptr [EBP + -0xc], EAX
0x40640f: FILD dword ptr [EBP + -0xc]
0x406412: FST double ptr [EBP + -0x8]
0x406415: FMUL double ptr [0x00420068]
0x40641b: FST double ptr [EBP + -0x8]
0x40641e: FSUB double ptr [0x00420070]
0x406424: FST double ptr [EBP + -0x8]
0x406427: FMUL double ptr [0x004210a0]
0x40642d: FST double ptr [EBP + -0x8]
0x406430: CALL 0x0041678c
0x406435: JMP 0x0040643a
0x40643a: POP EDI
0x40643b: POP ESI
0x40643c: POP EBX
0x40643d: LEAVE
0x40643e: RET
```

### 调用方传入什么

`0x411f40` 处的砖块碰撞函数把格子坐标乘以 30，再加上 20，然后在调用前将结果压栈：

```
0x411f50: MOV EAX, dword ptr [EBP + 0x8]
0x411f53: ADD EAX, EAX
0x411f55: LEA EAX, [EAX + EAX*0x2]
0x411f58: LEA EAX, [EAX + EAX*0x4]
0x411f5b: ADD EAX, 0x14
0x411f5e: PUSH EAX
0x411f5f: CALL 0x00406400
0x411f64: ADD ESP, 0x4
```

### 读取数据确认了什么

REA 读取的字节，按小端双精度浮点数解码

| 地址 | 数值 | 用于 |
| --- | --- | --- |
| `0x420068` | `1.5625` | 第一次乘法 |
| `0x420070` | `500.0` | 减法 |
| `0x4210a0` | `1.0` 最初 | 存储的声像缩放系数 |

使用 REA 4.1.0 记录，分析目标为通过哈希确定的 DX-Ball 1.07 文件。链接的调查笔记保留了 Evidence ID。

[重建源码](https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/src/gameplay.c#L41) · [调查笔记与证据引用](https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/docs/GAMEPLAY_OWNER.md)

## 验证还原的函数

还原的函数通过了两项检查：返回值与原版一致，编译后的字节与原始可执行文件一致。

### 3,205 个行为测试用例

测试执行原始 x86 函数，将返回值与重建的 C 函数比较。测试覆盖 0 到 640 的所有整数位置，并分别使用五个声像缩放系数：0、0.5、1、20 和 −1。

### 63 字节完全匹配

C 函数使用指定的 VC4.0 工具链编译。应用经过检查的重定位，并核对引用的常量后，编译结果与原始函数的全部字节一致。

缩放系数为 1 时，计算在屏幕左边缘返回 −500，中心返回 0，右边缘返回 500。接下来要将这个值接入游戏的声音后端。

[阅读编译器重播详细信息](https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/docs/BUILD_MATCHING.md)

## 继续调查

粒子更新、爆炸队列和奖励生成也使用同样的方法：检查相关函数，还原状态与依赖，再把实现和原版比较。

如果你也想分析这个声像函数，先按 DX-Ball 项目的说明配置环境，并准备匹配的原始文件。然后运行：

在 DX-Ball 仓库目录中

```
scripts/rea function original/DXBALL.EXE 0x00406400 \
  --snapshot .analysis/rea/dxball.snapshot.json --json
```

- [DX-Ball 仓库](https://github.com/N0zoM1z0/dx-ball)当前源码、进度和本地配置。
- [REA 分析流程](https://rea.tools/guides/native/)追踪这段计算，再用同样方法分析自己的二进制文件。
- [粒子与奖励生成](https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/docs/ENTITIES_OWNER.md)进一步分析状态、队列和像素写入。

[从你自己的目标开始](https://rea.tools/get-started/)
