实现一个可以生成dmp文件的代码
代码可直接复制、编译
#include <iostream>
#include <windows.h>
#include <dbghelp.h>
#pragma comment (lib, "dbghelp.lib")
#include <conio.h>
#include <tchar.h>
using namespace std;
/* 未捕获异常过滤函数 */
LONG WINAPI ExpFilter(struct _EXCEPTION_POINTERS* pExp) {
HANDLE hFile = CreateFile(
_T("C:\\Users\\win10\\Desktop\\test.dmp"),/* 自定义 生成dmp文件的路径 */
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE != hFile) {
MINIDUMP_EXCEPTION_INFORMATION einfo;
einfo.ThreadId = GetCurrentThreadId();
einfo.ExceptionPointers = pExp;
einfo.ClientPointers = FALSE;
MiniDumpWriteDump( /* 需包含头文件#include <dbghelp.h> 与链接文件#pragma comment (lib, "dbghelp.lib") */
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpWithFullMemory,/* 获取所有内存dmp */
&einfo,
NULL,
NULL);
CloseHandle(hFile);
}
return EXCEPTION_EXECUTE_HANDLER;
}
/* 开启未捕获异常过滤函数 修改_XcptFilter硬编码,直接返回EXCEPTION_CONTINUE_SEARCH */
void StartUnhandledExceptionFilter() {
/*
替换调用进程中所有已有线程和所有将来新创建线程的顶级异常过滤函数
调用此函数后,如果在未调试的进程中发生异常,并且这些异常没有被捕获时,
系统就会调用SetUnhandledExceptionFilter设置的函数
*/
SetUnhandledExceptionFilter(ExpFilter);
/*
链接器在链接可执行文件时,选择正确的C/C++运行库运行函数
在此运行库函数中调用的main函数
查看调用堆栈 可以看到 此运行库函数为 mainCRTStartup
调用main函数相关代码如下:
try {
...
调用main函数
...
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation())) {
_exit( GetExceptionCode() );
}
产生异常时,首先会调用_XcptFilter,之后才会被 ExpFilter 处理,堆栈可能会产生变动
修改_XcptFilter,让其直接返回 EXCEPTION_CONTINUE_SEARCH(0),
表示异常没有被识别到,异常继续往上层抛,直到SetUnhandledExceptionFilter设置的未捕获异常过滤函数
*/
HMODULE hModule = LoadLibrary(_T("msvcrt.dll"));
if (NULL != hModule) {
void* _XcptFilter = (void*)GetProcAddress(hModule, "_XcptFilter");
if (NULL != _XcptFilter) {
DWORD dwOldProtect;
VirtualProtect(_XcptFilter, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
VirtualProtect(_XcptFilter, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
*(char*)_XcptFilter = 0x33;
*((char*)_XcptFilter + 1) = 0xc0;
*((char*)_XcptFilter + 2) = 0xc2;
*((char*)_XcptFilter + 3) = 0x00;
*((char*)_XcptFilter + 4) = 0x00;
VirtualProtect(_XcptFilter, 5, dwOldProtect, &dwOldProtect);
}
}
}
int main() {
cout << "begin" << endl;
StartUnhandledExceptionFilter();
int i = 0;
i = i / i; /* 产生一个未处理的除零异常 */
cout << "end" << endl;
return 0;
}
WindbgPreview 分析dmp 文件
WindbgPreview —> 左上角 文件 —> Start debugging —> Open dump file —> 找到dmp文件 —> 打开
正常打开完毕,应该是这样的界面:
设置pdb符号路径 与 源码路径
这里需要注意到一个问题:
如果源代码并不是当初编译此程序的那份源代码,WindbgPreview展示的异常位置可能有误。
WindbgPreview的自动化分析
- 进入符号模糊匹配模式:!sym noisy on
- 加载pdb:.reload /i
- 自动化分析:!analyze -v
排查问题
- 阅读附近代码,我们会发现 i = i / i 被除数为0,修改代码解决问题
- 用WindbgPreview继续排查
- 切换到异常线程:.ecxr
- 打印异常堆栈:kvn
进入到了刚才的问题现场,这时我们再输入 r 命令,接着输入u命令(目的是为了多打印一些崩溃点代码上下文),看崩溃现场详细信息:
我们可以看到 idiv eax,dword ptr [ebp-8] 有符号除法,这里为 eax 除以 ebp - 8 位置的值
可以看到 [ebp-8] ss:002b:0114f97c=00000000
被除数为0,所以出错
参考
https://zhuanlan.kanxue.com/article-364.htm
https://blog.csdn.net/u010588861/article/details/41350647