Windows异常处理学习

一、处理过程

 ntdll中的KiDispatchException函数会按两次处理机会进行分发,过程:

  1. 如果程序正在被调试,那么将异常交给用户调试器处理,如果没有则跳过这一步;
  2. 如果没有调试器或者调试器没有处理,将控制权返回到KiUserExceptionDispatcher函数,由它调用RtlDispatchException函数进行用户态异常处理(SEH和VEH)。

二、什么是异常?

异常分为

  1. 软件异常:程序模拟的异常,由操作系统或开发人员触发。
  2. 硬件异常:由CPU产生,例如除0操作引发的异常。

注:异常的另一种分类法:错误(指令未执行)、陷阱(指令执行完毕)、和中止(发生严重错误,强迫中止)。INT N指令导致的是异常而不是中断。有些编译器会用0xCC来使代码段进行内存对齐。

INT 3 和 INT N(N = 3)是不同的:前者指令只占一个字节(0xCC),后者两个字节(0xCD 后跟一个字节N)。编译器遇到int 3会编译为0xCC而不是0xCD03。0xCD03不应该出现在汇编中,因为他会被解析为int 3。而该指令执行后EIP+1继续执行,而0xCD03是两个字节,会导致EIP指向错误,错误解析之后的机器码。

向量化异常处理VEH:面向整个进程,通过AddVectoredExceptionHandler来设置,优先调用,第一轮异常分发中处理。

结构化异常处理SEH:面向线程,SEH链保存在进程的数据结构中TEB。操作系统根据SEH链的顺序分发异常(前提是没有注册VEH,VEH是全局的,而SEH是局部的,VEH优先级更高),根据上一次分发的结果决定下一步操作,第一轮异常分发中处理。

UEF未处理异常过滤:面向整个进程,第二轮异常分发中处理。在用户代码范围内发生或触发的但用户代码没有处理的异常。

异常处理模型--try{}except( filter){}模型:对于同一个函数的try块都只构造一次异常栈帧,try块中过滤函数和异常处理函数都被存放在一个表格里。

                   Windows操作系统异常处理过程

注:如果是手工注册,ExecuteHandler2直接调用用户注册的函数,而不是编译器的_except_handler4函数处理。CONTEXT结构中保存了执行环境中寄存器的值。

kernel32.dll中有函数UnhandledExceptionFilter,不同版本的Windows有所不同。xp中,它会首先检测是否被调试,然后决定把异常交给调试器或是用户设置的UnhandledExceptionFilter函数。

 三、异常分发机制

       当异常发生时,CPU会通过IDT表找到异常处理函数,即内核中的KiTrapxx系列函数,然后转去执行。但是KiTrapxx函数通常只是对异常作简单的表征和描述,为了支持调试和软件自己定义的异常处理函数,系统需要将异常分发给调试器或应用的处理函数。

       每个异常最多两轮被处理的机会。

       对于第一轮分发,操作系统会把异常分发给用户代码注册的异常处理函数;

       对于第二轮分发,异常分发函数会先尝试分发给调试器,如果没有调试器或调试器不处理,就会分发给UEF来处理。

注:系统只有在第一轮分发时,才会把异常分发给用户代码注册的异常处理器,第二轮分发时不会怎么做。因此,SEH或VEH只有一轮处理异常的机会。进入第二轮分发的异常都是未处理异常。分发异常时,系统从最晚注册的异常处理器来查找,所以这个最早注册的SEH处理器是最后得到处理机会。

LONG UnhandledExceptionFilter(*ExInfo){
               #1.检查是否因为写资源区而导致的访问违例
               #2.检查当前进程是否正在被调试,如果在被调试,则返回不处理,让系统继续分发。
               #3.是否有通过SetUnhandledExceptionFilter()注册的顶层过滤函数TopLevelFilter(*ExInfo),如果有,则调用。
               ...
} 

补:

                                                 异常触发过程

posted @ 2020-09-02 14:00  An2i  阅读(362)  评论(0编辑  收藏  举报