C++ 記錄Windows程式崩潰時的dumpfile
阿新 • • 發佈:2019-02-14
【原理】
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