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,動態修改這些寫死的座標和解析度數值,讓遊戲能在更高解析度下正確顯示。這需要:

  1. 找到儲存解析度的記憶體位址
  2. 找到所有 UI 元素的座標計算邏輯
  3. 把這些數值根據新解析度重新計算並寫入

這是整個課程 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 / IWzNameSpaceWZ 資源管理(控制從哪裡讀取遊戲資源)

小結

概念重點
v83 版本2007 年版本,私服最流行,保護機制相對簡單
啟動流程exe → 載入 DLL → 初始化 → 連線 → 遊戲主迴圈
Themida 保護把程式碼變成暗語,不能直接修改 exe
WZ 資源系統遊戲資源的容器,像有層級的壓縮包
x86 (32-bit)記憶體上限 2GB,DLL 必須也是 32-bit 編譯
800×600 問題UI 座標寫死,需要 hook 才能修正解析度
dinput8.dll 介入點遊戲初始化之前,DllMain 被呼叫的那一刻

延伸閱讀