帶著 AI 把 2012 年的無原始碼老遊戲從零救活:多模型逆向除錯與本機 v1.0.0 優化實錄
每個人心裡都有一款想在自己電腦上重溫的童年老遊戲。當我們在封閉本機環境測試一套 2012 年、完全沒有原始碼的 3D MMORPG 雙端執行檔時,放上 2026 年的環境卻是一開機就全毀。這篇記錄我們如何搭配低阻攔模型一路拆解 x86 組語、封包雜湊與 Linux 服務端,完成本機 v1.0.0 原版優化基準。
這篇的段落(4)
[!IMPORTANT] 研究邊界與安全聲明 本文記錄之逆向分析與相容性調校,全程僅於個人封閉本機環境(
127.0.0.1)進行老舊軟體數位保存(Digital Preservation)與舊系統(Legacy System)相容性研究。文中已隱去特定商業作品名稱與網路位址;本專案不公開、不散播、亦不提供任何受著作權保護之執行檔、封包或資料庫下載,且絕不涉及任何私服架設或商業營運。文內提及之「v1.0.0 基準版」純屬個人本機相容性研究的封存里程碑。
每個人心裡大概都有一款十幾年前的童年神作。對我來說,那是一款 2012 年的經典 3D MMORPG:一直很想在自己的電腦上單機跑起來,偶爾上去站在主城聽聽背景音樂、牽著羊駝坐騎散步,重溫當年的感動。
但當我們在封閉本機環境裡,把 2012 年那批老檔案(1 支 32-bit C++ 客戶端 game_.exe、6 支靜態編譯的舊 Linux 服務端、4 座舊 PostgreSQL 資料庫)搬到 2026 年的 Windows 11 與現代容器上時,四個大坑同時炸開:
- Linux 服務端一啟動就秒崩(Segfault):十幾年前靜態編譯的執行檔,一碰到現代容器的名稱解析(
NSS / glibc)當場暴斃。 - 連進去了卻永遠卡在「載入角色列表」:
LoginServer讀出的資料庫還殘留著十幾年前寫死的舊公網 IP,而且單機同 IP(127.0.0.1)下WorldServer與ZoneServer的內外連接埠還會自己打架。 - 開遊戲切個視窗(
Alt+Tab)就閃退:每次啟動得先看 67 秒片頭名單、再卡 12 秒載入 141MB 動作檔;這期間只要滑鼠點到別的視窗,立刻跳出CEGUISingleton.h Line: 79 ms_Singleton空指標崩潰。 - 進遊戲後滿地亂碼、防外掛驗證碼踢人、硬碟還被狂刷:資料庫裡 87,325 筆中文因為當年
LATIN1 / BIG5雙重錯置全變天書;單機沒有外部圖片伺服器,走兩步跳出驗證碼(Captcha)就把人踢下線;6 支服務端在背景每秒狂刷幾千次EVAL()計時日誌與fflush;舊版點陣字在現代高解析度螢幕上更是糊到眼睛痛。
手邊沒有任何一行 C++ 原始碼,只有編譯好的二進位執行檔與加密封包。幾天後,我們帶著 AI 不只把這座 14 年前的老系統在本機救活,還完成了個人保存的 「v1.0.0 原版優化基準版」。
為什麼拿一般 AI 修無原始碼的老系統,第一步就會卡死?
因為只要提到「反組譯執行檔、檢視 ELF binary 位移、略過離線驗證碼或解析封包格式」,一般護欄過敏的模型就會開始跳針說教,拒絕往下分析。
很多人試過帶 AI 做底層除錯或逆向工程(Reverse Engineering),最挫折的地方往往卡在護欄:你明明只是在本機救一套十幾年前的孤兒系統,才剛把 objdump 的輸出貼上去,它就回你一段道德勸說:「我無法協助逆向或修改執行檔。」在沒有原始碼的老系統除錯裡,這種無腦攔截等於直接宣判死刑。
要打穿這道牆,做法其實很直接:透過我們開源的 model-relay 外掛,在同一個 Claude Code 工作台裡混搭主力模型與「阻攔低、不無腦說教」的模型(例如中國開源模型 DeepSeek、GLM,或是 Gemini / Argon 等願意直接啃組合語言與 Binary byte offset 的模型):
當 AI 不再跟你打官腔,願意直接把 objdump 與 Ghidra 等級的 x86 / x64 組合語言攤開來一行一行追記憶體位址,整個本機救援進度就像開了倍速一樣。
沒有一行原始碼,AI 怎麼讓 14 年前的老遊戲「3 秒瞬開、畫質升級、硬碟零寫入」?
靠的是直接追進客戶端訊息迴圈、推導 64-bit 虛擬檔案系統(VFS)的雜湊演算法,並對 6 支 Linux 服務端與資料庫動微創手術。
在主線模型統籌、低阻攔模型動刀的配合下,我們一路解開了三個原本看起來無解的陳年頑疾:
1. 抓出 2012 年的 Race Condition:主程式 0 byte 修改,啟動從 79 秒縮成 3 秒
以前開遊戲最痛苦的就是漫長等待,而且載入期間只要不小心按到 Alt+Tab 切視窗就會當場崩潰。AI 直接追進 game_.exe 的 Windows 訊息迴圈(0x6790c0 -> 0x78a520),一眼抓出當年留下的競態條件(Race Condition):客戶端在啟動時同步預載 141MB 的動作檔(.kfm),這 12 秒內 UI 系統(CEGUI::System)根本還沒初始化完成,但只要視窗失焦觸發 WM_ACTIVATEAPP,程式就會直接衝進去呼叫 CEGUI::System::getSingleton(),當場撞上空指標。
找到根因後,我們甚至連 game_.exe 主程式都不用改(0 byte 修改,完整保留原始數位簽章與 4GB LAA 大記憶體支援)。只要在 client.ini 關掉同步動作預載(PrecacheKFM=0),並略過每次啟動必播 67 秒的片頭人員名單,啟動時間直接從「67 秒 + 12 秒」縮短成 3 秒瞬開進登入畫面,怎麼切視窗都不會再閃退。
2. 解開設定檔混淆與 64-bit VFS 雜湊:把 8.7 萬筆亂碼與高清向量字體一次救回
解決了閃退,下一個難關是畫面上的天書與鋸齒字。我們先逆向解開了服務端 .ini 的 <encode> 雙層混淆(XOR 0x5A + 4-bit 旋轉 + 變長標頭),把雙端 319 張資料表全部對齊;再用腳本把 PostgreSQL 資料庫裡因 LATIN1 / BIG5 雙重錯置而毀掉的 87,325 筆資料無損還原回繁體中文,並補齊 15 個破損的 UI layout,讓 CEGUI.log 上百筆報錯直接歸零(0 Error)。
接著是字體升級。2012 年的舊字型內嵌了 1-bit 點陣表(EBDT / EBLC),在現代高解析度螢幕上鋸齒感極重。為了釐清同系引擎在 64-bit 架構下的無檔名封包(.nfs)索引機制,AI 針對引擎讀檔函式(0x140B7E290 與 0x140B7EA50)逆向推導出它專屬的 FNV-1 64-bit 有號位元組雙段雜湊演算法,從 21.8 萬個封包節點裡把 90 張資料表的真實路徑 100% 還原出來,並將拔除舊版 1-bit 點陣表的 61,190 字全向量高清字型 FONT01.TTF 掛回 2012 客戶端。一進主城,字體瞬間清晰得像現代重製版。
3. 對 6 支 Linux 服務端動 3-byte 微創手術:待機硬碟寫入降到 0 B/s、登入查詢快 149 倍
最後是背景那 6 支老 Linux 服務端。為了讓本機單機環境徹底順暢,我們在測試容器的執行檔與資料庫動了三刀:
| 原本卡死或拖慢的病灶 | 底層逆向找出的真因 | 微創手術與實測結果 |
|---|---|---|
| 走兩步就跳驗證碼把人踢下線 | 單機環境沒有外部驗證碼圖片伺服器,WorldServer 與 ZoneServer 逾時即斷線 | 定位 CCaptcha::IsA 與 SendCaptchaTest,用 3 個 byte(xor eax,eax; ret / ret)在單機環境停用 |
| 伺服器待機時硬碟狂刷不停 | 6 支服務端的 CPerfTimer::Elapsed 每秒狂刷幾千次 EVAL() 日誌與 fflush,加上 WSL2 微秒時鐘偶發倒退 | 將計時函式 patch 為回傳 0.0(fldz; ret)並夾制 ICurrentTime::Update,待機硬碟寫入降至 0.0 B/s |
| 角色登入與讀福袋明顯卡頓 | 當年資料庫漏建 13 個高頻外鍵的 B-Tree 索引,每次登入讀 fortune_bag 都觸發全表掃描 | 補齊 13 組索引,查詢耗時從 16.86 ms 降至 0.113 ms(加速 149 倍),且維持 synchronous_commit = on 斷電零掉檔 |
為什麼在封存「本機 v1.0.0 原版優化基準」前夕,AI 推翻了自己昨天的修改?
因為我們要求把所有改過的檔案與原始備份(.bak)做逐位元組(byte-by-byte)對帳,結果 AI 反組譯服務端後發現:前一天為了消掉客戶端報錯而改的一個字,反而會把原版 81 等任務的傳送座標蓋掉。
這整場救援裡最讓我們起雞皮疙瘩的一幕,發生在封版前夕的自我推翻。
在準備封存第一個完整里程碑時,我們定下了一條鐵則:這個本機里程碑叫做 「v1.0.0 原版優化基準版(Original Optimized Baseline)」。意思是單機重溫時必須維持 100% 原汁原味的 2012 年原版體驗(不改經驗倍率、不改掉寶、不改技能數值、不塞任何魔改內容,連新手創角的 8 支開場劇情動畫 Opening*.ogv 都完整保留),只讓它比當年更穩、更快、更清晰。
為了確保沒有誤傷任何原版邏輯,我們請 AI 把所有修改過的檔案跟原始 .bak 備份逐位元組比對。比對到任務腳本時,跳出了一個我們前一天剛修好的地方。
前一天我們在清查客戶端 error.log 時,看到系統報錯說 81 等任務(ID 3435)裡有一條指令 AcceptSRA 1 11 不存在。當時看同一行前面緊鄰著一個 AcceptTPA 1 51(傳送指令),我們跟 AI 都合理懷疑:S 跟 T 都在鍵盤左手邊,這八成是 14 年前企劃把 AcceptTPA 打錯成 AcceptSRA 了吧?於是昨天順手把它改成了 AcceptTPA 1 11,客戶端報錯也真的消失了。
但在 v1.0.0 封版審查時,AI 沒有放過這條差異。它主動把服務端 ZoneServer 反組譯出來核對,當場推翻了昨天的修改:
看到 AI 貼出 ZoneServer 位址 0x08488ce0 的反組譯結果時,真的會忍不住讚嘆:
原來在服務端裡,真的有一個獨立函式叫做 MC_AcceptSRA!它是用來設定角色重生區域(寫入任務結構體偏移 +0x04),而前面的 MC_AcceptTPA 是用來設定傳送目標(寫入結構體偏移 +0x1f8)。14 年前企劃寫下 AcceptTPA 1 51 再寫 AcceptSRA 1 11,是為了「把玩家傳送到 51 區,同時把重生點設在 11 區」;客戶端會報錯,純粹是因為客戶端的字串預覽表當年漏註冊了這個純伺服器端指令。如果照我們前一天的改法把 AcceptSRA 改成 AcceptTPA,表面上客戶端 error.log 變乾淨了,實際上卻會在伺服器裡把剛剛設好的 51 區傳送座標直接覆蓋成 11 區!
當下我們立刻把這行腳本還原回原版,正式完成本機 v1.0.0 原版優化基準版 的封存。
帶 AI 啃無原始碼的舊系統時,怎麼複製這套工作流?
回頭看這次在本機從零救活 2012 年老遊戲並完成 v1.0.0 基準版的過程,如果你手邊也有沒有原始碼的企業舊系統(Legacy Binary)、老舊工具或底層效能瓶頸想找 AI 一起攻克,動手前可以先檢查這三件事:
- 你的工具鏈有沒有被單一模型的護欄綁死?
遇到需要看
objdump反組譯、分析封包格式或修補二進位相容性的合法研究任務,先用model-relay把 DeepSeek、GLM 或 Gemini 等低阻攔模型接進 Claude Code。讓主線模型管架構與驗收、派低阻攔模型直接進場拆組語,就不會把時間浪費在跟護欄吵架。 - 修掉一行報錯前,有沒有對過「另一端」的真實行為?
無論是修老系統還是修前後端服務,不要只因為客戶端
error.log不再報錯就以為修好了。要求 AI 拿執行檔或接收端的實際程式碼對帳,確認那個欄位寫進哪個偏移、會不會蓋掉既有狀態。 - 封存前有沒有守住「與
.bak逐位元組對帳」的基準線? 先把第一版鎖定在「100% 不改業務與核心數值的原版優化基準」,每一處二進位或設定修改都必須說得出對應的根因位址,才不會在優化過程中把原作者的細節一起洗掉。
如果你想把這套「單視窗秒切各家模型 + 直接指派低阻攔子代理」的工作台裝進自己的終端機,可以直接看上一篇〈想在 Claude Code 混用 Gemini 與 GLM,為什麼一改設定檔就全亂套?〉,或參考〈AI 幫你寫的測試全綠,為什麼上線還是壞掉?〉看看我們怎麼設計不會被表面綠燈騙過去的驗收防線;想盤點自己手上的 AI 除錯與驗證流程卡在哪一層,也可以先做免費的 AI 工作流五層健檢。
