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

[首頁](https://rea.tools/zh-TW/) / [案例展示](https://rea.tools/zh-TW/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-TW/figures/dx-ball-sound-pan-investigation.svg)

從可執行檔案中的一個函式，到經過驗證的 C 程式碼：REA 提供指令、呼叫方和讀取的資料，重建專案據此實現並測試函式。[檢視大圖](https://rea.tools/assets/localized/zh-TW/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/)
