windows系统里,为了保证系统内核的强壮和稳定,为了保证用户程序的强壮和稳定,提供了异常处理机制,来帮助程序员和系统使用人员处理异常。如果想要更加深入的掌握操作系统,异常处理的知识是必不可少的,不仅如此,软件调试也与异常处理息息相关。
异常执行流程
CPU检测到异常 -> 查中断表执行处理函数 -> CommonDispatchException -> KiDispatchException -> KiUserExceptionDispatcher -> RtlDispatchException ->VEH -> SEH
异常产生后,首先是要记录异常信息(异常的类型、异常发生的位置等),然后要寻找异常的处理函数,我们称为异常的分发,最后找到异常处理函数并调用,我们称为异常处理。
异常的分类
-
CPU产生的异常
-
软件模拟产生的异常
CPU异常
CPU指令检测到异常(例:除0)
查IDT表,执行中断处理函数
CommonDispatchException
KiDispatchException
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UlplhROn-1652031739273)(/images/异常处理/image-20220327153026523.png)]](https://i-blog.csdnimg.cn/blog_migrate/37fcf3a318c79f9344255a71e5f9426e.png)
找到IDT表的0号中断
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYd2rjS0-1652031739275)(/images/异常处理/image-20220327153034674.png)]](https://i-blog.csdnimg.cn/blog_migrate/9c4df3d78ad2c9a3714d79c68593aa0a.png)
首先保存现场
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zVQCsdIi-1652031739276)(/images/异常处理/image-20220327153138693.png)]](https://i-blog.csdnimg.cn/blog_migrate/a48e12749d90445d0ac06fbfa009499d.png)
然后向下走,但是并没有直接异常处理的代码,这里有一个跳转跟进去。为什么操作系统没有直接将异常处理写进去,这是因为操作系统希望我们自己首先能够将异常给处理掉
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNstsHXv-1652031739277)(/images/异常处理/image-20220327153256604.png)]](https://i-blog.csdnimg.cn/blog_migrate/f38b4e41055dbb7a9c3b5cf6ffb68281.png)
跟进去后发现调用了CommonDispatchException函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBSP4Znd-1652031739278)(/images/异常处理/image-20220327153417571.png)]](https://i-blog.csdnimg.cn/blog_migrate/3f9792e93c67d0f074848b2fd0d15ad2.png)
CommonDispatchException主要是把一些异常的信息存储到了自己的结构体_EXCEPTION_RECORD里面,结构如下
type struct _EXCEPTION_RECORD
{
DWORD ExceptionCode; //异常代码
DWORD ExceptionFlags; //异常状态
struct _EXCEPTION_RECORD* ExceptionRecord; //下一个异常
PVOID ExceptionAddress; //异常发生地址
DWORD NumberParameters; //附加参数个数
ULONG_PTR ExceptionInformation
[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZsxEvmFw-1652031739278)(/images/异常处理/image-20220327153537518.png)]](https://i-blog.csdnimg.cn/blog_migrate/6f427abf2d7f9c3924a0070cb407045d.png)
然后通过KiDispatchException去找到异常处理函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKXCM7f4-1652031739279)(/images/异常处理/image-20220327154532542.png)]
在前面的跳转中,带过去了两个寄存器eax、ebx,eax我们可以发现它的值为0c000094,这个值是操作系统定义的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wwhStclo-1652031739279)(/images/异常处理/image-20220327153718760.png)]](https://i-blog.csdnimg.cn/blog_migrate/00f0dc9ebb1f7052d42ef48f00ab3427.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bjZIRxjN-1652031739279)(/images/异常处理/image-20220327153832583.png)]](https://i-blog.csdnimg.cn/blog_migrate/d1b8094aa869d565e6d6bd57b5552912.png)
然后再是ebx,ebp指向的是_Trap_Frame结构体的栈顶,+68指向的就是eip
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QqI0t4uZ-1652031739280)(/images/异常处理/image-20220327153931139.png)]](https://i-blog.csdnimg.cn/blog_migrate/88bf14ae09e6e70d9b307264baf0a3c7.png)
这两个值就对应了结构里面的ExceptionCode和ExceptionAddress
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVJnRNkk-1652031739281)(/images/异常处理/image-20220327154129883.png)]](https://i-blog.csdnimg.cn/blog_migrate/aef496a5782e3afa69c9d33d4954b38f.png)
再看ExceptionFlags,CPU导致的异常这个值为0,软件调试导致的异常这个值为1
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOxPjbVD-1652031739281)(/images/异常处理/image-20220327154213174.png)]](https://i-blog.csdnimg.cn/blog_migrate/a2548f6609eb19883076196b53efd05d.png)
CPU异常执行的流程:
1、CPU指令检测到异常
2、查IDT表,执行中断处理函数
3、调用CommonDispatchException(构建EXCEPTION_RECORD)
4、KiDispatchException(分发异常:目的是找到异常的处理函数)
模拟异常记录
调用过程
CxxThrowException
(KERNEL32.DLL)RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *lpArguments)
NTDLL.DLL!RtlRaiseException()
NT!NtRaiseException
NT!KiRaiseException
首先手动抛出异常
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-co4qd4Yr-1652031739282)(/images/异常处理/image-20220327155751293.png)]](https://i-blog.csdnimg.cn/blog_migrate/b39e90c21a6021f3a9fcca5d06ee6293.png)
然后去到返回表发现调用了CxxThrowException
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hL4qoHUn-1652031739282)(/images/异常处理/image-20220327155847189.png)]](https://i-blog.csdnimg.cn/blog_migrate/55a211dc8904b88e0b52e75660ebe33f.png)
CxxThrowException
__CxxThrowException@8:
00401290 push ebp
00401291 mov ebp,esp
00401293 sub esp,20h
00401296 push esi
00401297 push edi
00401298 mov ecx,8
0040129D mov esi,offset string "The value of ESP was not properl"...+0E0h (00423118)
004012A2 lea edi,[ebp-20h]
004012A5 rep movs dword ptr [edi],dword ptr [esi]
004012A7 mov eax,dword ptr [ebp+8]
004012AA mov dword ptr [ebp-8],eax
004012AD mov ecx,dword ptr [ebp+0Ch]
004012B0 mov dword ptr [ebp-4],ecx
004012B3 lea edx,[ebp-0Ch]
004012B6 push edx
004012B7 mov eax,dword ptr [ebp-10h]
004012BA push eax
004012BB mov ecx,dword ptr [ebp-1Ch]
004012BE push ecx
004012BF mov edx,dword ptr [ebp-20h]
004012C2 push edx
004012C3 call dword ptr [__imp__RaiseException@16 (0042b15c)]
该代码所做的事情如下:
① 先从内存中拷贝一段0x20字节的固定结构体到堆栈中;
② 将ExceptionList也拷贝到堆栈中(该结构体内部)
③ 传入有关参数调用RaiseException函数。
注意,ThrowCode虽然从用户代码传入进来,但分析其函数并没有用到,而是直接调用一段固定的异常码。而&ThrowCode以及异常链被作为其参数存储,这样通过分析就可以轻易找到其ThrowCode值,其作为参考之后来处理SEH。
RaiseException
跟进去调用了Kernel32.dll的RaiseException
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f9tuTsrf-1652031739283)(/images/异常处理/image-20220327161000483.png)]](https://i-blog.csdnimg.cn/blog_migrate/c7fc1fddb8702d5832d772fe75c40749.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4rjFYwEB-1652031739283)(/images/异常处理/image-20220327161910401.png)]](https://i-blog.csdnimg.cn/blog_migrate/d2a4f6c9eda902f2a1254e760d9f55c4.png)
这里跟CPU异常不同的是,CPU异常会将错误代码跟着寄存器一起传入,但是软件异常并没有,这里看一下
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0LA3yWs-1652031739284)(/images/异常处理/image-20220327161459530.png)]](https://i-blog.csdnimg.cn/blog_migrate/002c4fa9152a9edbf29f878e219f956d.png)
这里的edx为E06D7363就是软件调试的错误代码,这里注意,随着语言和版本的不同,这里的EDX即错误代码并不固定,取决于编译环境
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HN6ksxnr-1652031739284)(/images/异常处理/image-20220327161638930.png)]](https://i-blog.csdnimg.cn/blog_migrate/6cf7e8fafbb59574a7f13313d9ae18c6.png)
第二个差异就是CPU异常存储的是发生异常的地址,软件异常则是存储RaiseException函数的地址
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YxQskuaQ-1652031739284)(/images/异常处理/image-20220327162048454.png)]](https://i-blog.csdnimg.cn/blog_migrate/478d55f23982bc011afcd174e46e7697.png)
内核层异常处理流程
前面我们分析过,存在两种异常,CPU异常与用户模拟异常,其异常触发时收集的线路是不同的,但是其最终走经过KiDispatchException函数。
当走到KiDispatchException,CPU异常与用户模拟异常唯一的区别是CPU异常最高位置1(nt!KiRaiseException异常派发时的上一行代码),其余记录的都是一样的。
而KiDispatchException的处理是按照其先前模式来处理的,也就是内核异常与用户异常两种,而不是按照CPU异常与用户模拟异常来进行处理。
1) _KeContextFromKframes 将Trap_frame备份到context 为返回3环做准备
2) 判断先前模式 0是内核调用 1是用户层调用
3) 是否是第一次机会
4) 是否有内核调试器
5) 如果没有或者内核调试器不处理
6) 调用RtlDispatchException
7) 如果返回FALSE 也就是0
8) 再次判断是否有内核调试器 有就调用 没有直接蓝屏
KiDispatchException
首先定位到KiDispatchException函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o0x6W9Kj-1652031739284)(/images/异常处理/image-20220327205416986.png)]](https://i-blog.csdnimg.cn/blog_migrate/689ff94dd4e68e23c9d1146b10cea4dd.png)
首先备份Trap_Frame结构,如果是用户层的异常则需要返回3环堆栈
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0R0qgGqf-1652031739285)(/images/异常处理/image-20220327205703543.png)]](https://i-blog.csdnimg.cn/blog_migrate/f2cb3d56cada73deab015906f135eef1.png)
首先通过判断先前模式的值来识别是内核异常还是用户层异常,这里有一个是否第一次调用该函数的判断,这是因为这个函数会被调用很多次,如果不是第一次调用则直接跳转
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8wQtVNY-1652031739285)(/images/异常处理/image-20220327210422006.png)]](https://i-blog.csdnimg.cn/blog_migrate/6e0b7ec1aacf6967664f24bafe543d53.png)
这个函数的最后一个参数就是表示这个函数是第几次被调用
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABV9rcml-1652031739286)(/images/异常处理/image-20220327210439563.png)]](https://i-blog.csdnimg.cn/blog_migrate/97203026f33ade223ea2c33798900973.png)
然后继续判断有没有内核调试器的存在(如windbg)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bw7sa41g-1652031739286)(/images/异常处理/image-20220327210634934.png)]](https://i-blog.csdnimg.cn/blog_migrate/afc667fab8225c9e9c8fdf79562f5a4c.png)
如果有内核调试器的存在就走下面的KiDebugRoutine函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wmSMOvn-1652031739286)(/images/异常处理/image-20220327210735087.png)]](https://i-blog.csdnimg.cn/blog_migrate/6b7e123095c96fdfe7866ee3a2287349.png)
如果内核调试器没有处理返回失败的话就跳转
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TQ73BagA-1652031739287)(/images/异常处理/image-20220327212341854.png)]](https://i-blog.csdnimg.cn/blog_migrate/e5b649d07a2a73f99823dbf0ad558e08.png)
RtlDispatchException调用异常处理函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYoDislr-1652031739287)(/images/异常处理/image-20220327212316093.png)]](https://i-blog.csdnimg.cn/blog_migrate/9be25f5604a0f7355786a4c08d1f6f13.png)
跟进到RtlDispatchException
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9fiwoSuI-1652031739287)(/images/异常处理/image-20220327212548924.png)]](https://i-blog.csdnimg.cn/blog_migrate/01a7d050d8ea1a4a48c90e6e522aca1b.png)
又调用了RtlGetRegistrationHead
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mcpMM6Vv-1652031739287)(/images/异常处理/image-20220327212602769.png)]](https://i-blog.csdnimg.cn/blog_migrate/3b0b5e90abc1cef83cb5aff360b9950b.png)
跟进去发现取的是fs:[0]
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ANNAvc9A-1652031739288)(/images/异常处理/image-20220327212624842.png)]](https://i-blog.csdnimg.cn/blog_migrate/05b4e3de9ef7530a0713906fc65f379f.png)
_EXCEPTION_REGISTRATION_RECORD
我们知道0环的fs:[0]指向KPCR,KPCR的第一个结构是_NT_TIB,_NT_TIB的第一个成员是ExceptionList,是一个_EXCEPTION_REGISTRATION_RECORD类型的结构体
_EXCEPTION_REGISTRATION_RECORD结构如下
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
_EXCEPTION_REGISTRATION_RECORD里面有两个成员,*Next是一个指针指向下一个_EXCEPTION_REGISTRATION_RECORD结构,而第二个成员Handler指向的就是一个异常处理函数
RtlDispatchException的作用如下:
遍历异常链表,调用异常处理函数,如果异常被正确处理了,该函数返回1
如果当前异常处理函数不能处理该异常,那么调用下一个,以此类推。
如果到最后也没有处理这个异常,返回0。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCvp7Ic4-1652031739288)(/images/异常处理/image-20220327213102327.png)]](https://i-blog.csdnimg.cn/blog_migrate/7ca006979b5c814c5a3f95532dced2d3.png)
调用异常处理函数得到返回值后跳转到地址
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eqCopgfS-1652031739288)(/images/异常处理/image-20220327213504310.png)]](https://i-blog.csdnimg.cn/blog_migrate/4f57a595e7047c26cef24c8aa64fe5c5.png)
然后判断返回值是否为1,1的话就是处理成功,跳转
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyCvO0CJ-1652031739289)(/images/异常处理/image-20220327213527703.png)]](https://i-blog.csdnimg.cn/blog_migrate/7ccb13547acc8e5737283fdd9ee6366d.png)
异常被处理成功则把Context结构放回Trap_Frame里面
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U7LF8KFe-1652031739289)(/images/异常处理/image-20220327213735559.png)]](https://i-blog.csdnimg.cn/blog_migrate/7fb2aaaf871c780d417b9564f5deb10e.png)
如果没有被处理成功则继续往下走进行有无内核调试器的判断,如果有内核调试器则调用KiDebugRoutine
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCUGe45e-1652031739290)(/images/异常处理/image-20220327213835219.png)]](https://i-blog.csdnimg.cn/blog_migrate/87b91c9bd9152ac565cb2a827d08283c.png)
如果没有内核调试器或者有内核调试器但是没有处理异常,则跳转到下面的地方
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4FzzAg7-1652031739291)(/images/异常处理/image-20220327214035176.png)]](https://i-blog.csdnimg.cn/blog_migrate/6f3625c9ed4b63f73000960e73603a10.png)
操作系统蓝屏
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KBFsjqIb-1652031739292)(/images/异常处理/image-20220327214109874.png)]](https://i-blog.csdnimg.cn/blog_migrate/f1dcb9c10a78c1ecdc9458393edc492d.png)
用户层异常处理流程
定位到KiDispatchException,进入用户异常的函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mcxnau4s-1652031739293)(/images/异常处理/image-20220328095342284.png)]](https://i-blog.csdnimg.cn/blog_migrate/81a1d28ce3e2909329c4b53d1ae55167.png)
进入函数首先判断是不是第一次调用,然后继续往下走,如果有内核调试器则直接跳转,没有的话继续往下走
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZQxdoQM-1652031739293)(/images/异常处理/image-20220328100329260.png)]](https://i-blog.csdnimg.cn/blog_migrate/56ad46b32bf54d728904a0b96efb7dfd.png)
然后进行异常的处理,调用DbgkForwardException,这个函数的作用是调用3环的调试器 ,再进行判断有无3环的调试器接收异常,如果没有则返回3环处理
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CU6pmr6-1652031739293)(/images/异常处理/image-20220328100451340.png)]](https://i-blog.csdnimg.cn/blog_migrate/3c0f0ac583598e5ed030245da5a1dafb.png)
然后进行结构体的修改,这里同用户APC执行的修改过程
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OxUUtV9c-1652031739294)(/images/异常处理/image-20220328101211189.png)]](https://i-blog.csdnimg.cn/blog_migrate/25e61c5aa7b17856124b61981a54bfa9.png)
然后修改EIP的值为KeUserExceptionDispatcher函数的地址,这时候EIP的值已经是函数地址,这时候再回到原函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9TfLUfCl-1652031739294)(/images/异常处理/image-20220328101716892.png)]](https://i-blog.csdnimg.cn/blog_migrate/e9f0ea76bc61620c0cd7823f0ab9d572.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ihZIzZ0U-1652031739295)(/images/异常处理/image-20220328101848233.png)]](https://i-blog.csdnimg.cn/blog_migrate/8a9bcb946ca5a3fceee3b847c8c3f0fc.png)
注意这里并没有直接进行返回3环的操作,而是KiDispatchException这个函数执行结束过后回到原函数,用不同的方法回到3环
CPU异常:CPU检测到异常 -> 查IDT执行处理函数 -> CommonDispatchException <-> KiDispatchException
通过IRETD返回3环
模拟异常:CxxThrowException -> RaiseException -> RtlRaiseException -> NT!NtRaiseException -> NT!KiRaiseException <-> KiDispatchException
通过系统调用返回3环
无论通过哪种方式,但线程再次回到3环时,将执行KiUserExceptionDispatcher 函数
KiUserExceptionDispatcher
返回三环后,可以看到其调用一个RtlDispatchException。注意,在处理内核异常时,也有一个同名的RtlDispatchException,那是内核模块,这是三环模块。
RtlDispatchException可以认为是异常的核心,区别是如果在内核模块,则处理零环,如果在ntdll模块,则处理三环。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gcJQDW8d-1652031739296)(1827556-20200402110019370-957825440.png)]](https://i-blog.csdnimg.cn/blog_migrate/b28c4061ff2ec38a5b082ad5c2288584.png)


2737

被折叠的 条评论
为什么被折叠?



