Windows 异常机制

愿君学长松,慎勿作桃李。这篇文章主要讲述Windows 异常机制相关的知识,希望能为你提供帮助。
异常的分类(1)软件异常:由操作系统 / 应用程序 引发

用户:RaiseException -> RltRaiseException -> NtRaiseException -> KiRaiseException
内核:RtlRaiseException -> NtRaiseException -> KiRaiseException

软件异常归根结底都是基于 RaiseException 这个用户态 API 和 NtRaiseException 的内核服务建立起来的。
void RaiseException(
DWORD dwExceptionCode ,// 异常状态码
DWORD dwExceptionFlags,// 异常标志(可持续 / 不可持续)
DWORD nNumberofArguments,// lpArguments[] 数组长度
const DWORD* lpArguments// 传递给异常处理程序的筛选表达式
);
// 具体作用就是将异常信息传递给 EXCEPTION_RECORD 这个结构,然后再调用 RtlRaiseException 函数
/*
typedef struct _EXCEPTION_RECORD
DWORDExceptionCode; // 异常状态码
DWORDExceptionFlags; // 异常标志
struct _EXCEPTION_RECORD*ExceptionRecord; // 指向下一个异常的指针
PVOIDExceptionAddress; // 保存异常发生的地址
DWORDNumberParameters; // ExceptionInformation[] 数组参数个数
ULONG_PTRExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; // 异常描述信息
EXCEPTION_RECORD;
*/

(2)硬件异常:由 CPU 引发
① 错误异常
处理错误异常时,操作系统首先保存当前环境(比如寄存器的值),然后调用相应的异常处理函数,如果异常处理成功则恢复现场环境继续执行。
② 陷阱异常
和错误异常不同,陷阱异常发生时会保存要执行的下一条指令的环境(而不是正在执行的指令的环境),调试器的断点就是基于陷阱异常实现的。
③ 终止异常
·主要用来处理严重的硬件错误,和上面的异常不同,这种异常不会恢复执行而是直接退出。
异常和中断的关系中断可以再任何时候发生,和 CPU 正在执行什么指令无关,可以被取消。而异常是由于 CPU 执行了某条指令引起的,不能被取消。
异常的分发处理异常产生时,CPU是通过IDT进入内核来寻找处理函数:
(1)内核可以处理这个异常
异常处理程序执行完后会恢复现场并继续执行,这个过程我们感知不到。
(2)内核不能处理这个异常
① 如果这个异常来自内核,蓝屏。
② 如果这个异常来自应用程序,则异常处理权转交给应用程序的异常处理函数。如果程序处理了异常则程序继续执行,如果没有处理这个异常则程序崩溃。
上面的 ② 有两种可能:应用程序被调试 / 没有被调试 ——
< 1> 如果程序被调试,则异常处理权限转交给调试器(应用程序将调试信息包装成调试事件并发送给调试器,调试器使用 WaitForDebugEvent 获得该调试事件),调试器经过一系列操作后调用 ContinueDebugEvent 继续执行程序。如果调试器处理了这个异常则程序继续执行,如果没有处理则将异常处理权限转交给应用程序(也就是 < 2> )。
< 2> 如果程序没有被调试,则将异常信息连同线程上下文环境送入程序的栈中,并调用 KiUserExceptionDisptcher (由 ntdll 导出,所有的异常分发都会走这个函数)但KiUserExceptionDisptcher 实际不执行什么功能,实际发挥作用的是 RtlExceptionDisptcher,之后如果 RtlExceptionDisptcher 返回成功则调用 ZwContinue 继续执行,否则调用 ZwRaiseException 结束进程(如果正在被调试的话就把异常再次抛给调试器)。
【Windows 异常机制】但是如果调试器还是没有处理这个异常呢?(是个狠人

    推荐阅读