You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IAT Hook MessageBoxA出现递归调用问题求助

IAT Hook MessageBoxA 引发无限递归问题

问题现象

编写IAT Hook程序替换MessageBoxA为自定义函数print_evil,预期仅打印一次"Evil print"后调用原始MessageBoxA,但实际程序持续重复打印"Evil print",陷入无限循环。

问题原因

核心问题出在原始函数地址的获取方式:

FunctionLikeMessageBoxA originalMsgBoxA = MessageBoxA;

程序启动时,MessageBoxA其实是从当前进程的IAT(导入地址表)中读取的地址,并非user32.dll中函数的真实地址。当你完成IAT Hook后,IAT里的MessageBoxA地址已被替换为print_evil,此时originalMsgBoxA指向的就是你的Hook函数,调用它自然会触发递归,导致无限循环。

解决方案

直接从user32.dll中获取MessageBoxA的真实原始地址,绕过IAT的影响。具体修改如下:

修改后的代码

#include <Windows.h>
#include <stdio.h>

typedef int(__stdcall* FunctionLikeMessageBoxA) (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

FunctionLikeMessageBoxA originalMsgBoxA = NULL; // 先初始化为空

int print_evil(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
    printf("Evil print\n");
    Sleep(1000);

    return originalMsgBoxA(hWnd, lpText, lpCaption, uType);
}

int main()
{
    // 从user32.dll获取原始MessageBoxA地址
    HMODULE user32 = LoadLibraryA("user32.dll");
    originalMsgBoxA = (FunctionLikeMessageBoxA)GetProcAddress(user32, "MessageBoxA");

    // message box before IAT hooking
    MessageBoxA(NULL, "Before Hooking", "Before Hooking", 0);

    LPVOID imageBase = GetModuleHandleA(NULL);
    PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)imageBase;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)imageBase + dosHeaders->e_lfanew);

    PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
    IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)imageBase);

    LPCSTR currentLibraryName = NULL;
    HMODULE loadedLibrary = NULL;
    PIMAGE_IMPORT_BY_NAME functionName = NULL;

    while (importDescriptor->Name != NULL)
    {
        currentLibraryName = (LPCSTR)importDescriptor->Name + (DWORD_PTR)imageBase;
        loadedLibrary = LoadLibraryA(currentLibraryName);

        if (loadedLibrary)
        {
            PIMAGE_THUNK_DATA originalFirstThunk = NULL;
            PIMAGE_THUNK_DATA firstThunk = NULL;

            originalFirstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)imageBase + importDescriptor->OriginalFirstThunk);
            firstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)imageBase + importDescriptor->FirstThunk);

            while (originalFirstThunk->u1.AddressOfData != NULL)
            {
                functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)imageBase + originalFirstThunk->u1.AddressOfData);

                if (strcmp(functionName->Name, "MessageBoxA") == 0)
                {
                    DWORD oldProtect = 0;

                    VirtualProtect((LPVOID)(&firstThunk->u1.Function), sizeof(DWORD_PTR), PAGE_READWRITE, &oldProtect);

                    firstThunk->u1.Function = (DWORD_PTR)print_evil;

                    VirtualProtect((LPVOID)(&firstThunk->u1.Function), sizeof(DWORD_PTR), oldProtect, &NULL);
                }

                originalFirstThunk++;
                firstThunk++;
            }
        }

        importDescriptor++;
    }

    // after IAT hooking
    MessageBoxA(0, "Hello", "Hello msg", 0);
    return 0;
}

关键修改点

  1. 将originalMsgBoxA初始化为NULL,不再直接赋值为MessageBoxA
  2. 在main函数开头,通过LoadLibraryA加载user32.dll,再用GetProcAddress获取MessageBoxA的真实原始地址
  3. 调整VirtualProtect的内存大小参数为sizeof(DWORD_PTR),适配32/64位系统(原代码写死8字节仅适配64位)

这样修改后,originalMsgBoxA指向的是user32.dll中MessageBoxA的真实入口,调用它不会触发IAT Hook,自然避免了递归循环。

内容的提问来源于stack exchange,提问作者nortain32

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 20:55:20