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

WinAPI钩子Trampoline函数异常求助:Hook生效但MessageBox不显示

问题:Hook MessageBoxA后无法弹出消息框

我尝试通过Trampoline函数Hook WinAPI的MessageBoxA,用包含jmp指令的补丁跳转到Trampoline函数,Trampoline中保留了原API的初始字节,之后通过jmp跳回原函数补丁后的位置。运行程序时能输出Hooked MessageBoxA called!,说明Hook已生效,但无法看到消息框。

原代码

#include <windows.h>
#include <iostream>

struct HookedFunction {
    FARPROC functionOriginalAddress;
    BYTE functionOriginalBytes[12];
    SIZE_T bytesRead;
    SIZE_T bytesWritten;
    int modifyTimes;
};

HookedFunction* MessageBoxAStruct;
LPVOID trampolineAddress;

// Define the hooked MessageBoxA function
int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
    // Custom behavior
    std::cout << "Hooked MessageBoxA called!" << std::endl;

    // Call the original MessageBoxA using the trampoline
    auto trampoline = (decltype(&MessageBoxA))trampolineAddress;
    return trampoline(hWnd, lpText, lpCaption, uType);
}

void HookMessageBoxA() {
    printf("hooking...\n");

    MessageBoxAStruct = new HookedFunction;
    MessageBoxAStruct->bytesRead = 0;
    MessageBoxAStruct->bytesWritten = 0;

    // Get the handle to user32.dll (if it's already loaded)
    HMODULE library = GetModuleHandle(L"user32.dll");
    if (library == NULL) {
        std::cout << "Handle error - couldn't get the address: " << GetLastError() << std::endl;
        return;
    }

    if (MessageBoxAStruct->modifyTimes != 1)
    {
        printf("hooking...\n");

        FARPROC MessageBoxAAddress = GetProcAddress(library, "MessageBoxA");
        // Check if the address is valid
        if (MessageBoxAAddress == NULL) {
            std::cout << "Failed to get address of MessageBoxA: " << GetLastError() << std::endl;
            return;
        }
        MessageBoxAStruct->functionOriginalAddress = MessageBoxAAddress;
        // Save the first 12 bytes of the original MessageBoxA function - will need for unhooking
        if (!ReadProcessMemory(GetCurrentProcess(), MessageBoxAAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesRead)) {
            std::cout << "Failed to read original bytes of MessageBoxA: " << GetLastError() << std::endl;
            return;
        }

        MessageBoxAStruct->modifyTimes = 1;

        // Allocate memory for the trampoline function
        trampolineAddress = VirtualAlloc(NULL, sizeof(MessageBoxAStruct->functionOriginalBytes) + 14, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (trampolineAddress == NULL) {
            std::cout << "Failed to allocate memory for the trampoline: " << GetLastError() << std::endl;
            return;
        }

        // Copy original bytes to the trampoline
        if (!WriteProcessMemory(GetCurrentProcess(), trampolineAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesWritten)) {
            std::cout << "Failed to write trampoline: " << GetLastError() << std::endl;
            return;
        }

        // Add a jump from the trampoline back to the original function's next instruction
        DWORD64 jumpBackAddress = (DWORD64)MessageBoxAAddress + sizeof(MessageBoxAStruct->functionOriginalBytes);
        unsigned char jumpBackPatch[] = {
            0x48, 0xb8,              // mov rax,
            0x00, 0x00, 0x00, 0x00,  // <address to jump back to>
            0x00, 0x00, 0x00, 0x00,  // <address to jump back to>
            0xFF, 0xE0               // jmp rax
        };
        *(DWORD64*)&jumpBackPatch[2] = jumpBackAddress;

        if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)((DWORD64)trampolineAddress + sizeof(MessageBoxAStruct->functionOriginalBytes)), jumpBackPatch, sizeof(jumpBackPatch), &MessageBoxAStruct->bytesWritten)) {
            std::cout << "Failed to write jump back in trampoline: " << GetLastError() << std::endl;
            return;
        }
    }

    // Create a patch "mov rax, <address of new MessageBoxA>;; jmp rax"
    unsigned char patch[] = {
        0x48, 0xb8,             // mov rax,
        0x00, 0x00, 0x00, 0x00, // <address of hook>
        0x00, 0x00, 0x00, 0x00, // <address of hook>
        0xFF, 0xE0              // jmp rax
    };
    *(DWORD64*)&patch[2] = (DWORD64)HookedMessageBoxA;



    // Change memory protection to allow writing
    DWORD oldProtect;
    if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cout << "Failed to change memory protection: " << GetLastError() << std::endl;
        return;
    }

    if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)MessageBoxAStruct->functionOriginalAddress, patch, sizeof(patch), &MessageBoxAStruct->bytesWritten)) {
        std::cout << "Failed to write patch: " << GetLastError() << std::endl;
        return;
    }

    // Restore original memory protection
    if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), oldProtect, &oldProtect)) {
        std::cout << "Failed to restore memory protection: " << GetLastError() << std::endl;
        return;
    }

    printf("Hooked successfully\n");
    std::cout << "finished hooking...\n\n" << std::endl;
}

int main() {
    HookMessageBoxA();
    // Example call to the hooked function
    MessageBoxA(NULL, "Test", "Test", MB_OK);

    return 0;
}

问题原因

  • 指令截断错误:固定截取12字节原函数指令,但MessageBoxA的前12字节可能包含不完整的x64指令。x64汇编指令长度不固定,截断指令会导致Trampoline执行时逻辑错误,原函数后续流程崩溃,无法弹出消息框。
  • 跳转地址计算错误:跳转回原函数的地址是用原地址加12字节计算的,但如果前12字节包含的指令总长度不等于12,这个地址会指向错误位置,破坏原函数执行流程。
  • Trampoline内存分配大小错误:跳转回原函数的补丁实际是12字节,但代码中按12+14字节分配内存,虽不直接影响功能,但逻辑上存在冗余。

修复方案

1. 确保截取完整指令序列

不能固定截取字节数,需保证截取的是完整的指令序列。x64下MessageBoxA的初始指令总长度通常超过12字节,这里改为截取14字节以覆盖完整初始指令。

2. 修正跳转地址与内存分配

根据实际截取的指令长度计算跳转回原函数的地址,同时修正Trampoline的内存分配大小。

修复后的代码示例

#include <windows.h>
#include <iostream>

struct HookedFunction {
    FARPROC functionOriginalAddress;
    // 改为截取14字节,确保包含完整初始指令
    BYTE functionOriginalBytes[14];
    SIZE_T bytesRead;
    SIZE_T bytesWritten;
    int modifyTimes;
};

HookedFunction* MessageBoxAStruct;
LPVOID trampolineAddress;

int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
    std::cout << "Hooked MessageBoxA called!" << std::endl;
    auto trampoline = (decltype(&MessageBoxA))trampolineAddress;
    return trampoline(hWnd, lpText, lpCaption, uType);
}

void HookMessageBoxA() {
    printf("hooking...\n");

    MessageBoxAStruct = new HookedFunction;
    MessageBoxAStruct->bytesRead = 0;
    MessageBoxAStruct->bytesWritten = 0;
    MessageBoxAStruct->modifyTimes = 0;

    HMODULE library = GetModuleHandle(L"user32.dll");
    if (library == NULL) {
        std::cout << "Handle error - couldn't get the address: " << GetLastError() << std::endl;
        return;
    }

    if (MessageBoxAStruct->modifyTimes != 1)
    {
        FARPROC MessageBoxAAddress = GetProcAddress(library, "MessageBoxA");
        if (MessageBoxAAddress == NULL) {
            std::cout << "Failed to get address of MessageBoxA: " << GetLastError() << std::endl;
            return;
        }
        MessageBoxAStruct->functionOriginalAddress = MessageBoxAAddress;

        // 读取前14字节原指令
        if (!ReadProcessMemory(GetCurrentProcess(), MessageBoxAAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesRead)) {
            std::cout << "Failed to read original bytes of MessageBoxA: " << GetLastError() << std::endl;
            return;
        }

        MessageBoxAStruct->modifyTimes = 1;

        // 计算Trampoline所需内存:14字节原指令 + 12字节跳转补丁
        size_t trampolineSize = sizeof(MessageBoxAStruct->functionOriginalBytes) + 12;
        trampolineAddress = VirtualAlloc(NULL, trampolineSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (trampolineAddress == NULL) {
            std::cout << "Failed to allocate memory for the trampoline: " << GetLastError() << std::endl;
            return;
        }

        // 复制原指令到Trampoline
        if (!WriteProcessMemory(GetCurrentProcess(), trampolineAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesWritten)) {
            std::cout << "Failed to write trampoline: " << GetLastError() << std::endl;
            return;
        }

        // 计算跳转回原函数的地址:原地址 + 截取的指令长度
        DWORD64 jumpBackAddress = (DWORD64)MessageBoxAAddress + sizeof(MessageBoxAStruct->functionOriginalBytes);
        unsigned char jumpBackPatch[] = {
            0x48, 0xb8,              // mov rax, [address]
            0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
            0xFF, 0xE0               // jmp rax
        };
        *(DWORD64*)&jumpBackPatch[2] = jumpBackAddress;

        // 写入跳转补丁到Trampoline末尾
        if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)((DWORD64)trampolineAddress + sizeof(MessageBoxAStruct->functionOriginalBytes)), jumpBackPatch, sizeof(jumpBackPatch), &MessageBoxAStruct->bytesWritten)) {
            std::cout << "Failed to write jump back in trampoline: " << GetLastError() << std::endl;
            return;
        }
    }

    // 构建跳转Hook的补丁
    unsigned char patch[] = {
        0x48, 0xb8,             // mov rax, [HookedMessageBoxA地址]
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0xFF, 0xE0              // jmp rax
    };
    *(DWORD64*)&patch[2] = (DWORD64)HookedMessageBoxA;

    // 修改内存保护
    DWORD oldProtect;
    if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cout << "Failed to change memory protection: " << GetLastError() << std::endl;
        return;
    }

    // 写入补丁
    if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)MessageBoxAStruct->functionOriginalAddress, patch, sizeof(patch), &MessageBoxAStruct->bytesWritten)) {
        std::cout << "Failed to write patch: " << GetLastError() << std::endl;
        return;
    }

    // 恢复内存保护
    if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), oldProtect, &oldProtect)) {
        std::cout << "Failed to restore memory protection: " << GetLastError() << std::endl;
        return;
    }

    printf("Hooked successfully\n");
    std::cout << "finished hooking...\n\n" << std::endl;
}

int main() {
    HookMessageBoxA();
    MessageBoxA(NULL, "Test", "Test", MB_OK);
    return 0;
}

额外说明

如果要更严谨,建议使用反汇编库(如Capstone)自动分析指令长度,确保截取的是完整的指令序列,避免手动指定字节数带来的兼容性问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 07:47:03