使用MiniHook Hook RtlAllocateHeap在Win10死锁、Win7运行正常问题
问题根因
你遇到的挂起是典型的多线程锁顺序反转死锁,Win7下正常是因为Win7的MessageBoxA单线程运行,Win10下MessageBoxA会隐式启动多个后台线程处理UI渲染、输入法适配、主题加载等逻辑,多线程同时触发堆分配时触发了你的代码缺陷:
- 全局
intercept变量是所有线程共享的,多线程同时进入Hook回调时会互相污染拦截状态,原本用来防止单线程递归的逻辑完全失效 - Hook回调内调用的
GetModuleHandleExA、GetModuleBaseNameA、printf都会持有系统全局锁(模块加载锁、控制台输出锁),和堆管理器的内部锁形成跨线程的锁顺序冲突:线程A持有模块加载锁等待堆锁,线程B持有堆锁等待模块加载锁,最终死锁在ZwWaitForAlertByThreadId等待点。
修复方案
最低成本临时修复
首先将全局拦截标志改为线程局部存储,避免跨线程状态污染,同时将全局计数改为原子操作避免竞态:
// 替换原来的全局变量定义 __declspec(thread) BOOL intercept = FALSE; // 每个线程独立持有拦截标志 LONG iMbox = 0; LONG iTotal = 0;
Hook回调内的计数改为原子累加:
LPVOID HookedHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) { InterlockedIncrement(&iTotal); LPVOID ptr = OldHeapAlloc(hHeap, dwFlags, dwBytes); if (intercept) { return ptr; } intercept = TRUE; InterlockedIncrement(&iMbox); // 剩余原有逻辑不变 HMODULE hModule; char lpBaseName[32]; if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)_ReturnAddress(), &hModule) != 0) { if (GetModuleBaseNameA(GetCurrentProcess(), hModule, lpBaseName, sizeof(lpBaseName)) != 0) { printf("Reserved %d at %08x from %s\n", dwBytes, ptr, lpBaseName); } } intercept = FALSE; return ptr; }
这个修改可以解决绝大多数场景的死锁问题,如果仍然偶现挂起,需要用更稳妥的无锁队列方案。
生产级稳定修复
Hook回调属于极敏感的执行路径,禁止调用任何可能持有系统全局锁的API,所有日志逻辑异步处理:
- 预先实现一个无锁环形队列,用来暂存分配事件的参数(返回地址、分配大小、分配指针)
- Hook回调内只做参数入队操作,不做任何其他逻辑
- 单独启动一个工作线程,轮询无锁队列,出队后再做模块名解析、日志打印操作
这样彻底避免了锁冲突的可能性,兼容所有Windows版本。
内容的提问来源于stack exchange,提问作者oXis
相关产品推荐
相关产品推荐

