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

Hook Windows API MessageBoxW时触发写入访问违规的问题排查

解决MessageBoxW挂钩时的访问违例错误

你遇到的访问违例,核心原因是使用固定32位的std::uint32_t处理指针地址,在64位编译环境下会截断64位指针,导致写入操作指向错误内存区域。

具体来说,这段代码:

*(reinterpret_cast <std::uint32_t*>(reinterpret_cast<std::uint32_t>(hook_addr) + 1)) = rel_addr;

在64位程序中,hook_addr是64位指针,强制转换成std::uint32_t会丢失高32位地址信息,计算出的目标地址完全错误,写入操作自然触发访问违例。即便用32位编译,uint32_t虽能匹配指针长度,但用它处理指针并非标准做法,应使用uintptr_t(C++标准定义的、与指针位数匹配的无符号整数类型)。

此外还有两个潜在问题需要修正:

  • MessageBoxW的调用约定为__stdcall(x86),你的hook函数参数列表和类型需与原函数严格匹配,原函数的lpText和lpCaption是宽字符类型LPCWSTR,你误用了LPCSTR会导致参数错乱。
  • 相对地址计算需用适配指针位数的类型,避免地址截断。

修正后的完整代码

#include "Windows.h"
#include <cstdint>
#include <cstring>
#include <iostream>
#include <string> 
using namespace std;

// 匹配MessageBoxW的调用约定与参数类型
int __stdcall hookedFunc(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
    cout << "Hooked function called" << endl;
    return MessageBoxW(NULL, L"HOOKED BOX", L"HOOKED CAPTION", MB_YESNOCANCEL);
}

uintptr_t tramp_hook(void* hook_addr, void* new_func, uintptr_t instr_size)
{
    constexpr auto jmp_instr_size = 5;

    DWORD vp_old_prot{ 0u };
    // 增加VirtualProtect调用检查,方便排查权限问题
    if (!VirtualProtect(hook_addr, instr_size, PAGE_EXECUTE_READWRITE, &vp_old_prot)) {
        cerr << "VirtualProtect failed: " << GetLastError() << endl;
        return 0;
    }

    memset(hook_addr, 0x90, instr_size);

    // 使用uintptr_t处理指针地址,避免32/64位环境下的地址截断
    const uintptr_t hook_addr_uint = reinterpret_cast<uintptr_t>(hook_addr);
    const uintptr_t new_func_uint = reinterpret_cast<uintptr_t>(new_func);
    // JMP指令的相对偏移是32位有符号整数,用int32_t存储更准确
    const int32_t rel_addr = static_cast<int32_t>(new_func_uint - (hook_addr_uint + jmp_instr_size));

    *static_cast<uint8_t*>(hook_addr) = 0xE9;
    // 直接通过uint8_t*偏移计算地址,写法更安全直观
    *reinterpret_cast<int32_t*>(static_cast<uint8_t*>(hook_addr) + 1) = rel_addr;

    VirtualProtect(hook_addr, instr_size, vp_old_prot, nullptr);
    return hook_addr_uint + jmp_instr_size;
}

// 定义与MessageBoxW完全匹配的函数指针类型
typedef int (__stdcall* MESSAGEBOXW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);

MESSAGEBOXW hookedBox = reinterpret_cast<MESSAGEBOXW>(&hookedFunc);

int main() {
    MessageBoxW(NULL, L"not hooked", L"this is the caption", MB_YESNO);

    HMODULE dllHandl = LoadLibrary(L"user32.dll");
    void* msgBoxAddr = GetProcAddress(dllHandl, "MessageBoxW");

    cout << hookedBox << endl;

    tramp_hook(msgBoxAddr, hookedBox, 5);

    MessageBoxW(NULL, L"not hooked 2", L"this is the caption", MB_YESNO);
}

关键修正点说明

  1. 指针地址处理:用uintptr_t替代std::uint32_t,确保在32/64位编译环境下都能完整保存指针地址,避免截断。
  2. 相对偏移计算:使用int32_t存储JMP指令的相对偏移,符合x86指令集的32位有符号偏移要求,计算逻辑更准确。
  3. 调用约定与参数匹配:修正hook函数的调用约定为__stdcall,参数类型与MessageBoxW严格对齐,避免参数传递错乱。
  4. 地址偏移写法:通过uint8_t*直接偏移计算目标地址,比转成整数再加1更安全,规避潜在的地址对齐问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 11:25:26