【原理】
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
时间: 2024-10-19 18:22:10