当前位置:   article > 正文

C++ 记录Windows程序崩溃时的dumpfile_电骡 cminidumper

电骡 cminidumper
【原理】
      windows程序当遇到异常,没有try-catch或者try-catch也无法捕获到的异常时,程序就会自动退出,如果这时候没有dump文件的话,我们是没有得到任何程序退出的信息。在windows程序异常退出之前,会预先调用一个在程序中注册的异常处理回调函数(默认是没有设置),只要我们在这个回调函数中调用MiniDumpWriteDump函数就可以产生我们想要的dump文件。


【实现】

1.调用SetUnhandledExceptionFilter注册一个自定义的异常处理回调函数
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

2.CreateFile创建dump文件,调用MiniDumpWriteDump函数往dump文件写异常信息


【实现】

  1. #pragma once
  2. #include <windows.h>
  3. class CMiniDumper
  4. {
  5. public:
  6. static HRESULT CreateInstance();
  7. static HRESULT ReleaseInstance();
  8. public:
  9. LONG WriteMiniDump(_EXCEPTION_POINTERS *pExceptionInfo);
  10. private:
  11. void SetMiniDumpFileName(void);
  12. BOOL GetImpersonationToken(HANDLE* phToken);
  13. BOOL EnablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
  14. BOOL RestorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
  15. private:
  16. CMiniDumper();
  17. virtual ~CMiniDumper(void);
  18. private:
  19. TCHAR m_szMiniDumpPath[MAX_PATH];
  20. TCHAR m_szAppPath[MAX_PATH];
  21. };
  1. #include "stdafx.h"
  2. #include <windows.h>
  3. #include <stdio.h>
  4. #include <assert.h>
  5. #include <time.h>
  6. #include <tchar.h>
  7. #include <dbghelp.h>
  8. #include "miniDump.h"
  9. #ifdef UNICODE
  10. #define _tcssprintf wsprintf
  11. #define tcsplitpath _wsplitpath
  12. #else
  13. #define _tcssprintf sprintf
  14. #define tcsplitpath _splitpath
  15. #endif
  16. //-----------------------------------------------------------------------------
  17. // GLOBALs
  18. //-----------------------------------------------------------------------------
  19. CMiniDumper *gs_pMiniDumper = NULL;
  20. LPCRITICAL_SECTION gs_pCriticalSection = NULL;
  21. //-----------------------------------------------------------------------------
  22. // APIs
  23. //-----------------------------------------------------------------------------
  24. // Based on dbghelp.h
  25. typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess,
  26. DWORD dwPid,
  27. HANDLE hFile,
  28. MINIDUMP_TYPE DumpType,
  29. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  30. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  31. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
  32. BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
  33. {
  34. if (pModuleName == 0)
  35. {
  36. return FALSE;
  37. }
  38. WCHAR szFileName[_MAX_FNAME] = {0};
  39. _wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
  40. if (_wcsicmp(szFileName, L"ntdll") == 0)
  41. return TRUE;
  42. return FALSE;
  43. }
  44. BOOL WINAPI MiniDumpCallback( PVOID pParam,
  45. const PMINIDUMP_CALLBACK_INPUT pInput,
  46. PMINIDUMP_CALLBACK_OUTPUT pOutput)
  47. {
  48. if (pInput == 0 || pOutput == 0)
  49. return FALSE;
  50. switch (pInput->CallbackType)
  51. {
  52. case ModuleCallback:
  53. if (pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
  54. {
  55. if (!IsDataSectionNeeded(pInput->Module.FullPath))
  56. pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
  57. }
  58. return TRUE;
  59. case IncludeModuleCallback:
  60. case IncludeThreadCallback:
  61. case ThreadCallback:
  62. case ThreadExCallback:
  63. return TRUE;
  64. default:;
  65. }
  66. return FALSE;
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Name: unhandledExceptionHandler()
  70. // Desc: Call-back filter function for unhandled exceptions
  71. //-----------------------------------------------------------------------------
  72. LONG WINAPI UnhandledExceptionHandler(_EXCEPTION_POINTERS *pExceptionInfo)
  73. {
  74. if (NULL == gs_pMiniDumper)
  75. return EXCEPTION_CONTINUE_SEARCH;
  76. return gs_pMiniDumper->WriteMiniDump(pExceptionInfo);
  77. }
  78. // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
  79. void DisableSetUnhandledExceptionFilter()
  80. {
  81. HMODULE hModule = LoadLibrary(L"kernel32.dll");
  82. void* pAddr = (void*)GetProcAddress(hModule, "SetUnhandledExceptionFilter");
  83. if (pAddr)
  84. {
  85. unsigned char code[16] = { 0 };
  86. int size = 0;
  87. code[size++] = 0x33;
  88. code[size++] = 0xC0;
  89. code[size++] = 0xC2;
  90. code[size++] = 0x04;
  91. code[size++] = 0x00;
  92. DWORD dwOldFlag = 0;
  93. DWORD dwTempFlag = 0;
  94. VirtualProtect(pAddr, size, PAGE_READWRITE, &dwOldFlag);
  95. WriteProcessMemory(GetCurrentProcess(), pAddr, code, size, NULL);
  96. VirtualProtect(pAddr, size, dwOldFlag, &dwTempFlag);
  97. }
  98. FreeLibrary(hModule);
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Name: CreateInstance()
  102. // Desc: Instanse gs_pMiniDumper
  103. //-----------------------------------------------------------------------------
  104. HRESULT CMiniDumper::CreateInstance()
  105. {
  106. if (NULL == gs_pMiniDumper)
  107. {
  108. gs_pMiniDumper = new CMiniDumper();
  109. }
  110. if (NULL == gs_pCriticalSection)
  111. {
  112. gs_pCriticalSection = new CRITICAL_SECTION;
  113. InitializeCriticalSection(gs_pCriticalSection);
  114. }
  115. return(S_OK);
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Name: ReleaseInstance()
  119. // Desc: Release gs_pMiniDumper
  120. //-----------------------------------------------------------------------------
  121. HRESULT CMiniDumper::ReleaseInstance()
  122. {
  123. if (NULL != gs_pMiniDumper)
  124. {
  125. delete gs_pMiniDumper;
  126. gs_pMiniDumper = NULL;
  127. }
  128. if (NULL != gs_pCriticalSection)
  129. {
  130. DeleteCriticalSection(gs_pCriticalSection);
  131. gs_pCriticalSection = NULL;
  132. }
  133. return(S_OK);
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Name: CMiniDumper()
  137. // Desc: Constructor
  138. //-----------------------------------------------------------------------------
  139. CMiniDumper::CMiniDumper()
  140. {
  141. // 使应用程序能够取代每个进程和线程的顶级异常处理程序
  142. ::SetUnhandledExceptionFilter(UnhandledExceptionHandler);
  143. DisableSetUnhandledExceptionFilter();
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Name: ~CMiniDumper()
  147. // Desc: Destructor
  148. //-----------------------------------------------------------------------------
  149. CMiniDumper::~CMiniDumper(void)
  150. {
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Name: setMiniDumpFileName()
  154. // Desc:
  155. //-----------------------------------------------------------------------------
  156. void CMiniDumper::SetMiniDumpFileName(void)
  157. {
  158. time_t currentTime;
  159. time(¤tTime);
  160. _tcssprintf(m_szMiniDumpPath, _T("%s.%ld.dmp"), m_szAppPath, currentTime);
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Name: getImpersonationToken()
  164. // Desc: The method acts as a potential workaround for the fact that the
  165. // current thread may not have a token assigned to it, and if not, the
  166. // process token is received.
  167. //-----------------------------------------------------------------------------
  168. BOOL CMiniDumper::GetImpersonationToken(HANDLE* phToken)
  169. {
  170. *phToken = NULL;
  171. if (!OpenThreadToken(GetCurrentThread(),
  172. TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
  173. TRUE,
  174. phToken))
  175. {
  176. if (GetLastError() == ERROR_NO_TOKEN)
  177. {
  178. // No impersonation token for the current thread is available.
  179. // Let's go for the process token instead.
  180. if (!OpenProcessToken(GetCurrentProcess(),
  181. TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
  182. phToken))
  183. return FALSE;
  184. }
  185. else
  186. return FALSE;
  187. }
  188. return TRUE;
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Name: enablePrivilege()
  192. // Desc: Since a MiniDump contains a lot of meta-data about the OS and
  193. // application state at the time of the dump, it is a rather privileged
  194. // operation. This means we need to set the SeDebugPrivilege to be able
  195. // to call MiniDumpWriteDump.
  196. //-----------------------------------------------------------------------------
  197. BOOL CMiniDumper::EnablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld)
  198. {
  199. BOOL bOk = FALSE;
  200. TOKEN_PRIVILEGES tp;
  201. tp.PrivilegeCount = 1;
  202. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  203. bOk = LookupPrivilegeValue(0, pszPriv, &tp.Privileges[0].Luid);
  204. if (bOk)
  205. {
  206. DWORD cbOld = sizeof(*ptpOld);
  207. bOk = AdjustTokenPrivileges(hToken, FALSE, &tp, cbOld, ptpOld, &cbOld);
  208. }
  209. return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Name: restorePrivilege()
  213. // Desc:
  214. //-----------------------------------------------------------------------------
  215. BOOL CMiniDumper::RestorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld)
  216. {
  217. BOOL bOk = AdjustTokenPrivileges(hToken, FALSE, ptpOld, 0, NULL, NULL);
  218. return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Name: writeMiniDump()
  222. // Desc:
  223. //-----------------------------------------------------------------------------
  224. LONG CMiniDumper::WriteMiniDump(_EXCEPTION_POINTERS *pExceptionInfo)
  225. {
  226. LONG retval = EXCEPTION_CONTINUE_SEARCH;
  227. HANDLE hImpersonationToken = NULL;
  228. if (!GetImpersonationToken(&hImpersonationToken))
  229. return FALSE;
  230. // You have to find the right dbghelp.dll.
  231. // Look next to the EXE first since the one in System32 might be old (Win2k)
  232. HMODULE hDll = NULL;
  233. if (GetModuleFileName(NULL, m_szAppPath, _MAX_PATH))
  234. {
  235. wchar_t szDir[_MAX_DIR] = { 0 };
  236. TCHAR szDbgHelpPath[MAX_PATH] = { 0 };
  237. _wsplitpath(m_szAppPath, NULL, szDir, NULL, NULL);
  238. _tcscpy(szDbgHelpPath, szDir);
  239. _tcscat(szDbgHelpPath, _T("DBGHELP.DLL"));
  240. hDll = ::LoadLibrary(szDbgHelpPath);
  241. }
  242. if (hDll == NULL)
  243. {
  244. // If we haven't found it yet - try one more time.
  245. hDll = ::LoadLibrary(_T("DBGHELP.DLL"));
  246. }
  247. if (hDll)
  248. {
  249. // Get the address of the MiniDumpWriteDump function, which writes
  250. // user-mode mini-dump information to a specified file.
  251. MINIDUMPWRITEDUMP MiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
  252. if (MiniDumpWriteDump != NULL)
  253. {
  254. SetMiniDumpFileName();
  255. // Create the mini-dump file...
  256. HANDLE hFile = ::CreateFile(m_szMiniDumpPath,
  257. GENERIC_WRITE,
  258. FILE_SHARE_WRITE,
  259. NULL,
  260. CREATE_ALWAYS,
  261. FILE_ATTRIBUTE_NORMAL,
  262. NULL);
  263. if (hFile != INVALID_HANDLE_VALUE)
  264. {
  265. _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
  266. ExInfo.ThreadId = ::GetCurrentThreadId();
  267. ExInfo.ExceptionPointers = pExceptionInfo;
  268. ExInfo.ClientPointers = NULL;
  269. MINIDUMP_CALLBACK_INFORMATION mci;
  270. mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
  271. mci.CallbackParam = 0;
  272. // We need the SeDebugPrivilege to be able to run MiniDumpWriteDump
  273. TOKEN_PRIVILEGES tp;
  274. BOOL bPrivilegeEnabled = EnablePrivilege(SE_DEBUG_NAME, hImpersonationToken, &tp);
  275. BOOL bOk;
  276. // DBGHELP.dll is not thread-safe, so we need to restrict access...
  277. EnterCriticalSection(gs_pCriticalSection);
  278. {
  279. // Write out the mini-dump data to the file...
  280. bOk = MiniDumpWriteDump(GetCurrentProcess(),
  281. GetCurrentProcessId(),
  282. hFile,
  283. MiniDumpNormal,
  284. (NULL == pExceptionInfo) ? (NULL) : (&ExInfo),
  285. NULL,
  286. &mci);
  287. }
  288. LeaveCriticalSection(gs_pCriticalSection);
  289. // Restore the privileges when done
  290. if (bPrivilegeEnabled)
  291. RestorePrivilege(hImpersonationToken, &tp);
  292. if (bOk)
  293. {
  294. retval = EXCEPTION_EXECUTE_HANDLER;
  295. }
  296. ::CloseHandle(hFile);
  297. }
  298. }
  299. }
  300. FreeLibrary(hDll);
  301. if (NULL != pExceptionInfo)
  302. {
  303. TerminateProcess(GetCurrentProcess(), 0);
  304. }
  305. return retval;
  306. }
测试

  1. // Dumper.cpp : 定义控制台应用程序的入口点。
  2. #include "stdafx.h"
  3. #include "miniDump.h"
  4. extern CMiniDumper *gs_pMiniDumper;
  5. int _tmain(int argc, _TCHAR* argv[])
  6. {
  7. CMiniDumper::CreateInstance();
  8. char *p = NULL;
  9. *p = 'b';
  10. /*__try
  11. {
  12. *p = 'b';
  13. }
  14. __except (gs_pMiniDumper->WriteMiniDump(GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
  15. {
  16. }*/
  17. }

生成的dmp文件

sh


声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号