// 本质上来说就是自己设置了UnhandleExceptionFilter后,C运行库或者其他什么别的函数也调用了,所以自己设置的就无效了,解决方案就是HOOK SET函数,让别人无法取代自己
很多 C/C++ 程序会设置自己的 Unhandled Exception Filter 用于捕获 Unhandled exceptions 并输出一些信息(例如,创建 mini-dump 或者输出调用栈到日志文件中)。
从 VC++2005 开始出于安全因素微软改变了 CRT 的行为。在以下情况下 CRT 不会通知被注册的 Unhandled Exception Filter:
CRT 是通过何种方式使得我们注册的 Unhandled Exception Filter 不被调用的?答案在 CRT 的代码中:
- /* 代码来源于 gs_report.c */
- /* Make sure any filter already in place is deleted. */
- SetUnhandledExceptionFilter(NULL);
- UnhandledExceptionFilter(&ExceptionPointers);
CRT 通过调用 SetUnhandledExceptionFilter 并传递参数 NULL 来清除用户注册的 Unhandled Exception Filter。如果期望用户注册的 Unhandled Exception Filter 总是被调用那么应该避免 CRT 中相关的清理代码。做法之一就是修改 CRT 代码并且编译为静态库(微软的 VC++ Libraries 开发 Lead Martyn Lovell 在 https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=101337&SiteID;=210 谈到过有关的问题),这里并不建议使用此做法。另外一种做法则是改变 SetUnhandledExceptionFilter 的行为,使得 CRT 对 SetUnhandledExceptionFilter 的调用不起任何作用(更加详细的论述可以参考《Windows 核心编程》相关章节)。
- // 无法得知此代码来源于
- #ifndef _M_IX86
- #error "The following code only works for x86!"
- #endif
- // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
- void DisableSetUnhandledExceptionFilter()
- {
- void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),
- "SetUnhandledExceptionFilter");
- if (addr)
- {
- unsigned char code[16];
- int size = 0;
- code[size++] = 0x33;
- code[size++] = 0xC0;
- code[size++] = 0xC2;
- code[size++] = 0x04;
- code[size++] = 0x00;
- DWORD dwOldFlag, dwTempFlag;
- VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
- WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
- VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
- }
- }
只需要在注册 Unhandled Exception Filter 之后调用 DisableSetUnhandledExceptionFilter() 函数,那么之后所有对 SetUnhandledExceptionFilter 的调用都将无效,自然 CRT 也无法通过调用 SetUnhandledExceptionFilter 来清除用户注册的 Unhandled Exception Filter。