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

API Hook 核心技術:IAT 修改實現任意函數攔截 (以 MessageBoxW 為例)

     編輯:VC 教學

原理:導入地址表 (IAT) 重寫

PE 文件載入時,載入器解析導入表,將每個導入函數的實際地址寫入 IAT (FirstThunk)。**修改 IAT 中的函數指標,即可將調用重導向到自定義函數**。

核心數據結構


// 待 Hook 函數描述
typedef struct _HOOK_API {
    LPCSTR  szFunc;      // 目標函數名 (如 "MessageBoxW")
    PROC    pNewProc;    // 新函數指標
    PROC    pOldProc;    // 原函數指標 (保存用於呼叫原函數)
} HOOK_API, *LPHOOK_API;

關鍵函數:定位模組導入表中特定 DLL 的描述符


PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportMod)
{
    // 1. DOS Header
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hModule;
    if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return NULL;

    // 2. NT Headers
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDos->e_lfanew);
    if (pNt->Signature != IMAGE_NT_SIGNATURE) return NULL;

    // 3. 導入表 RVA
    DWORD importRVA = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    if (!importRVA) return NULL;

    // 4. 遍歷導入描述符
    PIMAGE_IMPORT_DESCRIPTOR pDesc = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)hModule + importRVA);
    while (pDesc->Name) {
        PSTR szName = (PSTR)((BYTE*)hModule + pDesc->Name);
        if (_stricmp(szName, szImportMod) == 0) // 找到目標 DLL (如 "USER32.dll")
            return pDesc;
        pDesc++;
    }
    return NULL;
}

核心 Hook 函數:修改 IAT 指標


BOOL HookAPIByName(HMODULE hModule, LPCSTR szImportMod, LPHOOK_API pHookApi)
{
    // 1. 取得導入描述符
    PIMAGE_IMPORT_DESCRIPTOR pDesc = GetNamedImportDescriptor(hModule, szImportMod);
    if (!pDesc) return FALSE;

    // 2. 取得 OriginalFirstThunk (INT) 和 FirstThunk (IAT)
    PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pDesc->OriginalFirstThunk);
    PIMAGE_THUNK_DATA pRealThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pDesc->FirstThunk);

    // 3. 遍歷 Thunk 陣列
    while (pOrigThunk->u1.Function) {
        // 忽略序數導入
        if (!(pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) {
            PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)
                ((BYTE*)hModule + pOrigThunk->u1.AddressOfData);

            // 比對函數名
            if (_stricmp(pHookApi->szFunc, (char*)pByName->Name) == 0) {
                // 4. 修改 IAT 頁面保護
                MEMORY_BASIC_INFORMATION mbi;
                VirtualQuery(pRealThunk, &mbi, sizeof(mbi));
                DWORD oldProtect;
                VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &oldProtect);

                // 5. 保存原指標、寫入新指標
                if (!pHookApi->pOldProc)
                    pHookApi->pOldProc = (PROC)pRealThunk->u1.Function;
                pRealThunk->u1.Function = (DWORD)pHookApi->pNewProc;

                // 6. 恢復保護
                VirtualProtect(mbi.BaseAddress, mbi.RegionSize, oldProtect, &oldProtect);
                return TRUE;
            }
        }
        pOrigThunk++;
        pRealThunk++;
    }
    return FALSE; // 未找到目標函數
}

自定義 MessageBoxW 範例


// 新函數實現
int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
    // 修改內容、記錄日誌、條件攔截...
    return MessageBoxW(hWnd, L"Hooked!", L"API Hook Demo", uType);
}

// 全域 Hook 描述
HOOK_API g_HookMsgBox = {
    "MessageBoxW",  // 目標函數名
    (PROC)MyMessageBoxW, // 新函數
    NULL             // 原函數 (Hook 成功後填入)
};

// DLL 入口:PROCESS_ATTACH 時安裝
BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID)
{
    if (reason == DLL_PROCESS_ATTACH) {
        // 結合全域鉤子 (WH_MOUSE) 確保 DLL 注入目標進程
        // 此處簡化:假設已注入
        HookAPIByName(GetModuleHandle(NULL), "USER32.dll", &g_HookMsgBox);
    }
    return TRUE;
}

EXE 端:載入 Hook DLL 並啟動全域鉤子


// 主程式
HMODULE hHookDll = LoadLibrary("ApiHook.dll"); // 載入 DLL
// 安裝滑鼠鉤子觸發 DLL 注入
HHOOK hMouseHook = SetWindowsHookEx(WH_MOUSE, 
    (HOOKPROC)GetProcAddress(hHookDll, "DummyMouseProc"), 
    hHookDll, 0); // 0 = 全域
// 此時 ApiHook.dll 已注入所有 GUI 進程,DLL_PROCESS_ATTACH 執行 HookAPIByName
// 彈出 MessageBoxW 的程式將顯示 "Hooked!"

進階議題

議題說明
EAT (導出表) Hook修改 DLL 自己的導出表,影響所有調用者,更徹底但風險高
Inline Hook (代碼填充)在目標函數開頭寫入 JMP 指令,無視 IAT,攔截動態 GetProcAddress
延遲載入 DLLIAT 初始為存根,首次調用時解析,需在解析後 Hook
CFG (Control Flow Guard)Win8.1+ 編譯器插入間接呼叫檢查,修改 IAT 可能觸發 CFG 失敗
64 位相容指標 64 位,IMAGE_THUNK_DATA 為 ULONGLONG,編譯 x64 DLL

防禦與檢測

  • 定期校驗關鍵 API (MessageBox, CreateFile, VirtualAlloc) 的 IAT 指標是否指向合法模組。
  • 使用 GetProcAddress 取得真實地址與 IAT 比對。
  • 啟用 CFG、ACG (Arbitrary Code Guard)、ETW 監控。

IAT Hook 原理圖

IAT Hook 是用戶態 API 攔截最經典、最穩定的技術,掌握 PE 結構與 VirtualProtect 操作是核心關鍵。

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