Windows的SEH机理简要介绍
1.异常分类
一般来说,我们把Exception分为2类,一类是CPU产生的异常,我们称之为CPU异常(或者硬件异常)。另一类为是通过调用RaiseException API产生的软件异常,我们称之为软件异常。
Windows使用同一的方式(KiDispatchException)来描述和分发这两类异常。但是,在处理各自异常时,会略有区别。
一般来说,异常处理过程可以分为2个阶段,第1阶段:异常登记过程;第2阶段:异常分发过程。下面分别简要介绍。
2.异常登记
1) CPU异常(硬件异常)登记:
在windows kernel中,存在一张中断描述符表(IDT, Interupt Descriptor Table). IDT是一张位于内核态物理内存中的线性表,其有256个表项。IDT中的每个表项叫做门描述符(Gate Descriptor)。门描述符的基本作用就是将CPU异常对应的中断号与其对应的异常处理函数KiTrapXX关联起来。
例如,0号中断(即除0错误)对应的处理例程为nt!KiTrap00
同时,我们可以通过以下debug comnand来列出IDT表中的各个表项。
lkd>!idt -a
对于CPU异常,通过中断向量找到其中断处理例程 KiTrapXX后,该KiTrapXX会调用CommDispatchException函数,其会获取异常发生时候的适当参数,用来初始化EXCEPTION_RECORD结构体之后,开始调用KiDispatchException进行异常分发。
(简述如下:中断向量 - 〉KiTrapXX - > CommonDispatchException - >KiDispatchException)
EXCEPTION_RECORD的结构如下:
0:000> dt ntdll!_EXCEPTION_RECORD
+0x000 ExceptionCode : Int4B
+0x004 ExceptionFlags : Uint4B
+0x008 ExceptionRecord : Ptr32 _EXCEPTION_RECORD
+0x00c ExceptionAddress : Ptr32 Void
+0x010 NumberParameters : Uint4B
+0x014 ExceptionInformation : [15] Uint4B
2) 软件异常登记
软件异常是通过直接或者间接调用内核服务NtRaiseException而产生的。而用户态中可以通过RaiseException API,或者Try-catch等高级语言来调用这个内核服务,而通过RaiseException来登记软件异常的过程可以简单表述如下:
RaiseException在初始化一个EXCEPTION_RECORD结构体之后,开始调用NTDLL中的RtlRaiseException; RtlRaiseException在初始化CONTEXT结构体之后,开始调用内核中NtRaiseException, NtRaiseException再调用另外一个内核函数KiRaiseException。接下来KiRaiseException会调用KiDispatchException开始异常的分发。
如下所示:

CONTEXT是一个用来保存用户态-核心态切换现场的数据结构,主要是切换状态时候的各个寄存器的状态,其结构如下:
struct _CONTEXT
+0x000 ContextFlags : Uint4B
...
...
+0x09c Edi : Uint4B
+0x0a0 Esi : Uint4B
+0x0a4 Ebx : Uint4B
+0x0a8 Edx : Uint4B
+0x0ac Ecx : Uint4B
+0x0b0 Eax : Uint4B
+0x0b4 Ebp : Uint4B
+0x0b8 Eip : Uint4B
+0x0c4 Esp : Uint4B
...
...
3. 异常派发过程(Dispatch Exception)
当产生CPU异常或者软件异常之后,最后都会调用到系统服务KiDispatchException进行异常的派发和处理。 对于CPU异常和软件异常,其处理过程略有不同。下面分别简要介绍
1)CPU异常派发过程:
对于第一轮的异常,其会尝试先让内核调试器来处理该异常(KiDebugRoutine)。如果KiDebugRoutine返回为True,也就是内核调试器处理了该异常,那么便停止异常分发。否则,会调用kernel mode下的RtlDispatchException (NTOSKRNL)来试图寻找已经注册的结构化异常处理器。
如果没有相应的异常处理器,系统会尝试进行第二次分发。如果这次KeDebugRoutine仍然返回FAlSE,表明这是一个无人处理的异常,从而调用KeBugCheckEx引发蓝屏。
其过程如下所示:

2) 软件异常派发过程:

当软件异常被派发到user-mode之后,如何处理这个exception呢?实际上,在TEB中有一个非常重要的结构体,叫做_NT_TIB。在_NT_TIB中有一个_EXCEPTION_REGISTRATION_RECORD类型的字段叫做exceptionlist, 他的值就是指向异常处理器(_exception_handler)的首地址。EXCEPTION_REGISTRATION_RECORD是一个单向链表。
那么这个EXCEPTION_REGISTRATION_RECORD的首地址值从何而来呢?他是保存在FS:[0]寄存器中的。也就是说,当异常发生时,取得FS:[0]中的值,即为EXCEPTION_REGISTRATION_RECORD的首地址。我们从windbg中可以得到验证,如下:

代码
TEB at 7ffdf000
ExceptionList: 0012fd04
StackBase: 00130000
StackLimit: 0012e000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdf000
EnvironmentPointer: 00000000
ClientId: 0000312c . 00001a50
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffdb000
LastErrorValue: 0
LastStatusValue: c0000135
Count Owned Locks: 0
HardErrorMode: 0
0:000> r fs
fs=0000003b
0:000> dd fs:[0] L4
003b:00000000 0012fd04 00130000 0012e000 00000000

在得到该异常处理链表之后,便开始遍历该链表。在遍历链表的过程中,当前节点的Exception_hanlder会判断是否能否handle当前的异常。如不能,则返回枚举类型_EXCEPTION_DISPOSITION的一个值 (ExceptionContinueSearch),以便让其继续向后遍历该链表,直到找到该exception_handler,并最终返回ExceptionContinueExecution,以便停止向下遍历的过程。其过程如下图所示:

但是,如果遍历到最后都没有找到handle当前exception的exception handler,那么便会触发unhandled exception并最终调用ntdll!RtlUnhandledExceptionFilter,对于桌面型应用程序,其就会崩溃; 而对于服务端程序,为了更好的用户体验,这时候比如asp.net 的runtime 就会捕捉到该exception,在客户端可能就看到service unavailable,或者服务器端错误等等。

代码
=============
typedef struct _TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
...
_NT_TIB的结构定义如下:
===========
typedef struct _NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
...
}NT_TIB
而ntdll!_EXCEPTION_REGISTRATION_RECORD的定义如下:
===========
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
typedef enum _EXCEPTION_DISPOSITION
ExceptionContinueSearch = 1
ExceptionNestedException = 2
ExceptionCollidedUnwind = 3
}EXCEPTION_DISPOSITION

附件1:
NTDLL模块中与exception处理相关的常见几个系统服务和相关函数

代码
ntdll!RtlpDphRaiseException
ntdll!RtlpHeapExceptionFilter
ntdll!RtlpDphUnexpectedExceptionFilter
ntdll!RtlUnhandledExceptionFilter2
ntdll!RtlSetUnhandledExceptionFilter
ntdll!RtlDispatchException
ntdll!RtlRaiseException
ntdll!RtlpExecuteHandlerForException
ntdll!KiRaiseUserExceptionDispatcher
ntdll!KiUserCallbackExceptionHandler
ntdll!KiUserExceptionDispatcher
ntdll!KiUserApcExceptionHandler

参考文档:
=======
A Crash Course on the Depths of Win32™ Structured Exception Handling
http://www.microsoft.com/msj/0197/exception/exception.aspx
Windows的SEH机理简要介绍的更多相关文章
- Windows ToolTips简要介绍(转)
原文转自 https://blog.csdn.net/sesiria/article/details/77450151 Windows 标准控件ToolTips简要介绍 参考文档 MSDN https ...
- HTTP协议简要介绍
1. 网络基础 TCP/IP 通常使用的网络是在TCP/IP协议簇基础上运作的. HTTP属于它内部的一个子集. TCP/IP分为4个层次, 应用层, 传输层, 网络层, 链路层. (Applicat ...
- 0-Broadcast机制原理简要介绍
Broadcast机制简要介绍 来源: http://blog.csdn.net/luoshengyang/article/details/6730748 导语 广播机制在Android系统中,也不算 ...
- Windos下的6种IO模型简要介绍
windows进行数据的收发有6种IO模型.分别是阻塞(blocking)模型,选择(select)模型,异步选择(WSAAsyncSelect)模型,事件选择(WSAEventSelect )模型, ...
- WCF简要介绍
什么是WCF WCF的全称是:Windows Communication Foundation.从本质上来说,它是一套软件开发包,是微软公司推出的符合SOA思想的技术框架.WCF为程序员提供了丰富的功 ...
- Windows Server 2012 NIC Teaming介绍及注意事项
Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...
- 简要介绍BASE64、MD5、SHA、HMAC几种方法。
加密解密,曾经是我一个毕业设计的重要组件.在工作了多年以后回想当时那个加密.解密算法,实在是太单纯了. 言归正传,这里我们主要描述Java已经实现的一些加密解密算法,最后介绍数字证书. ...
- [转]Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划
转自:Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划 前面我们从Android应用程序与SurfaceFlinger服务的关系出发,从侧面简单学习了Surfa ...
- [转] Android资源管理框架(Asset Manager)简要介绍和学习计划
转自:http://blog.csdn.net/luoshengyang/article/details/8738877 Android应用程序主要由两部分内容组成:代码和资源.资源主要就是指那些与U ...
随机推荐
- elasticsearch的基本用法(转载)
本文出自:http://blog.csdn.net/feelig/article/details/8499614 最大的特点: 1. 数据库的 database, 就是 index 2. 数据库 ...
- 面向对象编程思想(OOP)总结
本文我将从面向对象编程思想是如何解决软件开发中各种疑难问题的角度,来讲述我们面向对象编程思想的理解,梳理面向对象四大基本特性.七大设计原则和23种设计模式之间的关系. 软件开发中疑难问题: 软件复杂庞 ...
- Xtrabackup 热备
Xtrabackup介绍Xtrabackup是由percona开源的免费数据库热备份软件,它能对InnoDB数据库和XtraDB存储引擎的数据库非阻塞地备份(对于MyISAM的备份同样需要加表锁):m ...
- WebApi 路由机制剖析
阅读目录 一.MVC和WebApi路由机制比较 1.MVC里面的路由 2.WebApi里面的路由 二.WebApi路由基础 1.默认路由 2.自定义路由 3.路由原理 三.WebApi路由过程 1.根 ...
- JS对象 JavaScript 中的所有事物都是对象,如:字符串、数值、数组、函数等,每个对象带有属性和方法。
什么是对象 JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法. 对象的属性:反映该对象某些特定的性质的,如:字符串的长度.图像的长宽等: 对象的方法: ...
- 新建Application 报错android.app.Application cannot be cast
我在开发APP的时候重新使用了一个类,继承了android.app.Application.但是在运行的时候提示java.lang.ClassCastException: android.app.Ap ...
- CSIC_716_20191114【生成器、匿名函数、内置函数、三元表达式、列表生成式、生成器表达式】
生成器: 函数与yield连用,凡是函数中有yield的,调用该函数的时候均不会立即执行,而是会返回一个生成器. 生成器本质上是一个迭代器,需要通过 [生成器.__next__()]或者[nex ...
- CSIC_716_20191106【列表、元组、字典、集合】
python的数据类型及其内置方法 一.列表(续) list.count( ).list.index( ) list = ['1', '2', '3', '2', 'a', 'b', 'c', 'a' ...
- SUBTRACT
SUBTRACT 给出一个长度为n序列\(\{a_i\}\),定义一个操作,记做\(con(a,i)\),意思是用\(a_i-a_{i+1}\)替代\(a_i,a_{i+1}\),显然最后一个数字不能 ...
- BigDecimal踩过的大坑
通常Java中涉及金钱相关的计算为了保持精度,会采用BigDecimal来实现,但是BigDecimal中创建BigDecimal类对象的时候,如果使用直接new的话,必须是String类型的参数,否 ...