【原理】
windows程序当遇到异常,没有try-catch或者try-catch也无法捕获到的异常时,程序就会自动退出,如果这时候没有dump文件的话,我们是没有得到任何程序退出的信息。在windows程序异常退出之前,会预先调用一个在程序中注册的异常处理回调函数(默认是没有设置),只要我们在这个回调函数中调用MiniDumpWriteDump函数就可以产生我们想要的dump文件。
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
windows程序当遇到异常,没有try-catch或者try-catch也无法捕获到的异常时,程序就会自动退出,如果这时候没有dump文件的话,我们是没有得到任何程序退出的信息。在windows程序异常退出之前,会预先调用一个在程序中注册的异常处理回调函数(默认是没有设置),只要我们在这个回调函数中调用MiniDumpWriteDump函数就可以产生我们想要的dump文件。
【实现】
1.调用SetUnhandledExceptionFilter注册一个自定义的异常处理回调函数SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
2.CreateFile创建dump文件,调用MiniDumpWriteDump函数往dump文件写异常信息
【实现】
#pragma once
#include <windows.h>
class CMiniDumper
{
public:
static HRESULT CreateInstance();
static HRESULT ReleaseInstance();
public:
LONG WriteMiniDump(_EXCEPTION_POINTERS *pExceptionInfo);
private:
void SetMiniDumpFileName(void);
BOOL GetImpersonationToken(HANDLE* phToken);
BOOL EnablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
BOOL RestorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
private:
CMiniDumper();
virtual ~CMiniDumper(void);
private:
TCHAR m_szMiniDumpPath[MAX_PATH];
TCHAR m_szAppPath[MAX_PATH];
};
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <tchar.h>
#include <dbghelp.h>
#include "miniDump.h"
#ifdef UNICODE
#define _tcssprintf wsprintf
#define tcsplitpath _wsplitpath
#else
#define _tcssprintf sprintf
#define tcsplitpath _splitpath
#endif
//-----------------------------------------------------------------------------
// GLOBALs
//-----------------------------------------------------------------------------
CMiniDumper *gs_pMiniDumper = NULL;
LPCRITICAL_SECTION gs_pCriticalSection = NULL;
//-----------------------------------------------------------------------------
// APIs
//-----------------------------------------------------------------------------
// Based on dbghelp.h
typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess,
DWORD dwPid,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
if (pModuleName == 0)
{
return FALSE;
}
WCHAR szFileName[_MAX_FNAME] = {0};
_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
if (_wcsicmp(szFileName, L"ntdll") == 0)
return TRUE;
return FALSE;
}
BOOL WINAPI MiniDumpCallback( PVOID pParam,
const PMINIDUMP_CALLBACK_INPUT pInput,
PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
if (pInput == 0 || pOutput == 0)
return FALSE;
switch (pInput->CallbackType)
{
case ModuleCallback:
if (pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
{
if (!IsDataSectionNeeded(pInput->Module.FullPath))
pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
}
return TRUE;
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Name: unhandledExceptionHandler()
// Desc: Call-back filter function for unhandled exceptions
//-----------------------------------------------------------------------------
LONG WINAPI UnhandledExceptionHandler(_EXCEPTION_POINTERS *pExceptionInfo)
{
if (NULL == gs_pMiniDumper)
return EXCEPTION_CONTINUE_SEARCH;
return gs_pMiniDumper->WriteMiniDump(pExceptionInfo);
}
// 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
void DisableSetUnhandledExceptionFilter()
{
HMODULE hModule = LoadLibrary(L"kernel32.dll");
void* pAddr = (void*)GetProcAddress(hModule, "SetUnhandledExceptionFilter");
if (pAddr)
{
unsigned char code[16] = { 0 };
int size = 0;
code[size++] = 0x33;
code[size++] = 0xC0;
code[size++] = 0xC2;
code[size++] = 0x04;
code[size++] = 0x00;
DWORD dwOldFlag = 0;
DWORD dwTempFlag = 0;
VirtualProtect(pAddr, size, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(GetCurrentProcess(), pAddr, code, size, NULL);
VirtualProtect(pAddr, size, dwOldFlag, &dwTempFlag);
}
FreeLibrary(hModule);
}
//-----------------------------------------------------------------------------
// Name: CreateInstance()
// Desc: Instanse gs_pMiniDumper
//-----------------------------------------------------------------------------
HRESULT CMiniDumper::CreateInstance()
{
if (NULL == gs_pMiniDumper)
{
gs_pMiniDumper = new CMiniDumper();
}
if (NULL == gs_pCriticalSection)
{
gs_pCriticalSection = new CRITICAL_SECTION;
InitializeCriticalSection(gs_pCriticalSection);
}
return(S_OK);
}
//-----------------------------------------------------------------------------
// Name: ReleaseInstance()
// Desc: Release gs_pMiniDumper
//-----------------------------------------------------------------------------
HRESULT CMiniDumper::ReleaseInstance()
{
if (NULL != gs_pMiniDumper)
{
delete gs_pMiniDumper;
gs_pMiniDumper = NULL;
}
if (NULL != gs_pCriticalSection)
{
DeleteCriticalSection(gs_pCriticalSection);
gs_pCriticalSection = NULL;
}
return(S_OK);
}
//-----------------------------------------------------------------------------
// Name: CMiniDumper()
// Desc: Constructor
//-----------------------------------------------------------------------------
CMiniDumper::CMiniDumper()
{
// 使应用程序能够取代每个进程和线程的顶级异常处理程序
::SetUnhandledExceptionFilter(UnhandledExceptionHandler);
DisableSetUnhandledExceptionFilter();
}
//-----------------------------------------------------------------------------
// Name: ~CMiniDumper()
// Desc: Destructor
//-----------------------------------------------------------------------------
CMiniDumper::~CMiniDumper(void)
{
}
//-----------------------------------------------------------------------------
// Name: setMiniDumpFileName()
// Desc:
//-----------------------------------------------------------------------------
void CMiniDumper::SetMiniDumpFileName(void)
{
time_t currentTime;
time(¤tTime);
_tcssprintf(m_szMiniDumpPath, _T("%s.%ld.dmp"), m_szAppPath, currentTime);
}
//-----------------------------------------------------------------------------
// Name: getImpersonationToken()
// Desc: The method acts as a potential workaround for the fact that the
// current thread may not have a token assigned to it, and if not, the
// process token is received.
//-----------------------------------------------------------------------------
BOOL CMiniDumper::GetImpersonationToken(HANDLE* phToken)
{
*phToken = NULL;
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
TRUE,
phToken))
{
if (GetLastError() == ERROR_NO_TOKEN)
{
// No impersonation token for the current thread is available.
// Let's go for the process token instead.
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
phToken))
return FALSE;
}
else
return FALSE;
}
return TRUE;
}
//-----------------------------------------------------------------------------
// Name: enablePrivilege()
// Desc: Since a MiniDump contains a lot of meta-data about the OS and
// application state at the time of the dump, it is a rather privileged
// operation. This means we need to set the SeDebugPrivilege to be able
// to call MiniDumpWriteDump.
//-----------------------------------------------------------------------------
BOOL CMiniDumper::EnablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld)
{
BOOL bOk = FALSE;
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
bOk = LookupPrivilegeValue(0, pszPriv, &tp.Privileges[0].Luid);
if (bOk)
{
DWORD cbOld = sizeof(*ptpOld);
bOk = AdjustTokenPrivileges(hToken, FALSE, &tp, cbOld, ptpOld, &cbOld);
}
return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
}
//-----------------------------------------------------------------------------
// Name: restorePrivilege()
// Desc:
//-----------------------------------------------------------------------------
BOOL CMiniDumper::RestorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld)
{
BOOL bOk = AdjustTokenPrivileges(hToken, FALSE, ptpOld, 0, NULL, NULL);
return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
}
//-----------------------------------------------------------------------------
// Name: writeMiniDump()
// Desc:
//-----------------------------------------------------------------------------
LONG CMiniDumper::WriteMiniDump(_EXCEPTION_POINTERS *pExceptionInfo)
{
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HANDLE hImpersonationToken = NULL;
if (!GetImpersonationToken(&hImpersonationToken))
return FALSE;
// You have to find the right dbghelp.dll.
// Look next to the EXE first since the one in System32 might be old (Win2k)
HMODULE hDll = NULL;
if (GetModuleFileName(NULL, m_szAppPath, _MAX_PATH))
{
wchar_t szDir[_MAX_DIR] = { 0 };
TCHAR szDbgHelpPath[MAX_PATH] = { 0 };
_wsplitpath(m_szAppPath, NULL, szDir, NULL, NULL);
_tcscpy(szDbgHelpPath, szDir);
_tcscat(szDbgHelpPath, _T("DBGHELP.DLL"));
hDll = ::LoadLibrary(szDbgHelpPath);
}
if (hDll == NULL)
{
// If we haven't found it yet - try one more time.
hDll = ::LoadLibrary(_T("DBGHELP.DLL"));
}
if (hDll)
{
// Get the address of the MiniDumpWriteDump function, which writes
// user-mode mini-dump information to a specified file.
MINIDUMPWRITEDUMP MiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
if (MiniDumpWriteDump != NULL)
{
SetMiniDumpFileName();
// Create the mini-dump file...
HANDLE hFile = ::CreateFile(m_szMiniDumpPath,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL;
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = 0;
// We need the SeDebugPrivilege to be able to run MiniDumpWriteDump
TOKEN_PRIVILEGES tp;
BOOL bPrivilegeEnabled = EnablePrivilege(SE_DEBUG_NAME, hImpersonationToken, &tp);
BOOL bOk;
// DBGHELP.dll is not thread-safe, so we need to restrict access...
EnterCriticalSection(gs_pCriticalSection);
{
// Write out the mini-dump data to the file...
bOk = MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
(NULL == pExceptionInfo) ? (NULL) : (&ExInfo),
NULL,
&mci);
}
LeaveCriticalSection(gs_pCriticalSection);
// Restore the privileges when done
if (bPrivilegeEnabled)
RestorePrivilege(hImpersonationToken, &tp);
if (bOk)
{
retval = EXCEPTION_EXECUTE_HANDLER;
}
::CloseHandle(hFile);
}
}
}
FreeLibrary(hDll);
if (NULL != pExceptionInfo)
{
TerminateProcess(GetCurrentProcess(), 0);
}
return retval;
}
测试
// Dumper.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "miniDump.h"
extern CMiniDumper *gs_pMiniDumper;
int _tmain(int argc, _TCHAR* argv[])
{
CMiniDumper::CreateInstance();
char *p = NULL;
*p = 'b';
/*__try
{
*p = 'b';
}
__except (gs_pMiniDumper->WriteMiniDump(GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
{
}*/
}
生成的dmp文件
sh