写程序的时候避免不了出现异常或Bug,windows是怎么保证异常可以被try_catch,或者调试器捕获并处理的呢?如果异常不能被处理,windows会怎么做呢?
这要从windows异常处理器说起。异常处理器顾名思义,专门处理各类异常的。异常处理器按照内核和用户模式分成了两类,处理过程不尽相同。
内核模式异常处理
- 内核调试器第一次处理该异常
- 如果存在内核调试器,则交给它处理。如果异常被处理,则直接跳到5
- 如果存在内核调试器,则交给它处理。如果异常被处理,则直接跳到5
- 基于帧的异常处理器处理该异常
- 遍历结构化异常处理(SEH)链表,执行例程
- 如果有例程识别该异常(返回EXCEPTION_EXECUTE_HANDLER),则跳转到__except块中继续执行
- 如果有例程忽略了该异常(返回EXCEPTION_CONTINUE_EXECUTION)
- 如果(ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0,则构建一个新的异常(当前异常会放在新异常的ExceptionRecord域中),并再次调用异常处理器(这时,异常处理器从3处开始执行)
- 否则,跳到5处执行
- 如果最后一个例程PspUnhandledExceptionInSystemThread被调用,则直接蓝屏:KMODE_EXCEPTION_NOT_HANDLED
- 遍历结构化异常处理(SEH)链表,执行例程
- 内核调试器第二次处理该异常
- 如果存在内核调试器,则再次交给它处理。如果异常被处理,则直接跳到5
- 如果存在内核调试器,则再次交给它处理。如果异常被处理,则直接跳到5
- 异常未处理,则蓝屏
- 没人能处理该异常,则系统直接蓝屏.蓝屏代号:KMODE_EXCEPTION_NOT_HANDLED
- 没人能处理该异常,则系统直接蓝屏.蓝屏代号:KMODE_EXCEPTION_NOT_HANDLED
- 异常正确处理,则继续执行
- 恢复线程执行环境,退出调试器
- 恢复线程执行环境,退出调试器
用户模式异常处理
- 内核调试器处理该异常
- 如果不存在用户调试器且存在内核调试器,则交给内核调试器处理该异常。如果异常被处理,则直接跳到7
- 如果不存在用户调试器且存在内核调试器,则交给内核调试器处理该异常。如果异常被处理,则直接跳到7
- 用户调试器第一次处理该异常
- 如果存在用户调试器,即调试端口(debugPort)域不为空,则向调试端口发送该异常,并等待处理结果
- 如果用户调试器处理了该异常,则直接跳到7
- 基于帧的异常处理器处理该异常
- 由于基于帧的异常处理器是在用户态的ntdll中的KiUserExceptionDispatcher函数,所以会退出异常处理器,进入用户态执行KiUserExceptionDispatcher函数。
- 下面是KiUserExceptionDispatcher函数执行流程
- 如果存在向量化异常(VEH)处理例程(AddVectoredExceptionHandler),遍历VEH链表,循环执行例程
- 该处理例程只能返回两种状态:异常不被识别(EXCEPTION_CONTINUE_SEARCH);异常被忽略(EXCEPTION_CONTINUE_EXECUTION)
- 如果所有的例程都返回EXCEPTION_CONTINUE_SEARCH,则交由SEH处理
- 如果有例程返回EXCEPTION_CONTINUE_EXECUTION,则交由NtContinue(再次进入内核态)函数恢复线程执行环境,在原指令处继续执行。
- 遍历结构化异常处理(SEH)链表
- 如果有例程识别该异常(返回EXCEPTION_EXECUTE_HANDLER),则跳转到__except块中继续执行。(不会进入内核)
- 如果有例程忽略了该异常(返回EXCEPTION_CONTINUE_EXECUTION)
- 如果(ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0,则构建一个新的异常(当前异常会放在新异常的ExceptionRecord域中),并再次调用异常处理器(进入内核)
- 否则,交由NtContinue(再次进入内核态)函数恢复线程执行环境,在原指令处继续执行。
- 如果(ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0,则构建一个新的异常(当前异常会放在新异常的ExceptionRecord域中),并再次调用异常处理器(进入内核)
- 如果最后一个例程被执行(UnhandledExceptionFilter)则
- 如果注册了UnhandledException例程(SetUnhandledExceptionFilter),该UnhandledException例程会在此时被执行
- 如果UnhandledException例程返回EXCEPTION_EXECUTE_HANDLER,进程随后被终止
- 如果UnhandledException例程返回EXCEPTION_CONTINUE_EXECUTION,则则交由NtContinue(再次进入内核态)函数恢复线程执行环境,在原指令处继续执行。
- 如果没注册了UnhandledException例程例程或该例程返回EXCEPTION_CONTINUE_SEARCH
- 检查是否存在实时调试器(KLM\software\microsoft\windows nt\currentvsrsion\aedebug),如果存在并支持自动附加,则则启动该实时调试进程
- 否则,构建一个新的异常(当前异常会放在新异常的ExceptionRecord域中),并再次调用异常处理器(进入内核,从第4步开始执行)
- 如果注册了UnhandledException例程(SetUnhandledExceptionFilter),该UnhandledException例程会在此时被执行
- 如果有例程识别该异常(返回EXCEPTION_EXECUTE_HANDLER),则跳转到__except块中继续执行。(不会进入内核)
- 用户调试器第二次处理异常
- 如果存在用户调试器,则第二次向调试端口发送该异常,并等待处理结果
- 如果用户调试器处理了该异常,则直接跳到7
- WIN32子系统处理异常
- 如果存在异常端口(ExceptionPort),一般情况下都存在,则向异常端口发送该异常。如果处理了该异常,则跳到7
- win32子系统这时会弹出一个程序异常提示框,可以选择调试或者退出进程
- 进入win32子系统还有最后一次处理该异常的机会,那就是在异常提示框中选择“调试”按钮。
- 只要win32子系统接收到该异常,就不会走到6处。
- 如果存在异常端口(ExceptionPort),一般情况下都存在,则向异常端口发送该异常。如果处理了该异常,则跳到7
- 异常未被处理
- 所有的异常机制都没有处理该异常,则杀死当前进程,并且系统蓝屏。蓝屏代号:KMODE_EXCEPTION_NOT_HANDLED
- 所有的异常机制都没有处理该异常,则杀死当前进程,并且系统蓝屏。蓝屏代号:KMODE_EXCEPTION_NOT_HANDLED
- 异常被处理
- 被处理,退出异常处理器
转载请注明出处。ddlx studio。点点灵犀。 http://blog.youkuaiyun.com/sunyikuyu