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 ...
随机推荐
- 【POJ】2240 Arbitrage
题目链接:http://poj.org/problem?id=2240 题意:n种国家的货币,m个换算汇率.问你能不能赚钱. eg:1美元换0.5英镑,1英镑换10法郎,1法郎换0.21美元,这样1美 ...
- NEO4J亿级数据全文索引构建优化
NEO4J亿级数据全文索引构建优化 一.数据量规模(亿级) 二.构建索引的方式 三.构建索引发生的异常 四.全文索引代码优化 1.Java.lang.OutOfMemoryError 2.访问数据库时 ...
- 查看Linux服务器公网IP
参考:https://www.cnblogs.com/pyyu/p/8545896.html 方法1:curl ifconfig.me 方法2:curl cip.cc
- ECMAScript中所有参数传递的都是值,不可能通过引用传递参数
今天在看JavaScript高级程序设计(第三版)时,看到了这个问题:ECMAScript中所有参数传递的都是值,不可能通过引用传递参数. 在我的印象中,其他语言比如Java,C++等,传递参数有两种 ...
- CF528E Triangles3000
题意:给你一个不存在三线共交点的一次函数组a[i]x+b[i]y+c[i]=0. 问等概率选取三条直线,围成三角形的面积的期望. n<=3000. 标程: #include<bits/st ...
- 关于Modelsim安装闪退
在盗版Windows系统上,安装Modelsim时可能出现闪退. 现象表现为,在任务管理器中仍然有Modelsim的进程,但是看不到安装界面. 碰到这种情况可以尝试如下方法:退到安全模式下安装. 一般 ...
- JQuery Ajax 向后台传参方式
在jquery的ajax函数中,可以传入3种类型的数据 文本:"uname=alice&mobileIpt=110&birthday=1983-05-12" jso ...
- python多线程,event,互斥锁,死锁,递归锁,信号量
Python多线程/event 多线程-threading python的thread模块是⽐较底层的模块, python的threading模块是对thread做了⼀些包装的, 可以更加⽅便的被使⽤ ...
- hadoop新增新数据节点和退役数据节点
新增数据节点 0. 需求随着公司业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点.1. 环境准备 (1)在hadoop03主机上再克 ...
- Hadoop yarn任务调度策略介绍
二.Capacity Scheduler(容器调度器)的配置 2.1 容器调度介绍 Capacity 调度器允许多个组织共享整个集群,每个组织可以获得集群的一部分计算能力.通过为每个组织分配专门的队列 ...