MapleStory v83 客戶端架構概覽
本篇定位
這是 Phase 1 的第二篇筆記。閱讀本篇之前,建議先看 01_什麼是DLL與DLL劫持,了解 DLL 的基本概念。本篇帶你認識「遊戲本體」是由哪些零件組成的,以及 MapleEzorsia 為什麼能介入其中。
一、v83 是哪個版本?為什麼這麼流行?
MapleStory v83 大約是 2007 年 發布的版本。以現代眼光來看,它已經是非常古老的版本——畫面解析度只有 800×600,介面也非常簡單。
但在私服社群,v83 卻是目前最受歡迎的版本,原因有三:
| 原因 | 說明 |
|---|---|
| 懷舊感 | 許多玩家的童年回憶,技能系統簡單、節奏輕鬆 |
| 資源豐富 | 十幾年來社群累積了大量教學、工具、伺服器原始碼 |
| 技術門檻低 | 相比後期版本,v83 的保護機制較少,適合學習研究 |
私服為什麼選 v83?
後期版本(v100 以後)加入了大量反外掛保護與加密,逆向工程難度大幅上升。v83 剛好在「夠複雜、值得學習」與「還能被研究」之間取得平衡,因此成為私服社群的標準基底。
二、遊戲啟動流程
當你按下 MapleStory.exe 的那一刻,發生了以下一連串的事:
flowchart TD A["使用者雙擊\nMapleStory.exe"] --> B["Windows 載入程式\n(Loader)"] B --> C["載入相依 DLL\ndinput8.dll\nnmconew.dll\nijl15.dll"] C --> D["MapleEzorsia 的\ndinput8.dll 被載入\n🔑 介入點在這裡"] D --> E["MapleEzorsia 設定好\n所有修改與 Hook"] E --> F["遊戲初始化\nCWvsApp::SetUp()"] F --> G["載入 WZ 資源檔\nIWzFileSystem"] G --> H["連線到登入伺服器\nCClientSocket::Connect()"] H --> I["遊戲主迴圈開始\nCWvsApp::Run()"] I --> J["🎮 玩家看到登入畫面"]
各階段說明
1. Windows 載入程式
Windows 在執行任何 .exe 之前,會先查看這個程式「需要哪些 DLL」。就像食譜上列的食材,Windows 會先把食材備齊。
2. 載入相依 DLL MapleStory.exe 本身需要幾個 DLL 才能運作。Windows 會按照固定的搜尋順序去找這些 DLL 檔案,優先從遊戲所在的資料夾開始找。
3. MapleEzorsia 的介入點
這就是關鍵:把我們自製的 dinput8.dll 放進遊戲資料夾,Windows 就會載入「我們的版本」而不是 Windows 系統內建的版本。我們的 DLL 會趁這個機會安裝各種修改。
4. 遊戲初始化 → 連線 → 遊戲主迴圈 修改安裝完畢後,遊戲正常繼續啟動。玩家完全不知道中間發生了什麼。
三、MapleStory.exe 的保護:Themida
不能直接修改 exe!
MapleStory.exe 受到 Themida 保護。如果你用十六進位編輯器打開它,你看到的不是真正的程式碼,而是一堆亂碼。Themida 會在執行時才把真正的程式碼解密,讓分析和修改變得極度困難。
Themida 是什麼?
Themida 是一種虛擬化保護工具。它的原理可以比喻成這樣:
想像你有一本食譜(程式碼),但你不想讓別人抄走。你把食譜翻譯成只有你自己才看得懂的「暗語」,並且隨書附上一本「暗語字典」。只有同時擁有食譜和字典,才能做出料理。
Themida 做的就是這件事:
- 把
MapleStory.exe裡的原始程式碼,變成只有它自己的虛擬機器才能執行的「暗語指令」 - 程式執行時,Themida 的虛擬機器即時翻譯這些暗語
- 沒有虛擬機器,你看到的只是一堆無意義的位元組
所以要怎麼辦?
不動 exe,改用 DLL 注入。 等遊戲自己解密、跑起來之後,我們的 DLL 再從記憶體的角度去修改它的行為。這正是 MapleEzorsia 的核心思路——完全不碰 exe 本體,只透過 DLL 劫持在執行期間介入。
四、WZ 資源系統
MapleStory 的所有遊戲資源(圖片、聲音、地圖資料等)都打包在 .wz 檔案裡。
WZ 是什麼?
WZ 檔就像是一個分類整理好的「壓縮資料夾」。裡面的資料有層級結構,你可以用 HaRepacker 或 WzLib 等工具打開來查看內容。MapleStory 啟動時會透過
IWzFileSystem這個系統來讀取這些檔案。
主要的 WZ 檔案
| 檔案 | 內容 |
|---|---|
Map.wz | 地圖資料、怪物位置、傳送點 |
UI.wz | 介面元素(血條、地圖顯示、登入畫面) |
Sound.wz | 背景音樂與音效 |
Character.wz | 角色外觀、動作動畫 |
Mob.wz | 怪物外觀與屬性資料 |
Item.wz | 道具圖示與說明 |
Skill.wz | 技能圖示與特效 |
String.wz | 遊戲內所有文字(道具名稱、說明文) |
WZ 與登入器的關係
MapleEzorsia 的 Phase 7 會深入處理 WZ 資源。有時候伺服器自訂了一些內容(新地圖、新道具),就需要在 WZ 層面做修改,或是讓遊戲從 .img 檔案直接讀取,繞過 WZ 的限制。
五、x86(32-bit)意味著什麼?
MapleStory v83 是一個 32-bit 應用程式,也就是俗稱的 x86。這對我們的開發工作有幾個直接影響:
記憶體限制
2 GB 的天花板
32-bit 程式最多只能使用 2 GB 的記憶體(理論上限是 4 GB,但 Windows 保留另一半給系統使用)。現代遊戲動輒需要 8 GB,但 v83 的時代還不需要擔心這個問題。
工具鏈必須匹配
我們寫的 DLL 也必須編譯成 32-bit,否則 Windows 根本無法把它載入進 32-bit 的遊戲程序。就像 USB-A 插頭不能插進 USB-C 的孔一樣,位元數不匹配就無法運作。
在 Visual Studio 裡,這代表你必須把編譯目標設定為 x86,而不是預設的 x64。這是新手最常犯的錯誤之一。
記憶體位址的格式
x86 的記憶體位址是 4 個位元組(32-bit),格式如 0x00ABCDEF。你在 MapleEzorsia 原始碼裡看到的所有地址(例如 CWvsApp::SetUp 的位置)都是這種格式。
六、原始解析度問題:800×600 在現代螢幕
v83 的遊戲視窗固定是 800×600 像素(4:3 比例)。
現代螢幕的問題
現代螢幕普遍是 1920×1080(16:9)或更高解析度。把一個 800×600 的視窗放到這樣的螢幕上,有兩種結果:
- 視窗模式:視窗很小,在大螢幕上顯得格格不入
- 全螢幕模式:畫面被強制拉伸,比例失真、模糊,或是四周出現黑邊
這不只是視覺問題。遊戲內的 UI 元素座標是寫死(hardcode)在程式碼裡的,以 800×600 為基準設計。如果你只是拉大視窗,UI 不會跟著移動,血條還是在左上角、小地圖還是在右上角——但比例全跑掉了。
MapleEzorsia 解決了什麼?
MapleEzorsia 的核心功能之一,就是在遊戲啟動時透過 hook,動態修改這些寫死的座標和解析度數值,讓遊戲能在更高解析度下正確顯示。這需要:
- 找到儲存解析度的記憶體位址
- 找到所有 UI 元素的座標計算邏輯
- 把這些數值根據新解析度重新計算並寫入
這是整個課程 Phase 5 的核心主題。
七、MapleEzorsia 的介入點:精確到哪個環節?
把前面的啟動流程放大來看,MapleEzorsia 的實際介入位置在:
Windows 載入 dinput8.dll
↓
DllMain() 被呼叫 ← MapleEzorsia 程式碼從這裡開始執行
↓
安裝所有 Hook
(CWvsApp、CClientSocket、CSecurityClient 等)
↓
讓真正的 dinput8.dll 繼續工作(代理)
↓
遊戲初始化 CWvsApp::SetUp() ← Hook 在這裡攔截,套用修改
為什麼是 dinput8.dll 而不是其他 DLL?
dinput8.dll 是 Windows DirectInput 函式庫,負責鍵盤和搖桿輸入。MapleStory 確定會在啟動時載入它,而且它的名字固定、不會變動,是一個非常穩定的「下手點」。相比之下,如果選擇其他 DLL,可能在不同版本之間名稱不一樣,反而麻煩。
MapleEzorsia Hook 的主要目標類別
從 dllmain.cpp 的程式碼可以看出,MapleEzorsia 主要 hook 了以下遊戲類別:
| 類別 | 功能 |
|---|---|
CWvsApp | 遊戲應用程式主體(初始化、主迴圈、連線登入) |
CClientSocket | 網路連線(控制連到哪個伺服器) |
CSecurityClient | 安全模組(與 HackShield 相關,私服需要繞過) |
IWzFileSystem / IWzNameSpace | WZ 資源管理(控制從哪裡讀取遊戲資源) |
小結
| 概念 | 重點 |
|---|---|
| v83 版本 | 2007 年版本,私服最流行,保護機制相對簡單 |
| 啟動流程 | exe → 載入 DLL → 初始化 → 連線 → 遊戲主迴圈 |
| Themida 保護 | 把程式碼變成暗語,不能直接修改 exe |
| WZ 資源系統 | 遊戲資源的容器,像有層級的壓縮包 |
| x86 (32-bit) | 記憶體上限 2GB,DLL 必須也是 32-bit 編譯 |
| 800×600 問題 | UI 座標寫死,需要 hook 才能修正解析度 |
| dinput8.dll 介入點 | 遊戲初始化之前,DllMain 被呼叫的那一刻 |
延伸閱讀
- 01_什麼是DLL與DLL劫持 — 上一篇,理解 DLL 劫持的原理
- 03_dinput8代理DLL原理 — 下一篇,深入 dinput8.cpp 如何同時代理真實函式庫並安裝修改
- 04_開發環境建立(Visual_Studio_x86) — 如何設定 Visual Studio 編譯 x86 DLL
- 03_WZ與IMG資源整合原理 — WZ 資源系統的實務操作
- 02_解析度修改原理與實作 — Phase 5 深入解析解析度 hook 的實作細節