概述:Windows 下调用 dbghelp 自动生成程序 Dump
[toc]
一、Windows 生成 dump
本文主要说明windows操作系统下,程序如何自动生成dump文件
如果生成dump
生成dump主要依赖于windowsapi函数 SetUnhandledExceptionFilter
,参数为一个回调函数,主要依赖于该函数以及回调函数中的相关逻辑生成dump。
1 2
| LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( _In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
注意点:
- 最好是在程序开始处设置回调
- 如果回调不生效,也可使用
AddVectoredExceptionHandler
函数设置回调,其用法与 SetUnhandledExceptionFilter
基本一致,但是需要注意的是,使用 AddVectoredExceptionHandler
后,outpuydebugstring
也会触发回调,所以这个函数慎用。
dump回调函数
dump回调函数可根据自己的需求去编写,以下为一个示例:
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
| LONG WINAPI MyUnhandledExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) { SYSTEMTIME st; GetLocalTime(&st); CString time_now = _T(""); time_now.Format(_T("%04d_%02d_%02d_%02d_%02d_%03d.dmp"), st.wYear, st.wMonth, st.wDay,st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); CString dump_file_path = GetRootPath(); dump_file_path += _T("dump/"); CreateDirectory(dump_file_path, NULL); dump_file_path += time_now; HANDLE hFile = CreateFile(dump_file_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) return EXCEPTION_CONTINUE_EXECUTION;
MINIDUMP_EXCEPTION_INFORMATION mdei; mdei.ThreadId = GetCurrentThreadId(); mdei.ExceptionPointers = ExceptionInfo; mdei.ClientPointers = NULL; MINIDUMP_CALLBACK_INFORMATION mci; mci.CallbackRoutine = NULL; mci.CallbackParam = 0;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);
CloseHandle(hFile);
return EXCEPTION_EXECUTE_HANDLER; }
|
设置回调
写完回调函数之后,调用 SetUnhandledExceptionFilter
,入参为回调函数。
1
| SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
|
到此,设置并捕获异常的接口完成,以下补充几点个人经验。
二、手写崩溃
1 2 3 4 5
| void CreteCrash() { int *ptr = nullptr; *ptr = n; }
|
三、回调不生效问题
SetUnhandledExceptionFilter
是无法捕获 printf
, scan
, strlen
等CRT函数的异常的,如printf(NULL)
异常;除此之外,在有的环境当中,因为软件被hook的问题,导致 函数不生效,设置的回调往往不起作用,此时可以修改设置
的函数地址来屏蔽其他软件的hook。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void DisableSetUnhandledExceptionFilter() { void* addr = (void*)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "SetUnhandledExceptionFilter"); if (addr != NULL) { 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); } }
|
使用说明
在程序启动并设置dump回调之后调用 DisableSetUnhandledExceptionFilter
即可。
注意事项
经本人测试,在部分机器上,使用 WriteProcessMemory
和 VirtualProtect
可能会导致程序崩溃,并且不会生成dump。可尝试修改 VirtualProtect
和 WriteProcessMemory
函数的参数测试。
方案一
修改 VirtualProtect
的 flag.设置为 PAGE_EXECUTE_READWRITE
1
| VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &dwOldFlag);
|
方案二
WriteProcessMemory
可直接使用 memcpy
替代。
1
| memcpy(addr, code, size);
|
代码总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| DWORD dwOldFlag = 0; VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &dwOldFlag); #if 0 HANDLE hProcess = GetCurrentProcess(); if (!(hProcess == NULL || hProcess == INVALID_HANDLE_VALUE)) { WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL); } else { memcpy(addr, code, size); } #endif memcpy(addr, code, size); VirtualProtect(addr, size, dwOldFlag, &dwOldFlag);
|