首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 實作全域攔截阻止惡意 Hook DLL 載入

     編輯:關於VC++

全域 Hook 載入機制

程式 A 安裝 WH_GETMESSAGE 全域 Hook,Hook 函式在 B.dll。當其他程式呼叫 GetMessage,系統檢測到 Hook,若目標進程未載入 B.dll,呼叫 LoadLibrary 強制載入。此機制導致 Hook DLL 注入所有呼叫 GetMessage 的進程。

阻止策略:攔截 LoadLibraryExW 並識別來源

  1. 正常 DLL 載入來自 kernel32.dll;Hook DLL 載入來自 user32.dll(XP 驗證,新版本類似)。
  2. 攔截 LoadLibraryExW,檢查呼叫者回傳位址是否落在 user32.dll 模組範圍內。
  3. 若是,回傳 NULL 模擬載入失敗,阻止 Hook 注入。

三步驟實作

1. 替代函式


HMODULE WINAPI NewLoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) {
    DWORD dwCaller;
    __asm {
        push dword ptr [ebp+4] // 取得回傳位址
        pop  dword ptr [dwCaller]
    }
    if (dwCaller > g_dwUser32Base && dwCaller < g_dwUser32End) {
        // 來自 user32.dll → Hook DLL 載入
        return NULL; // 阻止
    }
    return RealLoadLibraryExW(lpLibFileName, hFile, dwFlags); // 正常載入
}

2. 建立蹦床函式保存原始指令

  • 複製 LoadLibraryExW 前 7 位元組(兩條指令)到 FakeLoadLibraryExW。
  • 在 FakeLoadLibraryExW 末尾加 JMP 回原函式第 8 位元組處。
  • 原因:前兩條指令共 7 位元組,不能只複製 5 位元組再跳回第 6 位元組(指令邊界錯誤)。

3. 修改原始函式入口 JMP 至替代函式


// 修改 LoadLibraryExW 前 5 位元組為 JMP NewLoadLibraryExW
// 並將原始函式指標指向 FakeLoadLibraryExW

完整封裝類別


class GBlockHookDll {
public:
    GBlockHookDll() {
        MODULEINFO mi = {0};
        GetModuleInformation(GetCurrentProcess(), GetModuleHandle("user32.dll"), &mi, sizeof(mi));
        m_dwUser32Low = (DWORD)mi.lpBaseOfDll;
        m_dwUser32Hi  = m_dwUser32Low + mi.SizeOfImage;
    }

    void PatchLoadLibrary() {
        LPVOID* pRaw = (LPVOID*)&RealLoadLibraryExW;
        LPVOID  pNew = (LPVOID)NewLoadLibraryExW;
        BYTE*   pRawCode = (BYTE*)*pRaw;

        // 1. 備份前 7 位元組到 Fake
        const int N = 7;
        memcpy(FakeLoadLibraryExW, *pRaw, N);
        FakeLoadLibraryExW[N] = 0xE9; // JMP
        *(DWORD*)(FakeLoadLibraryExW + N + 1) = (DWORD)(pRawCode + N) - (DWORD)(FakeLoadLibraryExW + N + 5);

        // 2. 修改原始函式入口 JMP 到 New
        DWORD oldProtect;
        VirtualProtect(pRawCode, N, PAGE_READWRITE, &oldProtect);
        pRawCode[0] = 0xE9;
        *(DWORD*)(pRawCode + 1) = (DWORD)pNew - (DWORD)(pRawCode + 5);
        VirtualProtect(pRawCode, N, oldProtect, &oldProtect);

        // 3. 更新函式指標
        *pRaw = FakeLoadLibraryExW;
    }

private:
    static HMODULE WINAPI NewLoadLibraryExW(LPCWSTR, HANDLE, DWORD);
    static DWORD m_dwUser32Low, m_dwUser32Hi;
    static BYTE FakeLoadLibraryExW[12];
    static HMODULE (WINAPI *RealLoadLibraryExW)(LPCWSTR, HANDLE, DWORD);
};

// 靜態成員定義
DWORD GBlockHookDll::m_dwUser32Low = 0;
DWORD GBlockHookDll::m_dwUser32Hi  = 0;
BYTE  GBlockHookDll::FakeLoadLibraryExW[12] = {0};
HMODULE (WINAPI *GBlockHookDll::RealLoadLibraryExW)(LPCWSTR, HANDLE, DWORD) = LoadLibraryExW;

使用方式


GBlockHookDll g_Blocker; // 全域物件
// 在初始化階段 (如 CWinApp::InitInstance) 呼叫
g_Blocker.PatchLoadLibrary();

關鍵技術細節

  • 回傳位址取得:push ebp / mov ebp, esp 後,[ebp+4] 為回傳位址。
  • 指令長度分析:通用 Hook 需反組識別指令邊界(參考 Microsoft Detours 實作)。
  • 記憶體保護:VirtualProtect 修改代碼段屬性為可寫。
  • 相容性:僅針對 LoadLibraryExW,32 位元環境;64 位元需調整指令編碼與呼叫慣例。

風險與限制

  • 系統載入失敗會重試,可能影響效能(但 Hook 執行同樣有開銷)。
  • 誤判正常來自 user32.dll 的載入(極少見)。
  • 繞過技術:攻擊者可直接呼叫 LdrLoadDll 或手動映射 DLL。
  • 屬防禦性技術,非絕對安全邊界。

總結

透過 Inline Hook LoadLibraryExW、回傳位址溯源識別 user32.dll 發起的載入請求,可有效阻止全域 Hook DLL 強制注入。此技術展示 Windows 使用者模式 Hook 防禦的核心思路,實戰中建議結合簽名驗證、完整性檢查等多層防護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved