逆向工具

  • ChatGPT 5.5
  • IDA MCP
  • IDA Pro 9.1
  • Wie Emulator

逆向檔案

  • 0002E1A1.jar/binary.mod

逆向目標

  • Sera Shop(세라샵)內的線上商品可隨意購買。
  • 完全跳過開幕的網路校驗,無需做無意義的等待。

修補準則

  • 基於原遊戲語義。
  • 維持 binary.mod 的原始大小,不擴展 .text,不移動 ELF section。

切入點

Sera Shop 字串

  • 세라샵:商店入口
  • 구입하시겠습니까:購買確認訊息
  • 구입하였습니다:購買成功訊息,訊息 ID 10006
  • 인벤공간이 부족합니다:背包空間不足
  • 현금정보이용료통화료별도:運營商計費 KEY

通過定位這些關鍵字串,範圍可被限定到 商店 UI、訊息渲染、購買確認和伺服器成功處理 附近。

Sera Shop 事件狀態

KEY 全局變數:

Address Meaning
0x0150c464 當前 UI/state 描述
0x0150c520 當前確認面板選項
0x0150b268 事件槽數組,每槽 0x14 Byte
0x0150c484/0x0150c485 排程可見的當前事件
0x0150b0b8 Sera 當前商品類型和索引
0x0150c446 當前選中的商品和索引

KEY State:

State Meaning
0x27 普通購買詳情/確認面板
0x12 數量類購買流程
0x28 等級/數量選擇面板
0x2a 原運營商確認計費面板

開幕網路驗證狀態

開幕驗證的關鍵為『初次啟動狀態機』:

  • 0x0150000c:啟動認證計數/完成標記。
  • handleCommand(7000):初次認證入口,原鏈路會進入 0x63 -> 0x64 -> 0x65
  • handleCommand(2000):並行網路等待入口,原鏈路會進入 0x6d/0x6e
  • setGameState:啟動後進入 標題/認證/遊戲狀態 的核心狀態切換。
  • 0x62ae0:標題階段渲染分流,錯誤狀態會進入未初始化完整場景渲染。

逆向定位路徑

1. 定位『購買成功』的路徑

使用遊戲原本的鏈路完成購買:

1
2
3
0x1efc4: Sera success processor
0x81329: backpack item insertion
0x1b428: processMessage(id)

最終本地物品發放鏈路為:

1
2
3
4
5
6
0x1e604
-> 0x8114c
-> scan physical inventory slots
-> update [player + 0x1f5]
-> 0x1efc4
-> processMessage(10006)

2. 繞過無效伺服器購買校驗

Address Final behavior
0x1e582 0x4b 『直接確認類』商品進入 0x1e604
0x1e66e 0x4b 『直接確認類』商品進入 0x1e604
0x1de38 『數量選擇類』商品確認之後進入 0x1e604
0x1e3ba state=0x2a 兜底進入 0x1e604
0x1e39a SKIP f670/f5b0 舊計費檢查,進入 0x1e604

NOTE: 『直接確認類』商品不一定是 type == 0x4b,只修 0x4b 會使 裝備1800s 類商品在規則之外。

3. 修正『背包已滿』的誤判

進入 0x81329 前的背包計數元資料不可信,因此:

1
2
3
4
5
0x1e604
-> 調用 0x8114c 整理/重建背包
-> 掃描 [player + 0x1f9 + n * 0x3c]
-> 找到第一個空槽位
-> 寫回 [player + 0x1f5]

0x81329 的滿包判斷只以物理 90 格為最終邊界:

  • 0x81350
  • 0x8140e

這樣既避免空包誤判,也不允許越界寫背包陣列。

4. 返回/取消必須恢復事件棧

返回的正確語意是彈出目前確認事件,並恢復上一層商店事件。

原函式 0x10014(index) 的語義正確:

1
2
clear 0x0150b268 + index * 0x14
restore previous event to 0x0150c484/0x0150c485

不安全之處,則在於原函式內部透過間接的 native/memset 清除事件槽;在 WIE 執行路徑中,會觸發 undefined instruction。

最終的修法是改寫 0x10014 本體:

  • 以 5 次直接 str 將 20 Byte 的事件槽清零。
  • 保留後半段恢復前一事件的邏輯。

5. 等級商品必須阻止舊計費視窗的建立

在舊的偵錯過程中,曾嘗試在 state=0x2a 內部跳過計費檢查,但實際測試中仍會出現「購買成功,但彈窗下方又再次生成商品彈窗」的問題。

原因在於補丁位置太晚:0x2a 視窗已由父狀態建立。

正確的建立點在 state=0x28

1
2
0x1df4a..0x1dfae: 寫入商品類型、索引、等級/數量
0x1dfb0: bl 0x1b0c0 ; 原版建立 state=0x2a 運營商計費視窗

最終修法:

1
2
3
4
5
6
7
0x1dfb0: b    0x1e3ee
0x1dfb2: mov r8, r8

0x1e3ee: ldr r3, =0x0150c464
0x1e3f0: ldrb r0, [r3, #1]
0x1e3f2: bl 0x10014 ; pop current level-select event
0x1e3f6: b 0x1e604 ; local grant

6. 開幕驗證必須保留初始化,只替換狀態目標

失敗的偵錯方向:

  • 直接跳過首次啟動分支:會跳過必要初始化,導致進入錯誤的 UI。
  • 只隱藏 0x63/0x6e 視窗:輸入仍停留在認證/網路狀態,標題畫面按鍵無效。
  • caller-side no-op handleCommand(7000/2000):會留下不一致狀態,導致渲染或輸入崩潰。
  • 在補丁 stub/新堆疊框架中呼叫 0x6ccc:此方向錯誤。0x6ccc0x5cf0 大函式的共享 epilogue,並非一般函式。

最終策略:

保留首次啟動初始化,並將首次啟動後的狀態改為非首次的標題狀態

1
2
3
4
5
6
7
8
9
10
11
12
0x5dda: bl   0x6ea61
0x5dde: ldr r2, =0x0150000c
0x5de0: movs r3, #1
0x5de2: strb r3, [r2]
0x5de4: mov r8, r8
0x5de6: movs r0, #0
0x5de8: movs r1, #4
0x5dea: mov r8, r8
0x5dec: movs r2, #0
0x5dee: movs r3, #0
0x5df0: bl setGameState
0x5df4: bl 0x6ccc ; 原始函式內跳入共享 epilogue

這裡保留反組譯看到的 bl 0x6ccc,但不能將它理解為一般函式呼叫。0x5df4 位於 0x5cf0 這個大函式內部,執行到此處時,當前堆疊框架正是 0x6ccc epilogue 預期要彈出的那個框架。

先前失敗的是在補丁 stub 內另起一個呼叫框架後再 bl 0x6ccc。這會讓 0x6ccc 從錯誤的堆疊位置 pop 返回地址,最終跳到無效地址。因此『不可作為一般函式呼叫』是指不可從外部 stub/新堆疊框架 呼叫它,而非要改掉原始 0x5df4 這條控制流。

並保留兩處配套修正:

1
2
0x1c66e: b    0x1c684    ; [0x0150000c] == 2 時跳過 handleCommand(2000)
0x62ad4: cmp r2, #0 ; state=0 走標題輕量渲染

關鍵結論

  • 0x1efc4 是 Sera Shop 本地成功處理的正確抽象。
  • 0x12c4c 並非通用的購買發放函式,不可傳入 Sera 商品索引。
  • 直接確認商品不等於 type == 0x4b
  • 背包容量須以物理 90 格陣列為最終邊界。
  • 返回/取消是事件堆疊恢復問題,而非 UI 跳轉問題。
  • 等級商品的問題在於 state=0x28 建立 state=0x2a 的時機,而非 state=0x2a 內部。
  • 開幕驗證不能只遮蔽視窗,必須讓狀態機進入原版非首次的標題狀態。
  • 0x6ccc0x5cf0 的共享 epilogue,只能沿原函式內控制流抵達;不可從補丁 stub 當一般函式呼叫。
  • WIPI/Thumb-1 補丁填充必須使用 c046,不可引入 Thumb-2 的 00bf
  • 維持相同大小的原地補丁,是 WIPI ELF 最穩健的交付方式。

補丁思路

正確的補丁點應位於狀態機已準備好資料、但即將進入已失效線上流程之前。

購買鏈路:

1
2
3
4
繞過已失效的線上檢查
保留原遊戲的購買成功處理器
在進入成功處理器前修正本地狀態
依事件堆疊語義關閉目前的 UI

啟動鏈路:

1
2
3
4
保留首次啟動初始化
標記認證已完成
進入原版非首次的標題狀態
避免隱藏認證視窗後殘留認證輸入狀態

實驗性:音頻 Hack

因韓國手機的硬體限制,多數遊戲至多同時播放一個音軌。

這很煩人,因為音樂頻頻被音效打斷的感覺並不好。

不過既然現在有模擬器,那為何不作 Patch 呢?

修補的思路如下:

  1. .text 追加 0xd4 Byte 声音 stub。
  2. .bss 擴大到 0x3b100,用於新增音樂 handle 0x0153b000
  3. 0x6c722: bl 0x6c428 改為 bl 0x9d350

OK,大概就這樣了,畢竟我對二進位逆向實在不太在行。

這次仰仗 ChatGPT 和 IDA 的犀利,才能順利完成這項工作。

不知日後的漢化會遇見何種糟糕的問題呢?

現在已經實現了初步的成果,但從 1 到 100,從來不是一件容易的事情。


小敘

本不打算寫這篇總結,畢竟假以外物,通篇皆為『之乎者也』,令人不知所云。

整個逆向過程相當之無趣:靜態分析 → 偵錯 → 靜態分析 → 偵錯…… 機械到令人髮指。

作為一個二進位逆向的菜鳥,沒有比逆向過程不受自我掌控更可怕的事情……

不過我更擔憂的是,玩家手裡,只有一個成品;而 AI 最終產出的方法論,淪為『黑箱』。

如果後人想要復現,該如何開始?

這便是本 POST 整理兼發佈的目的。