什麼是 DLL 與 DLL 劫持
什麼是 DLL
想像你在一間大餐廳工作。廚房裡有一本「食譜手冊」,任何廚師只要需要做某道菜,就去翻那本手冊,不需要每個人自己記下所有食譜。
DLL(Dynamic Link Library,動態連結程式庫)就是這本食譜手冊。
它是一個存放「功能程式碼」的檔案,副檔名是 .dll。某個程式需要某項功能時,就去「呼叫」DLL 裡面對應的函式,完成工作後繼續跑自己的程式。
DLL 白話定義
DLL 是 Windows 下的「功能包」。程式不需要把所有功能都自己寫進去,可以在執行時隨用隨取,就像手機 App 可以呼叫系統相機功能,而不必自己寫一套相機程式。
常見例子:
kernel32.dll— 提供開檔、關檔、記憶體操作等基本功能user32.dll— 提供視窗、按鈕、對話框等 UI 功能dinput8.dll— 提供鍵盤、搖桿等輸入裝置的讀取功能(DirectInput 8)
Windows 為什麼要有 DLL
節省記憶體
假設你電腦同時開著 10 個程式,每個程式都需要「顯示視窗」這個功能。如果每個程式都把這段程式碼自己帶一份,記憶體就要存 10 份一模一樣的東西。
有了 DLL,這 10 個程式共用同一份 user32.dll,記憶體只需要載入一次。
模組化更新
如果微軟發現 user32.dll 裡有安全漏洞,只要更新那一個 DLL 檔,所有依賴它的程式就都自動修好了,不需要每個程式重新發布更新。
方便分工開發
大型程式可以把不同功能切成不同 DLL,由不同團隊開發,最後組合在一起,就像積木一樣。
類比總結
DLL 就像城市的「共用基礎建設」——電線桿、自來水管。每棟房子不需要自己架一套,直接接上就能用,既省空間又方便維護。
MapleStory 用到哪些 DLL
MapleStory v83 客戶端在啟動時,會向 Windows 索取許多 DLL,包括:
| DLL 名稱 | 功能 |
|---|---|
dinput8.dll | DirectInput 8,負責讀取鍵盤、滑鼠、搖桿輸入 |
d3d8.dll | Direct3D 8,負責 3D 圖形渲染 |
kernel32.dll | 核心系統功能(檔案、記憶體、程序) |
user32.dll | 視窗與使用者介面 |
其中 dinput8.dll 是 MapleEzorsia-v2 劫持的目標,原因很簡單:MapleStory 一啟動就會去載入它,時機點非常早,適合用來在遊戲初始化前插入我們自己的程式碼。
DirectInput 是什麼?
DirectInput 是微軟 DirectX 套件的一部分,專門處理玩家的輸入裝置。你按下鍵盤上的「↑」,MapleStory 能知道角色要往上走,就是透過 dinput8.dll 在幕後幫忙讀取的。
什麼是 DLL 劫持(DLL Hijacking)
Windows 的 DLL 搜尋順序
當一個程式說「我要載入 dinput8.dll」,Windows 不會直接去系統資料夾找,而是按照固定順序搜尋:
- 程式本身所在的資料夾(例如
C:\Maplestory\) - 系統資料夾(
C:\Windows\System32\) - Windows 資料夾(
C:\Windows\) - 其他 PATH 環境變數指定的資料夾
關鍵重點
Windows 找到第一個符合名稱的 DLL 就立刻載入,不會繼續往後找。這個「先到先得」的機制,就是 DLL 劫持的核心漏洞。
劫持概念
既然 Windows 優先搜尋程式自己的資料夾,那我只要:
- 在 MapleStory 的遊戲資料夾裡,放一個我自己做的
dinput8.dll - MapleStory 啟動,說「我要 dinput8.dll」
- Windows 搜尋:「欸,遊戲資料夾就有了!」——載入的是我的版本
- 我的 DLL 執行我想要的初始化程式碼
這就叫做 DLL Search Order Hijacking(DLL 搜尋順序劫持)。
生活化比喻
你訂了一份快遞,快遞員說「找王先生簽收」。結果大樓門口有個人自稱王先生,快遞員就把包裹交給他了,根本沒有去確認是否是本人。
DLL 劫持就是這樣——程式只認名字,不認身份證。
MapleEzorsia 怎麼用 DLL 劫持
MapleEzorsia-v2 實作的是更進一步的技術:DLL Proxy Hijacking(代理劫持)。
流程示意
MapleStory 啟動
↓
載入遊戲資料夾的 dinput8.dll(假的,MapleEzorsia 的版本)
↓
MapleEzorsia 執行初始化:
- 啟用 HD 解析度支援
- 設定 localhost 伺服器連線
↓
MapleEzorsia 把真正的 DirectInput 呼叫轉發給
C:\Windows\System32\dinput8.dll(真的系統版本)
↓
鍵盤輸入正常運作,遊戲繼續跑
核心程式碼解讀
MapleEzorsia 的 dinput8.cpp 裡有這樣的邏輯:
void dinput8::CreateHook() {
char szPath[MAX_PATH];
// 1. 取得系統目錄路徑(C:\Windows\System32)
GetSystemDirectoryA(szPath, sizeof(szPath));
// 2. 拼接成完整路徑:C:\Windows\System32\dinput8.dll
strcat(szPath, "\\dinput8.dll");
// 3. 載入真正的系統 DLL
HMODULE hModule = LoadLibraryA(szPath);
// 4. 取得真正函式的位置,存起來備用
DirectInput8Create_Proc = GetProcAddress(hModule, "DirectInput8Create");
GetdfDIJoystick_Proc = GetProcAddress(hModule, "GetdfDIJoystick");
}
// 當遊戲呼叫 DirectInput8Create,直接跳到真正的函式
extern "C" __declspec(dllexport) __declspec(naked) void DirectInput8Create() {
__asm jmp dword ptr[DirectInput8Create_Proc]
}讀懂這段程式碼的白話版
GetSystemDirectoryA— 問 Windows「系統資料夾在哪裡?」LoadLibraryA— 親自去載入真正的dinput8.dll,把位置記下來GetProcAddress— 從真正的 DLL 裡找到真正的函式,把「電話號碼」記下來__asm jmp— 當遊戲打來電話,我直接幫它轉接到真正的函式,遊戲感覺不到任何差異
這就是「Proxy(代理人)」的概念:MapleEzorsia 的 DLL 夾在中間,做完自己的事之後,假裝自己是真的 DLL 繼續服務遊戲。
代理比喻
就像你請了一位秘書。老闆打電話來,秘書先接下(做一些篩選和記錄),然後把電話轉給你。老闆不知道中間有秘書存在,你也正常接到了電話。
這樣做有什麼好處
| 優點 | 說明 |
|---|---|
| 不需要額外的啟動器 EXE | 玩家直接雙擊 MapleStory.exe 就好,不需要先跑注入器 |
| 不需要手動注入 | 傳統 DLL 注入需要外部工具把程式碼「塞進」遊戲程序,容易被防毒誤判;這個方法由遊戲自己載入 |
| 對使用者完全透明 | 使用者不需要知道背後發生了什麼,體驗跟原版遊戲啟動一模一樣 |
| 功能完整轉發 | 真正的鍵盤輸入功能完全保留,遊戲操作不受影響 |
安全提醒
DLL 劫持在資安領域通常是一種攻擊手法,惡意程式也會用這個技術。這裡我們是在自己的私服環境中,對自己的遊戲客戶端進行修改,屬於合法的技術研究與學習用途。請勿將此技術用於未經授權的軟體。
延伸閱讀
- 02_MapleStory_v83客戶端架構概覽 — 下一篇:MapleStory 啟動流程與 Themida 保護機制
- 03_dinput8代理DLL原理 — 深入 dinput8.cpp 如何代理真正的 DLL 並安裝修改
- 04_開發環境建立(Visual_Studio_x86) — 設定 Visual Studio 編譯 x86 DLL
- 02_dllmain.cpp_DLL入口點 — Phase 2:DLL 載入後,第一行我們的程式碼從哪裡開始