#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
void CreateDumpFile(const TCHAR *lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
{
// 创建Dump文件
HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Dump信息
//
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = TRUE;
// 写入Dump文件内容
//
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
CloseHandle(hDumpFile);
}
// 处理Unhandled Exception的回调函数
//
LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException)
{
// 在这里添加处理程序崩溃情况的代码
// 现在很多软件都是弹出一个发送错误报告的对话框
// 这里以弹出一个错误对话框并退出程序为例子
CreateDumpFile(TEXT("test.dmp"), pException);
FatalAppExit(-1, TEXT("*** Unhandled Exception! ***"));
return EXCEPTION_EXECUTE_HANDLER;
}
在程序启动时注册处理函数
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationCrashHandler);
注意 Release版本,需要配置:
-
c/c++
>优化
>优化
选择已禁用/Od
-
c/c++
>常规
>调试信息格式
选择程序数据库 (/Zi)
-
链接器
>调试
>生成调试信息
选择优化以便于调试 (/DEBUG)
-
链接器
>调试
>生成程序数据库文件
要注意生成出来的pdb
文件最好与dump文件在同一目录
UCRT hook
windows下,ucrt内部的异常不会走SetUnhandledExceptionFilter. 因为新版本的CRT实现在异常处理中强制删除所有应用程序先前设置的捕获函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
#include <stdlib.h>
#include <windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
bool CreateDumpFile(const TCHAR *lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
{
printf("CreateDumpFile\n");
// 创建Dump文件
HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE) {
printf("CreateDumpFile fail\n");
return false;
}
printf("CreateDumpFile succ\n");
// Dump信息
//
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = TRUE;
printf("MiniDumpWriteDump...\n");
// 写入Dump文件内容
//
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
printf("MiniDumpWriteDump\n");
CloseHandle(hDumpFile);
printf("CloseHandle\n");
return true;
}
// 处理Unhandled Exception的回调函数
//
LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException)
{
printf("ApplicationCrashHandler\n");
// 在这里添加处理程序崩溃情况的代码
// 现在很多软件都是弹出一个发送错误报告的对话框
// 这里以弹出一个错误对话框并退出程序为例子
if (CreateDumpFile(TEXT("test.dmp"), pException)) {
printf("dump succ\n");
FatalAppExit(-1, TEXT("*** Unhandled Exception! dmp created ***"));
}
else {
printf("dump fail\n");
FatalAppExit(-1, TEXT("*** Unhandled Exception! dmp create fail ***"));
}
return EXCEPTION_EXECUTE_HANDLER;
}
class Hi {
public:
void say() {
printf("age=%d\n", age);
}
int age = 10;
};
// 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
bool DisableSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibraryW(L"kernel32.dll");
if (hKernel32 == NULL) return FALSE;
void *addr = (void*)::GetProcAddress(hKernel32,
"SetUnhandledExceptionFilter"); // The function is ANSI version.
if (addr)
{
#ifdef _M_IX86
// Code for x86:
// 33 C0 xor eax,eax
// C2 04 00 ret 4
unsigned char szExecute[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 };
#elif _M_X64
// 33 C0 xor eax,eax
// C3 ret
unsigned char szExecute[] = { 0x33, 0xC0, 0xC3 };
#else
#error "The following code only works for x86 and x64!"
#endif
SIZE_T bytesWritten = 0;
DWORD dwOldFlag, dwTempFlag;
VirtualProtect(addr, sizeof(szExecute), PAGE_EXECUTE_READWRITE, &dwOldFlag);
BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
addr, szExecute, sizeof(szExecute), &bytesWritten);
VirtualProtect(addr, sizeof(szExecute), dwOldFlag, &dwTempFlag);
return bRet;
}
return false;
}
void test() {
Hi *hi = NULL;
//hi->say();
char data[10];
//auto data = new char[10];
char tmp[5500] = { 1 };
tmp[5000] = 0;
data[30] = 10;
sprintf(data, "lasdjfjal;fjasdlfjalfjalsdfja;sdfjaslkfjasdklfjasdl;fjasdl;fjasdl;fjsdlf%s\n", tmp);
printf("%s\n", data);
printf("hi\n");
}
int main() {
printf("main\n");
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationCrashHandler);
bool succ = DisableSetUnhandledExceptionFilter();
//printf("disable succ=%d\n", succ);
printf("start test\n");
test();
printf("lala\n");
system("pause");
return 0;
}
内存越界
内存越界,崩不崩溃都有可能,也不一定能走SetUnhandledExceptionFilter
。
对于堆内存越界,可以使用gflags
的完全页堆
方案找出。
原理是分配内存后跟不可访问访问内存区,这样内存越界时,就会触发崩溃。
但是对于栈内存越界,我没有找到好的方案。
网友评论