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); }
关键修正点说明
- 指针地址处理:用
uintptr_t替代std::uint32_t,确保在32/64位编译环境下都能完整保存指针地址,避免截断。 - 相对偏移计算:使用
int32_t存储JMP指令的相对偏移,符合x86指令集的32位有符号偏移要求,计算逻辑更准确。 - 调用约定与参数匹配:修正hook函数的调用约定为
__stdcall,参数类型与MessageBoxW严格对齐,避免参数传递错乱。 - 地址偏移写法:通过
uint8_t*直接偏移计算目标地址,比转成整数再加1更安全,规避潜在的地址对齐问题。
内容的提问来源于stack exchange,提问作者CoderPE
相关产品推荐
相关产品推荐

