// 本质上来说就是自己设置了UnhandleExceptionFilter后,C运行库或者其他什么别的函数也调用了,所以自己设置的就无效了,解决方案就是HOOK SET函数,让别人无法取代自己
很多 C/C++ 程序会设置自己的 Unhandled Exception Filter 用于捕获 Unhandled exceptions 并输出一些信息(例如,创建 mini-dump 或者输出调用栈到日志文件中)。
从 VC++2005 开始出于安全因素微软改变了 CRT 的行为。在以下情况下 CRT 不会通知被注册的 Unhandled Exception Filter:
- 调用了 abort() 并且设置 abort 的行为为 _CALL_REPORTFAULT(Release 版本默认使用此设置)
- Security Checks 失败时,具体来说就是检查到一些会引发安全问题的堆栈溢出时不会通知被注册的 Unhandled Exception Filter,会引发安全问题的堆栈溢出包括:覆盖了函数的返回值,覆盖了 Exception handler 的地址,覆盖了某些类型的参数。关于编译器的 Security Checks 的内容,详细参考:http://msdn.microsoft.com/en-us/library/Aa290051(注意,此文章谈到的是 Visual Studio .NET 2003,其中 _set_security_error_handler 函数在 VC++2005 以及以上版本已经无法使用)
- 如果没有调用 _set_invalid_parameter_handler 设置 Invalid parameter handler 时,检查到了非法的参数
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。