什麼是 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.dllDirectInput 8,負責讀取鍵盤、滑鼠、搖桿輸入
d3d8.dllDirect3D 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 不會直接去系統資料夾找,而是按照固定順序搜尋

  1. 程式本身所在的資料夾(例如 C:\Maplestory\
  2. 系統資料夾(C:\Windows\System32\
  3. Windows 資料夾(C:\Windows\
  4. 其他 PATH 環境變數指定的資料夾

關鍵重點

Windows 找到第一個符合名稱的 DLL 就立刻載入,不會繼續往後找。這個「先到先得」的機制,就是 DLL 劫持的核心漏洞。

劫持概念

既然 Windows 優先搜尋程式自己的資料夾,那我只要:

  1. 在 MapleStory 的遊戲資料夾裡,放一個我自己做的 dinput8.dll
  2. MapleStory 啟動,說「我要 dinput8.dll」
  3. Windows 搜尋:「欸,遊戲資料夾就有了!」——載入的是我的版本
  4. 我的 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]
}

讀懂這段程式碼的白話版

  1. GetSystemDirectoryA — 問 Windows「系統資料夾在哪裡?」
  2. LoadLibraryA — 親自去載入真正的 dinput8.dll,把位置記下來
  3. GetProcAddress — 從真正的 DLL 裡找到真正的函式,把「電話號碼」記下來
  4. __asm jmp — 當遊戲打來電話,我直接幫它轉接到真正的函式,遊戲感覺不到任何差異

這就是「Proxy(代理人)」的概念:MapleEzorsia 的 DLL 夾在中間,做完自己的事之後,假裝自己是真的 DLL 繼續服務遊戲。

代理比喻

就像你請了一位秘書。老闆打電話來,秘書先接下(做一些篩選和記錄),然後把電話轉給你。老闆不知道中間有秘書存在,你也正常接到了電話。


這樣做有什麼好處

優點說明
不需要額外的啟動器 EXE玩家直接雙擊 MapleStory.exe 就好,不需要先跑注入器
不需要手動注入傳統 DLL 注入需要外部工具把程式碼「塞進」遊戲程序,容易被防毒誤判;這個方法由遊戲自己載入
對使用者完全透明使用者不需要知道背後發生了什麼,體驗跟原版遊戲啟動一模一樣
功能完整轉發真正的鍵盤輸入功能完全保留,遊戲操作不受影響

安全提醒

DLL 劫持在資安領域通常是一種攻擊手法,惡意程式也會用這個技術。這裡我們是在自己的私服環境中,對自己的遊戲客戶端進行修改,屬於合法的技術研究與學習用途。請勿將此技術用於未經授權的軟體。


延伸閱讀