第25章 SEH结构化异常处理_未处理异常及向量化异常
Posted 浅墨浓香
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第25章 SEH结构化异常处理_未处理异常及向量化异常相关的知识,希望对你有一定的参考价值。
25.1 UnhandledExceptionFilter函数详解
25.1.1 BaseProcessStart伪代码(Kernel32内部)
void BaseProcessStart(PVOID lpfnEntryPoint) //参数为线程函数的入口地址 { DWORD retValue; DWORD currentESP; DWORD exceptionCode; currentESP = ESP; //lpfnEntryPoint被try/except封装着,这是系统安装的默认的异常处理程序,也是SEH链上最后一个异常处理程序 __try { NtSetInformationThread(GetCurrentThread(), ThreadQuerySetWin32StartAddress, &lpfnEntryPoint, sizeof(lpfnEntryPoint)); retValue = lpfnEntryPoint(); ExitThread(retValue); //如果异常,线程从这里退出! } __except ( //过滤器表达式代码 exceptionCode = GetExceptionInformation(), UnhandledExceptionFilter(GetExceptionInformation())) //出现异常会调用Unhandled...这个函数,该函数内部会调用
//用户通过SetUnhandledFilter设置的全局异常处理函数。 { //如果UnhandledExceptionFilter返回EXCEPTION_EXECUTE_HANDLER,则会控制流会执行到这里 ESP = currentESP;
if (!_BaseRunningInServerProcess) //普通进程,则退出进程 ExitProcess(exceptionCode); else // 线程是作为服务来运行的,只退出线程并不终止整个服务 ExitThread(exceptionCode); } }
(1)如果异常过滤程序返回EXCEPTION_CONTINUE_SEARCH时,系统会继续向外层寻找异常过滤程序。但如果每个异常过滤程序都返回EXCEPTION_CONTINUE_SEARCH时,会未到遇处理异常。
(2)调用SetUnhandledExceptionFilter安装用户提供的全局(顶层)异常过滤回调函数(为所有线程共享)。如果顶层异常回调函数返回EXCEPTION_EXECUTE_HANDLER或EXCEPTION_CONTINUE_SEARCH则直接传递给UnhandledExceptionFilter函数,UnhandledExceptionFilter根据这个返回值判断是终止进程还是重新执行异常代码。如果顶层异常回调函数返回EXCEPTION_CONTINUE_SEARCH,则接下来的要发生的事情就比较复杂(可参考后面的《UnhandledExceptionFilter内部工作流程》)
(3)SetUnhandledExceptionFilter返回值为上次安装的异常过滤程序的地址。如果使用C/C++运行库,则会默认安装一个__CxxUnhandledExceptionFilter过滤程序。该函数首先检查异常是不是C++异常,如果是则在结束时执行abort函数(该函数内部调用了UnhandledExceptionFilter函数,注意这可能会造成循环调用,因为UnhandledExceptionFilter内部调用了我们安装的全局异常过滤函数_CxxUnhandledExceptionFilter,而这个函数的内部又调用UnhandledExceptionFilter,为了防止无限递归调用,_CxxUnhandledExceptionFilter在调用UnhandledExceptionFilter之前会调用SetUnhandledExceptionFilter(NULL))。如果不是C++异常则返回EXCEPTION_CONTINUE_SEARCH。所以当我们调用SetUnhandled*函数,返回的地址为_CxxUnhandledExceptionFilter的地址。
(4)注意,在我们的顶层异常过滤函数里,在返回EXCEPTION_CONTINUE_SEARCH前,不应调用之前的全局异常过滤函数(即我们通过SetUnhandledExceptionFilter的返回值取得的那个函数)。因为如果这个函数是在某个动态库里,那它随时都可能被卸载了。
(5)如果SetUnhandledExceptionFilter(NULL),则取消我们设置的全局异常过滤函数。
25.1.2 UnhandledExceptionFilter内部工作流程
①判断是否因为对资源进行写入操作引发的异常。如果是,将资源的只读属性改为可写入,并返回EXCEPTION_CONTINUE_EXECUTION以允许失败的指令再次执行。
②确定进程是否被调试。如果被调试,就返回EXCEPTION_CONTINUE_SEARCH给调试器,通知调试器定位异常指令,并告知我们出了什么样的异常。
③调用我们设置的顶层异常过滤函数(如果存在的话)。如果顶层过滤函数返回EXCEPT_EXECUTE_HANDLER或EXCEPTION_CONTINUE_EXECUTION,将直接传递给UnhandledExceptionFilter,由它将返回值给系统。如果返回EXCEPT_CONTINUE_SEARCH,则跳到第④步。
④再次将未处理异常报告给调试器
⑤终止进程:如果线程调用SetErrorMode并设置SEM_NOGPFAULTERRORBOX标志,那么UnhandledExceptFilter会返回EXCEPTION_EXECUTE_HANDLER,在未处理异常的情况下进行全局展开并执行未执行的finally块,然后进程终止。
如果没有调用SetErrorMode函数,UnhandledExceptionFilter会返回EXCEPTION_CONTINUE_SEARCH。于是系统内核得到程序控制,它将通过ALPC(高级本地过程调用)机制将异常通知给WerSvc(Windows错误报告专用服务),然后ALPC先阻塞自己的线程,直到WerSvc执行完毕。
⑥UnhandledExceptionFilter与WER的交互
当WerSvc接到通知时,会先调用CreateProcess来启动WerFault.exe,然后 WerSvc会等待这个新进程的结束。而WerFault.exe会向我们创建上面的两个对话框以报告错误的发生。当第1个对话框出现时,可以选择“取消”来终止我们的应用程序,否则过一会儿,会弹出第2个对话框,如果我们选择“关闭程序”,则WerFault.exe会调用TerminateProcess来结束我们的应用程序。如果选择“调试”,WerFault.exe会创建一个子进程(调试器),让他附着在出错的程序上进行“即时调试”
25.2 即时调试
(1)默认调试器:HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug子项下有一个名为Debugger的值,系统通过个值找到调试器。
(2)WerFault.exe会给这个调试器传入两个参数:要调试的进程ID和继承过来的事件句柄(这个句柄由WerSvc服务创建用于通知被调试进程调试也结束)
(3)通过将调试器附着到被调试进程,可以查看全局、局部和静态变量的值,也可以设置断点,检查函数调用树等调试工作。
【Spreadsheet程序】通过SEH向预订的地址空间稀疏调拨存储器
25.3 向量化异常和继续处理程序
25.3.1向量化异常(vectored exception handing,VEH)——在SEH前被调用
①对于多层嵌套的SEH来说,外层的__try_except语句块可能没有机会处理被内层嵌套拦截的异常。对于一般软件来说,这不是太大的问题,但是当内层嵌套的软件是第三方的库函数,并且内部以不友好的方式处理了异常,比如:异常退出进程了事,这对整个程序将造成很不利的影响。
②此时可以利用向量化异常处理,在正常的SEH之前以合适的方式拦截和处理异常。
③当异常发生时,系统在执行SEH过滤程序之前,会先依次调用VEH列表中的每个VEH异常处理函数。
(2)注册VEH异常处理程序:AddVectoredExceptionHandler(bFirstInTheList,pfnHandler)
①参数bFirstInTheList为0表示pfnHandler被添加到列表尾端,非0在列表头部。
②pfnHandler:异常处理函数,如果返回EXCEPTION_CONTINUE_SEARCH,则重新执行导致异常的指令,如果返回EXCEPTION_CONTINUE_SEARCH表示让VEH链表中的其他函数去处理异常,如果所有函数都返回EXCEPTION_CONTINUE_SEARCH,SEH过滤函数就会被执行。
(3)删除VEH异常处理函数:RemoveVectoredExceptionHandler(pHandler),其中pHandler这个句柄为AddVectoredExceptionHandler的返回值。
25.3.2 继续处理程序:——用于实现程序的诊断和跟踪
(1)安装:PVOID AddVectoredContinueHandler(bFirstInTheList,pfnHandler);
①参数bFirstInTheList为0,表示安装在继续处理程序列头的尾部,非0在头部。
②通过该函数安装的异常处理程序是在SetUnhandledExceptionFilter安装的异常处理程序返回EXCEPTION_CONTINUE_SEARCH之后才被调用。
③如果pfnHandler函数返回EXCEPTION_CONTINUE_EXECUTION重新执行导致异常的指令,EXCEPTION_CONTINUE_SEARCH让系统执行它后面的异常处理程序。
(2)删除:RemoveVectoredContinueHandler(pHandler);
以上是关于第25章 SEH结构化异常处理_未处理异常及向量化异常的主要内容,如果未能解决你的问题,请参考以下文章