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