SetUnhandledExceptionFilter SEH无法捕获printf等CRT函数异常的问题

通常我们通过SetUnhandledExceptionFilter来捕获程序的异常进而得到相应的dump文件,就像下面:

LONG NTAPI TopLevelExcepFilter(EXCEPTION_POINTERS *pExcepInfo)
{
	printf("TopLevelEHandler\n");
	return EXCEPTION_CONTINUE_EXECUTION;
}
SetUnhandledExceptionFilter(&TopLevelExcepFilter);

但是对于部分CRT函数比如printf类不定参数类型的函数的异常是捕获不到的,比如:

printf(NULL);

这个动作是会导致程序崩溃的,但是SEH并没有捕获到,但是我们可以通过crt的另一个函数来设置异常检测的回调:_set_invalid_parameter_handler

_invalid_parameter_handler oldHandler;
oldHandler = _set_invalid_parameter_handler(myInvalidParameterHandler);
void myInvalidParameterHandler(const wchar_t* expression,
							   const wchar_t* function, 
							   const wchar_t* file, 
							   unsigned int line, 
							   uintptr_t pReserved)
{
	// function、file、line在Release下无效
	wprintf(L"Invalid parameter detected in function %s."
		L" File: %s Line: %d\n", function, file, line);
	wprintf(L"Expression: %s\n", expression);
	// 必须抛出异常,否则无法定位错误位置
	throw 1;
}

这样再次调用printf(NULL);就会定位到myInvalidParameterHandler函数中来了,但是只有debug下才能看到参数中的function、file、line
我们在回调函数中抛出异常,这样外面的SEH设置的回调函数就可以将dmp输出了。
 

同样对于程序的纯虚函数调用SEH也是无法捕获到的,我们同样需要通过CRT函数中_set_purecall_handler来设置异常回调:

_purecall_handler old_pure_handle;
old_pure_handle = _set_purecall_handler(myPurecallHandler);
void myPurecallHandler(void)
{
	printf("In _purecall_handler.");
	// 必须抛出异常,否则无法定位错误位置
	throw 1;
}

 

而至于为什么SEH函数无法捕获他们的异常呢,是因为这些函数内部在有异常发生的时候,会自调用SetUnhandledExceptionFilter这个函数,我们知道这个函数是注册了一个异常调用传递链表,每次调用这个函数,都会把新的回调函数放到链表的头部,系统只会把异常传递给给这个链表的表头,而第一个接收到异常的处理函数可以继续将异常传递给链表后面的异常处理函数去继续处理,也可以直接截断。

       CRT调用SEH的代码如下:

/* 代码来源于 gs_report.c */
/* Make sure any filter already in place is deleted. */
SetUnhandledExceptionFilter(NULL);
UnhandledExceptionFilter(&ExceptionPointers);

     所以我们可以调用完这个函数后,就把这个hook住,或者disable掉,这样就可以防止其他地方再去调用这个函数了。

// 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
void DisableSetUnhandledExceptionFilter()
{
#define X86_NOP                0x90
	void* addr = (void*)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");

	if (addr)
	{
		unsigned char code[16];
		int size = 0;

		code[size++] = 0x33;
		code[size++] = 0xC0;
		code[size++] = 0xC2;
		code[size++] = 0x04;
		code[size++] = 0x00;

		DWORD dwOldFlag, dwTempFlag;
		VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &dwOldFlag);
		WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
		VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
	}
}

 

还有一个CRT函数是terminate(),我们可以通过set_terminate来获得回调:

 

#include "stdafx.h"
#include <cstdio>
#include <windows.h>
#include <iostream>
void my_terminate_handler()
{
	// Abnormal program termination (terminate() function was called)
	// Do something here
	std::cout << "terminate.\n";
	exit(1);
}
void main()
{
	set_terminate(my_terminate_handler);
	terminate();
}

 

 

 

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Qt中,可以使用QApplication类的setUnhandledExceptionFilter函数来设置全局异常处理函数。具体步骤如下: 1. 创建一个全局异常处理函数,该函数需要接收一个std::exception指针作为参数,如下所示: ``` void myExceptionHandler(std::exception *e) { qDebug() << "Exception caught: " << e->what(); } ``` 2. 在应用程序的main函数中,调用QApplication类的setUnhandledExceptionFilter函数并传入全局异常处理函数,如下所示: ``` int main(int argc, char *argv[]) { QApplication app(argc, argv); qInstallMessageHandler(myMessageHandler); QApplication::setUnhandledExceptionFilter(myExceptionHandler); // 设置全局异常处理函数 // 其他代码... return app.exec(); } ``` 在上述代码中,我们先调用了qInstallMessageHandler函数,设置了一个自定义的消息处理函数myMessageHandler,以便在程序运行过程中可以将一些调试信息输出到控制台。然后,我们调用QApplication类的setUnhandledExceptionFilter函数,将全局异常处理函数myExceptionHandler传入。 当程序中发生未捕获异常时,该异常会被传递给myExceptionHandler函数进行处理。在该函数中,我们可以打印出异常信息,或者进行一些其他的处理操作。 注意:在Qt中,如果一个异常没有被捕获,那么程序将会调用terminate函数结束运行,因此我们必须在全局异常处理函数中尽可能地处理异常,避免程序异常终止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值