异常分发(用户异常)

内容回顾:

异常如果发送在内核层,处理起来比较简单,因为异常处理函数也在0环,不用切换堆栈,但是如果异常发生在3环,就意味着必须要切换堆栈,回到3环执行处理函数

切换堆栈的处理方式与用户APC的执行过程几乎是一样的,惟一的区别就是执行用户APC时返回3环后执行的函数是KiUserApcDispatcher,而异常处理时返回3环后执行的函数是KiUserExceprionDispatcher

所以,理解用户APC的执行过程是理解3环异常处理的关键

用户异常的处理流程:

VOID KiDispatchException(ExceptionRecord,ExceptionFrame,TrapFrame,PreviousMode,FirstChance)
1._KeContextFromKframes 将Trap_frame备份到context为返回3环做准备
在这里插入图片描述

第一步:不管用户异常还是内核异常,先把Trap_frame(当前线程3环进入0环时,那些寄存区环境,也就是eip运行地方那些值)备份到context里(为返回3环做准备)

这个函数两种异常(用户异常和内核异常)的分发都归它管,所以就存在异常处理是否需要回到三环,内核异常不用回去(内核异常处理函数在0环),用户异常需要回去(用户层处理函数在3环0)。
在这里插入图片描述

2.第二步:判断先前模式,0是内核调用,1是用户调用,用户层异常呢,紧接着就是跳转:0x4258C3

在这里插入图片描述

第三步:第一次执行时,肯定是第一次调用(这个函数不止执行一次),所以接着往下:

扫描二维码关注公众号,回复: 12581157 查看本文章

这里判断是否启用了内核调试器,如果有内核调试器的话,那么这个值是非零的,如果有内核调试器的话,那它就调用,把异常信息先发送给异常调试器(那么我们此处假设没有内核异常调试器,或者内核异常调试器没有处理)
在这里插入图片描述

第四步:用来判断3环调试器(3环调试器如果不存在或者3环调试器没有处理的话,那么接着往下走)

第五步:为回到三环做准备
在这里插入图片描述

一开始就把Trap_frame备份到context里,然后这里接下来就可以随心所欲地改
在这里插入图片描述
在这里插入图片描述

最关键的修改,把KeUserExceptionDispatcher里面的值覆盖到Eip,它并没有在当前位置直接返回三环。而是让当前程序结束执行
在这里插入图片描述

总结

VOID KiDispatchException(ExceptionRecord,ExceptionFrame,TrapFrame,PreviousMode,FirstChance)

  1. _KeContextFromKframes 将Trap_frame备份到context为返回3环做准备
  2. 判断先前模式 0是内核调用,1是用户调用
  3. .是否是第一次机会
  4. 是否有内核调试器
  5. 发送给3环调试
  6. 如果3环调试器没有处理这个异常,修正EIP为KiUserExceptionDispatcher
  7. KiDispatchException函数执行结束:CPU异常与模拟异常返回地点不同
    CPU异常:CPU检测到异常—>查IDT执行处理函数—>CommonDispatchException------>KiDispatchException 通过IRETD返回3环
    模拟异常:CxxThrowException—>RaiseException—>RtlRaiseException---->Nt!NtRaiseException—>Nt!KiRaiseException---->KiDispatchException通过系统调用返回3环
  8. 无论通过哪种方式,但线程再次回到3环,将执行KiUserExceptionDispatcher函数

猜你喜欢

转载自blog.csdn.net/CSNN2019/article/details/113837725