[toc]

一、Windows 生成dump

本文主要说明windows操作系统下,程序如何自动生成dump文件

如果生成dump

生成dump主要依赖于windowsapi函数 SetUnhandledExceptionFilter,参数为一个回调函数,主要依赖于该函数以及回调函数中的相关逻辑生成dump。

C++cpp
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
        _In_ LPTOP_LEVEL_EXCEPTION_FILTER  lpTopLevelExceptionFilter)

注意点:

  1. 最好是在程序开始处设置回调
  2. 如果回调不生效,也可使用 AddVectoredExceptionHandler 函数设置回调,其用法与 SetUnhandledExceptionFilter 基本一致,但是需要注意的是,使用 AddVectoredExceptionHandler 后,outpuydebugstring 也会触发回调,所以这个函数慎用。

dump回调函数

dump回调函数可根据自己的需求去编写,以下为一个示例:

C++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 ,入参为回调函数。

C++cpp
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

到此,设置并捕获异常的接口完成,以下补充几点个人经验。

二、手写崩溃

C++cpp
void CreteCrash()
{
    int *ptr = nullptr;
	*ptr = n;
}

三、回调不生效问题

SetUnhandledExceptionFilter 是无法捕获 printf, scan, strlen 等CRT函数的异常的,如printf(NULL) 异常;除此之外,在有的环境当中,因为软件被hook的问题,导致 函数不生效,设置的回调往往不起作用,此时可以修改设置 的函数地址来屏蔽其他软件的hook。

代码

C++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即可。

注意事项

经本人测试,在部分机器上,使用 WriteProcessMemoryVirtualProtect 可能会导致程序崩溃,并且不会生成dump。可尝试修改 VirtualProtectWriteProcessMemory 函数的参数测试。

方案一

修改 VirtualProtect 的 flag.设置为 PAGE_EXECUTE_READWRITE

C++cpp
VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &dwOldFlag);

方案二

WriteProcessMemory 可直接使用 memcpy 替代。

C++cpp
memcpy(addr, code, size);

代码总结

C++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);