异常的产生
- CPU执行程序汇编代码时,发现了异常,然后把异常告诉了操作系统。
- 操作系统首先会让程序自身处理这个异常。
- 如果程序自身有能力处理,程序就继续运行。
- 有能力是值程序中有注册的异常处理函数。
- 如果程序没能力处理,这个异常还没被处理,操作系统就会来处理,操作系统就会提示用户是调用注册表中记录的调试器调试还是结束程序
- 没处理能力是指程序中没有注册的异常处理函数。
1 |
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug |
- 要注意的是,如果在有调试器参与的情况下,程序出现了异常,操作系统是不会把异常处理权交给程序自身,而是交给调试器。
调试器可以把处理权继续转交给程序,也可以自己处理。(修改异常处代码,更改程序运行路径)。
捕获异常
- 一般要捕获异常只需要两个函数:SetUnhandledExceptionFilter截获异常,MiniDumpWriteDump写dump文件。
- 但是由于CRT函数可能会在内部调用SetUnhandledExceptionFilter(NULL)来解除我们程序设置的异常处理,导致我们的程序无法完整捕获奔溃。
- 另外,还有一部分非异常的CRT错误,不属于SEH异常捕获的范畴,需要调用_set_invalid_parameter_handler,_set_purecall_handler拦截,不这样做的话,它就会弹出很丑的Runtime Error提示框。
- 为了保证所有异常都能由我们捕获,就需要把SetUnhandledExceptionFilter函数Hook掉,不让其他人去设置自己的Exception处理,发生Exception就用我们自己的处理搞定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
inline int InitializeDump(const wchar_t* pszDumpDLL) { int nReturn = -1; HMODULE hLib = NULL; HANDLE hThread = NULL; PFN_KxEOpenDumpMonitorEx2 fnOpenMonitor = NULL; if (pszDumpDLL == NULL) pszDumpDLL = L"kdump.dll"; hLib = LoadLibraryW(pszDumpDLL); if (!hLib) goto Exit0; fnOpenMonitor = (PFN_KxEOpenDumpMonitorEx2)GetProcAddress(hLib, "KxEOpenDumpMonitorEx2"); if (!fnOpenMonitor) goto Exit0; hThread = (HANDLE)_beginthreadex(NULL, 0, InitNewDumpThread, (void*)fnOpenMonitor, 0, NULL); Sleep(10); if (hThread) { nReturn = 0; CloseHandle(hThread); } Exit0: return nReturn; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
namespace { void HandleAbortSignal(int signum) { ::RaiseException(STATUS_FATAL_APP_EXIT, 0, 0, nullptr); _exit(1); } void KSInvalidParameter(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t reserved) { ::RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr); _exit(1); } void KSPureCall() { ::RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr); _exit(1); } void RegisterCrashHandlers() { _set_invalid_parameter_handler(KSInvalidParameter); _set_purecall_handler(KSPureCall); signal(SIGABRT, HandleAbortSignal); } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// 这里加载kdump.dll成功后,新开了一个线程跑kdump.dll里面的内容 inline int InitializeDump(const wchar_t* pszDumpDLL) { int nReturn = -1; HMODULE hLib = NULL; HANDLE hThread = NULL; PFN_KxEOpenDumpMonitorEx2 fnOpenMonitor = NULL; if (pszDumpDLL == NULL) pszDumpDLL = L"kdump.dll"; hLib = LoadLibraryW(pszDumpDLL); if (!hLib) goto Exit0; fnOpenMonitor = (PFN_KxEOpenDumpMonitorEx2)GetProcAddress(hLib, "KxEOpenDumpMonitorEx2"); if (!fnOpenMonitor) goto Exit0; hThread = (HANDLE)_beginthreadex(NULL, 0, InitNewDumpThread, (void*)fnOpenMonitor, 0, NULL); Sleep(10); if (hThread) { nReturn = 0; CloseHandle(hThread); } Exit0: return nReturn; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); InitializeDump(); RegisterCrashHandlers(); HRESULT hRes = ::CoInitialize(NULL); return hRes; } |
进程外捕获奔溃
- 使用进程间通信(IPC、内存映射、管道等),把EXCEPTION_POINTERS指针数据等信息通知进程,让捕获进程去写dump文件。
- 一般来说,进程外捕获dump是比较推荐的做法,因为在进程内部写minidump是不安全的,原因是在进程的内部写dump文件时,关键的进程数据结构可能会被破坏掉,或者异常处理程序获得的堆栈可能是被覆盖了的。
(https://www.cnblogs.com/cswuyg/p/3286244.html)
多模块dump处理
- 如果CRT是/MD,那么CTR错误错误捕获是EXE,DLL公用;
dump捕获是多个EXE,DLL公用,只需要在EXE里面加上捕获处理。 - 如不CRT是/MT,那么CRT错误捕获是各PE文件独立,EXE,DLL必须有自己的处理;
dump捕获是多EXE,DLL公用。
dump文件存放目录
- 在%temp%文件夹下KS路径下。
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Windows 核心编程 _ 进程二06/19
- ♥ Cef:介绍06/29
- ♥ Windows 高级调试 _ 内存破坏03/21
- ♥ WindowsETW进程监控相关03/17
- ♥ Soui九07/25
- ♥ Windows 核心编程 _ 创建&&终止线程07/02