這是「登入器原始碼開發」系列的第一篇。這條路線以開源專案 MapleEzorsia-v2(GMS v83 私服登入器,用 DLL 劫持技術連線)的原始碼為教材,帶你從零理解一個登入器是怎麼做出來的。跟著這系列只需要看得懂基本 C++ 語法,不需要精通——每個概念都會先用生活化比喻說明,再逐行解讀程式碼。

什麼是 DLL

DLL(動態連結程式庫)是 Windows 下的「功能包」:程式不用把所有功能自己寫進去,執行時可以隨用隨取。就像手機 App 呼叫系統相機功能,而不必自己寫一套相機程式。這樣做能省記憶體(多個程式共用同一份 DLL)、方便模組化更新(修好一個DLL,所有依賴的程式都跟著修好)。

v83 客戶端啟動時會跟 Windows 要一批 DLL,包括 dinput8.dll(讀取鍵盤滑鼠輸入)、d3d8.dll(3D 圖形渲染)等。其中 dinput8.dll 就是 MapleEzorsia-v2 動手的目標。

DLL 劫持:Windows 的「先到先得」漏洞

當程式要載入某個 DLL,Windows 會按固定順序搜尋:先找程式自己所在的資料夾,再找系統資料夾(System32)。而且找到第一個符合名稱的就立刻載入,不會繼續往後找

這就是 DLL 劫持的核心:只要在遊戲資料夾裡放一個自己做的 dinput8.dll,Windows 就會載入「假的」版本,而不是系統內建的真版本。就像快遞員只認名字不認身份證——有人自稱王先生簽收,包裹就交出去了。

DLL 搜尋順序劫持在資安領域常被惡意軟體利用。這裡的前提是在自己的私服環境對自己的客戶端做修改,屬於合法的技術研究,不應用於未經授權的軟體。

MapleEzorsia-v2 用的是進一步的 Proxy DLL(代理劫持):假 DLL 執行完自己的初始化程式碼後,把所有呼叫原封不動轉發給真正的系統 DLL,遊戲完全感覺不到差異。

text
📋 複製
MapleStory 啟動
   ↓ 載入遊戲資料夾的 dinput8.dll(假的)
MapleEzorsia 執行初始化(HD解析度、localhost連線設定…)
   ↓ 把真正的 DirectInput 呼叫轉發給 System32 的真 dinput8.dll
鍵盤輸入正常運作,遊戲繼續跑

這麼做的好處:玩家不需要額外的啟動器 EXE,直接雙擊 MapleStory.exe 就好;不需要外部工具手動把程式碼「塞進」遊戲程序(容易被防毒誤判);體驗跟原版遊戲一模一樣。

為什麼選 dinput8.dll?

條件dinput8.dll 是否符合
遊戲一定會載入它✅ 需要讀取鍵盤輸入,啟動早期就會載入
原本不在遊戲資料夾✅ 住在System32,遊戲資料夾沒有,我們才能放假的
匯出函式數量少✅ 只需假裝2個函式:DirectInput8CreateGetdfDIJoystick
不容易被防毒誤報✅(相對其他常見注入目標更低調)

Proxy DLL 怎麼寫:CreateHook() 逐行解析

假 DLL 必須匯出跟真 DLL 完全相同的函式名稱,否則遊戲會直接報錯關閉。核心初始化程式碼:

cpp
📋 複製
void dinput8::CreateHook() {
    char szPath[MAX_PATH];
    // 問Windows系統目錄在哪裡(通常是 C:\Windows\System32)
    GetSystemDirectoryA(szPath, sizeof(szPath));
    // 拼接成完整路徑:C:\Windows\System32\dinput8.dll
    strcat(szPath, "\\dinput8.dll");
    // 把真正的系統DLL載入進記憶體
    HMODULE hModule = LoadLibraryA(szPath);
    // 查出真正函式的記憶體位址,存起來備用
    DirectInput8Create_Proc = GetProcAddress(hModule, "DirectInput8Create");
    GetdfDIJoystick_Proc    = GetProcAddress(hModule, "GetdfDIJoystick");
}

// 遊戲呼叫我們的假函式時,直接跳轉到真函式
extern "C" __declspec(dllexport) __declspec(naked) void DirectInput8Create() {
    __asm jmp dword ptr[DirectInput8Create_Proc]
}

三個步驟:問路GetSystemDirectoryA 找系統目錄)→ 開門LoadLibraryA 載入真DLL)→ 記地址GetProcAddress 存下真函式位置)。之後遊戲呼叫假函式時,用 __asm jmp 直接跳到真函式的位址執行——naked(裸函式)代表編譯器不會加任何額外的準備/收尾程式碼,避免堆疊亂掉導致遊戲崩潰。真正的注入邏輯(Hook遊戲行為)發生在同一個DLL裡的其他程式碼,dinput8.cpp 只負責當那扇「不會被發現的門」。

v83 客戶端架構:為什麼能被 DLL 介入

v83 大約是 2007 年的版本,在私服社群最受歡迎,原因是資源豐富(十幾年累積的教學與工具)、保護機制相對簡單(後期版本加了大量反外掛保護,逆向難度大增)。

啟動流程:雙擊 exe → Windows 載入相依 DLL(含 dinput8.dll,介入點在這裡)→ 遊戲初始化 CWvsApp::SetUp() → 載入 WZ 資源 → 連線登入伺服器 → 遊戲主迴圈開始。

MapleStory.exe 本身受 Themida 虛擬化保護,直接用十六進位編輯器打開只會看到亂碼,執行時才會即時解密——所以不能直接改 exe,只能透過 DLL 在執行期間、遊戲自己解密跑起來之後介入。這正是整條技術路線的核心思路。

v83 是 32-bit(x86) 程式,這代表我們寫的 DLL 也必須編譯成 32-bit,Visual Studio 裡要把編譯目標設為 x86 而非預設的 x64——這是新手最常犯的錯誤之一。

MapleEzorsia-v2 主要 Hook 的遊戲類別:CWvsApp(應用程式主體、初始化、連線)、CClientSocket(網路連線)、CSecurityClient(安全模組)、IWzFileSystem(WZ資源管理)。這些會在後面幾篇陸續深入。

建置開發環境:Visual Studio x86

1. 安裝 Visual Studio Community(免費)

visualstudio.microsoft.com 下載 Community 版本,建議選 Visual Studio 2022

安裝時的 Workload 畫面,務必勾選:

  • 使用 C++ 的桌面開發(Desktop development with C++)
  • 確認右側自動勾選了 MSVC v143 - VS2022 C++ x86/x64 build toolsWindows SDK

沒選對 Workload,之後開啟專案會出現「找不到編譯器」的錯誤。

2. 取得並開啟專案

從 GitHub 下載 ZIP 或 git clone,建議放在沒有中文或空格的路徑(例如 C:\Projects\MapleEzorsia-v2\)。雙擊 .sln 檔案用 Visual Studio 開啟。

3. 確認編譯設定:Platform 必須是 x86

Visual Studio 工具列頂端有兩個下拉選單:[Debug ▼] [x86 ▼]Platform 一定要選 x86,不是 x64——如果下拉選單裡沒有 x86 選項,代表安裝時沒選到正確的 build tools,需要重新執行 Visual Studio Installer 補裝。一般實際使用選 Release 組態(Debug版注入遊戲後行為可能不同)。

4. 第一次編譯

確認 Platform=x86、Configuration=Release 後,按 Ctrl+Shift+B(Build Solution)。輸出視窗最後出現:

text
📋 複製
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

編譯好的 DLL 會在 專案根目錄\Release\ezorsia.dll,這就是之後要放進 MapleStory 客戶端資料夾的主角。

常見編譯錯誤:找不到 detours.lib

text
📋 複製
LINK : fatal error LNK1181: cannot open input file 'detours\detours.lib'

代表 Microsoft Detours 函式庫(用來攔截遊戲函式呼叫的核心工具)沒放對位置。確認專案資料夾內有 detours\ 子資料夾,且包含 detours.libsyelog.libdetours.h。如果沒有,通常是只複製了 .cpp 原始碼,需要從 GitHub 重新完整下載整個專案。


下一篇進入專案結構本身:認識 ezorsia 專案裡每個檔案的角色,並編譯出第一個能實際運作的 DLL。