概述
Windows 下调用 dbghelp 自动生成程序 Dump
[toc]
说明
本文主要说明windows操作系统下,程序如何自动生成dump文件
使用 SysInternalsSuite 工具
相关参考
进程异常崩溃
抓取异常的程序 dump
procdump -ma -w test.exe D:\testdump通过 AeDebug 抓取
procdump -ma -i D:\testdump抓取程序启动时异常 dump
procdump -ma -e -w explorer.exe D:\testdump高CPU抓取异常
procdump 20548 -ma -c 70 -s 5 -n 2 -o D:\testdump命令行说明:
当进程 CPU>70% 时连续间隔超过 5s ,自动抓两个dump 。
20548 → 进程ID
-ma → FullDump,为了减少痛苦,必须用这个
-c 70 → CPU >70% 时开始抓取,你可以设置 大一点。
-s 5 → 连续超过 5s 时
-n 2 → 总计生成2个dump
-o xx → dump保存地
最后就是截图,我改成 cpu>15% 的阈值,可以看到目录已成功生成 2 个dump。

Windows 生成 dump
如果生成dump
生成dump主要依赖于windowsapi函数 SetUnhandledExceptionFilter,参数为一个回调函数,主要依赖于该函数以及回调函数中的相关逻辑生成dump。
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
_In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)注意点:
- 最好是在程序开始处设置回调
- 如果回调不生效,也可使用
AddVectoredExceptionHandler函数设置回调,其用法与SetUnhandledExceptionFilter基本一致,但是需要注意的是,使用AddVectoredExceptionHandler后,outpuydebugstring也会触发回调,所以这个函数慎用。
dump回调函数
dump回调函数可根据自己的需求去编写,以下为一个示例:
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; // 往下传,继续交给 windows 默认的处理函数处理
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 ,入参为回调函数。
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);到此,设置并捕获异常的接口完成,以下补充几点个人经验。
二、手写崩溃
void CreteCrash()
{
int *ptr = nullptr;
*ptr = 1;
}三、回调不生效问题
SetUnhandledExceptionFilter是无法捕获printf,scan,strlen等CRT函数的异常的,如printf(NULL)异常;除此之外,在有的环境当中,因为软件被Hook的问题,导致函数不生效,设置的回调往往不起作用,此时可以修改设置的函数地址来屏蔽其他软件的Hook。
代码
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和 [[【winapi】VirtualProtect|VirtualProtect]] 可能会导致程序崩溃,并且不会生成dump。可尝试修改 [[【winapi】VirtualProtect|VirtualProtect]] 和 WriteProcessMemory 函数的参数测试。
方案一
修改 VirtualProtect 的 flag.设置为 PAGE_EXECUTE_READWRITE
VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &dwOldFlag);方案二
WriteProcessMemory 可直接使用 memcpy 替代。
memcpy(addr, code, size);代码总结
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);