利用 PE 格式的匯入位址表 (IAT, Import Address Table) 修改,將目標模組對特定 DLL 函數 (如 MessageBoxW) 的呼叫重導向至自訂函數。配合全域 Hook 將 DLL 注入目標進程,實現 API 攔截。
WH_GETMESSAGE),強制系統將 DLL 映射至目標進程位址空間DLL_PROCESS_ATTACH) 執行 IAT 修改,替換目標函數入口static int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText,
LPCWSTR lpCaption, UINT uType) {
// 完全取代訊息內容
return MessageBox(hWnd, L"TNT", L"TNT", uType);
}
// 取得指定模組的指定匯入 DLL 描述子
PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportMod) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hModule;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)pDos + pDos->e_lfanew);
if (pNT->Signature != IMAGE_NT_SIGNATURE) return NULL;
// 匯入表 RVA
DWORD importRVA = pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if (!importRVA) return NULL;
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDos + importRVA);
while (pImport->Name) {
PSTR szCurr = (PSTR)((DWORD)pDos + pImport->Name);
if (stricmp(szCurr, szImportMod) == 0) break; // 找到目標 DLL (如 USER32.DLL)
pImport++;
}
return (pImport->Name) ? pImport : NULL;
}
// 依函數名稱 Hook API
BOOL HookAPIByName(HMODULE hModule, LPCSTR szImportMod, LPHOOKAPI pHookApi) {
PIMAGE_IMPORT_DESCRIPTOR pImport = GetNamedImportDescriptor(hModule, szImportMod);
if (!pImport) return FALSE;
PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + pImport->OriginalFirstThunk);
PIMAGE_THUNK_DATA pRealThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + pImport->FirstThunk);
while (pOrigThunk->u1.Function) {
// 依名稱匯入 (非序數)
if (!(pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) {
PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)hModule + pOrigThunk->u1.AddressOfData);
if (strcmpi(pHookApi->szFunc, (char*)pByName->Name) == 0) {
// 修改頁面保護為可寫
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(pRealThunk, &mbi, sizeof(mbi));
DWORD oldProtect;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &oldProtect);
// 保存原函數指標並替換
if (!pHookApi->pOldProc) pHookApi->pOldProc = (PROC)pRealThunk->u1.Function;
pRealThunk->u1.Function = (PDWORD)pHookApi->pNewProc;
// 還原保護屬性
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, oldProtect, &oldProtect);
return TRUE;
}
}
pOrigThunk++; pRealThunk++;
}
return FALSE;
}
BYTE OldCode[5], NewCode[5]; // 儲存原始/新指令 (JMP 相對跳轉 5 字節)
FARPROC fpTarget; // 目標 API 位址
void HookOn() {
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
VirtualProtectEx(hProc, fpTarget, 5, PAGE_READWRITE, &oldProtect);
WriteProcessMemory(hProc, fpTarget, NewCode, 5, NULL); // 寫入 JMP MyFunc
VirtualProtectEx(hProc, fpTarget, 5, oldProtect, &oldProtect);
}
void HookOff() {
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
VirtualProtectEx(hProc, fpTarget, 5, PAGE_READWRITE, &oldProtect);
WriteProcessMemory(hProc, fpTarget, OldCode, 5, NULL); // 還原原始指令
VirtualProtectEx(hProc, fpTarget, 5, oldProtect, &oldProtect);
}
// 動態載入 DLL 並安裝 Hook
extern "C" __declspec(dllimport) BOOL __stdcall InstallHook();
extern "C" __declspec(dllimport) BOOL __stdcall UninstallHook();
void __fastcall TForm1::Button1Click(TObject *Sender) {
if (InstallHook()) {
MessageBox(NULL, "內容", "標題", MB_OK); // 實際彈出 "TNT"
}
UninstallHook();
}
JMP rel32 指令佔 5 字節 (0xE9 + 4 位元組相對位移)VirtualProtect/VirtualProtectEx 設為 PAGE_READWRITE,修改後還原JMP [RIP+offset] 或絕對跳轉,IAT 結構亦不同