异常如果发生在内核层,处理起来比较简单,因为异常处理函数也在0环,不用切换堆栈,但是如果异常发生在3环,就意味着必须要切换堆栈,回到3环执行处理函数。
切换堆栈的处理方式与用户APC的执行过程几乎是一样的,唯一的区别就是执行用户APC时返回3环后执行的函数是KiUserApcDispatcher,而异常处理时返回3环后执行的函数是KiUserExceptionDispatcher.
所以,理解用户APC的执行过程是理解3环异常处理的关键。
VOID KiDispatchException
(
ExceptionRecord,
ExceptionFrame,
TrapFrame,
PreviousMode,
FirstChance
)
用户异常跳转
这些都是修改Ktrap_Frame回准备回3环操作
修改Ktrap_Frame.eip
- KeContextFromKframes将Trap_frame备份到context为返回3环做准备
- 判断先前模式0是内核调用1是用户层调
- 是否是第一次机会
- 是否有内核调试器
- 发送给3环调试
- 如果3环调试器没有处理这个异常,把异常数据填充到用户栈。
- 修正EIP为KiUserExceptionDispatcher。这个函数是ntdll的
- 调用KiDispatchException 返回用户层
无论通过那种方式,但线程再次回到3环时,将执行KiUserExceptionDispatcher函数
KiUserExceptionDispatcher()函数会将异常发生给一个基于帧的异常处理器,如果存在异常处理器处理了该异常,则程序继续执行。
如果该异常仍未被处理,调用NtRaiseException函数再次进入内核中处理该异常,这次KiDispatchException函数的FirstChance为FALSE(不是第一次了),第二次还没处理该异常,则终止该进程