// 本質(zhì)上來說就是自己設(shè)置了UnhandleExceptionFilter后,C運行庫或者其他什么別的函數(shù)也調(diào)用了,所以自己設(shè)置的就無效了,解決方案就是HOOK SET函數(shù),讓別人無法取代自己
很多 C/C++ 程序會設(shè)置自己的 Unhandled Exception Filter 用于捕獲 Unhandled exceptions 并輸出一些信息(例如,創(chuàng)建 mini-dump 或者輸出調(diào)用棧到日志文件中)。
從 VC++2005 開始出于安全因素微軟改變了 CRT 的行為。在以下情況下 CRT 不會通知被注冊的 Unhandled Exception Filter:
- 調(diào)用了 abort() 并且設(shè)置 abort 的行為為 _CALL_REPORTFAULT(Release 版本默認(rèn)使用此設(shè)置)
- Security Checks 失敗時,具體來說就是檢查到一些會引發(fā)安全問題的堆棧溢出時不會通知被注冊的 Unhandled Exception Filter,會引發(fā)安全問題的堆棧溢出包括:覆蓋了函數(shù)的返回值,覆蓋了 Exception handler 的地址,覆蓋了某些類型的參數(shù)。關(guān)于編譯器的 Security Checks 的內(nèi)容,詳細(xì)參考:http://msdn.microsoft.com/en-us/library/Aa290051(注意,此文章談到的是 Visual Studio .NET 2003,其中 _set_security_error_handler 函數(shù)在 VC++2005 以及以上版本已經(jīng)無法使用)
- 如果沒有調(diào)用 _set_invalid_parameter_handler 設(shè)置 Invalid parameter handler 時,檢查到了非法的參數(shù)
CRT 是通過何種方式使得我們注冊的 Unhandled Exception Filter 不被調(diào)用的?答案在 CRT 的代碼中:
- /* 代碼來源于 gs_report.c */
- /* Make sure any filter already in place is deleted. */
- SetUnhandledExceptionFilter(NULL);
- UnhandledExceptionFilter(&ExceptionPointers);
CRT 通過調(diào)用 SetUnhandledExceptionFilter 并傳遞參數(shù) NULL 來清除用戶注冊的 Unhandled Exception Filter。如果期望用戶注冊的 Unhandled Exception Filter 總是被調(diào)用那么應(yīng)該避免 CRT 中相關(guān)的清理代碼。做法之一就是修改 CRT 代碼并且編譯為靜態(tài)庫(微軟的 VC++ Libraries 開發(fā) Lead Martyn Lovell 在 https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=101337&SiteID=210 談到過有關(guān)的問題),這里并不建議使用此做法。另外一種做法則是改變 SetUnhandledExceptionFilter 的行為,使得 CRT 對 SetUnhandledExceptionFilter 的調(diào)用不起任何作用(更加詳細(xì)的論述可以參考《Windows 核心編程》相關(guān)章節(jié))。
- // 無法得知此代碼來源于
- #ifndef _M_IX86
- #error "The following code only works for x86!"
- #endif
-
- // 此函數(shù)一旦成功調(diào)用,之后對 SetUnhandledExceptionFilter 的調(diào)用將無效
- 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 之后調(diào)用 DisableSetUnhandledExceptionFilter() 函數(shù),那么之后所有對 SetUnhandledExceptionFilter 的調(diào)用都將無效,自然 CRT 也無法通過調(diào)用 SetUnhandledExceptionFilter 來清除用戶注冊的 Unhandled Exception Filter。