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中可以得到验证,如下:

代码

0:000> !teb

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,或者服务器端错误等等。

代码

_TEB (thread environment blcok即线程环境块)定义如下:

=============

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

   ...
   ...   
 }TEB
   

_NT_TIB的结构定义如下:

===========

typedef struct _NT_TIB
{

   +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD

   +0x004 StackBase        : Ptr32 Void

   +0x008 StackLimit       : Ptr32 Void

   ...
   ...
   +0x018 Self             : Ptr32 _NT_TIB

}NT_TIB

而ntdll!_EXCEPTION_REGISTRATION_RECORD的定义如下:

===========
typedef struct _EXCEPTION_REGISTRATION_RECORD
{

   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD

   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION 
}EXCEPTION_REGISTRATION_RECORD
 
ntdll!_EXCEPTION_DISPOSITION的定义如下:
============

  typedef enum _EXCEPTION_DISPOSITION
{
  ExceptionContinueExecution = 0

   ExceptionContinueSearch = 1

   ExceptionNestedException = 2

   ExceptionCollidedUnwind = 3

}EXCEPTION_DISPOSITION

附件1:

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

代码

 ntdll!RtlpUnhandledExceptionFilter 

 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机理简要介绍的更多相关文章

  1. Windows ToolTips简要介绍(转)

    原文转自 https://blog.csdn.net/sesiria/article/details/77450151 Windows 标准控件ToolTips简要介绍 参考文档 MSDN https ...

  2. HTTP协议简要介绍

    1. 网络基础 TCP/IP 通常使用的网络是在TCP/IP协议簇基础上运作的. HTTP属于它内部的一个子集. TCP/IP分为4个层次, 应用层, 传输层, 网络层, 链路层. (Applicat ...

  3. 0-Broadcast机制原理简要介绍

    Broadcast机制简要介绍 来源: http://blog.csdn.net/luoshengyang/article/details/6730748 导语 广播机制在Android系统中,也不算 ...

  4. Windos下的6种IO模型简要介绍

    windows进行数据的收发有6种IO模型.分别是阻塞(blocking)模型,选择(select)模型,异步选择(WSAAsyncSelect)模型,事件选择(WSAEventSelect )模型, ...

  5. WCF简要介绍

    什么是WCF WCF的全称是:Windows Communication Foundation.从本质上来说,它是一套软件开发包,是微软公司推出的符合SOA思想的技术框架.WCF为程序员提供了丰富的功 ...

  6. Windows Server 2012 NIC Teaming介绍及注意事项

    Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...

  7. 简要介绍BASE64、MD5、SHA、HMAC几种方法。

    加密解密,曾经是我一个毕业设计的重要组件.在工作了多年以后回想当时那个加密.解密算法,实在是太单纯了.     言归正传,这里我们主要描述Java已经实现的一些加密解密算法,最后介绍数字证书.     ...

  8. [转]Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划

    转自:Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划 前面我们从Android应用程序与SurfaceFlinger服务的关系出发,从侧面简单学习了Surfa ...

  9. [转] Android资源管理框架(Asset Manager)简要介绍和学习计划

    转自:http://blog.csdn.net/luoshengyang/article/details/8738877 Android应用程序主要由两部分内容组成:代码和资源.资源主要就是指那些与U ...

随机推荐

  1. 【2018ACM/ICPC网络赛】徐州赛区

    呃.自闭了自闭了.我才不会说我写D写到昏天黑地呢. I  Characters with Hash 题目链接:https://nanti.jisuanke.com/t/31461 题意:给你一个字符串 ...

  2. 面试39 MySQL读写分离

    (1)如何实现mysql的读写分离? 其实很简单,就是基于主从复制架构,简单来说,就搞一个主库,挂多个从库,然后我们就单单只是写主库,然后主库会自动把数据给同步到从库上去. (2)MySQL主从复制原 ...

  3. JS去重算法

    1.遍历数组法 它是最简单的数组去重方法(indexOf方法) 实现思路:新建一个数组,遍历去要重的数组,当值不在新数组的时候(indexOf为-1)就加入该新数组中: var arr=[2,8,5, ...

  4. CSIC_716_20191113【装饰器进阶以及迭代器】

    装饰器的进阶主要包含叠加装饰器和有参装饰器 叠加装饰器:在一个被装饰的对象中,添加多个装饰器. 为什么要用叠加装饰器的原因:    -每一个新的功能都应该写一个新的装饰器,否则会导致,代码冗余,结构不 ...

  5. Java 基础 - final vs static

    总结 共同点,都可以修饰类,方法,属性.而不同点: static 含义:表示静态或全局,被修饰的属性和方法属于类,可以用类名.静态属性 / 方法名 访问 static 方法:只能被static方法覆盖 ...

  6. Python 学习杂项

    #print("Hello World!") #name = "nihfjkds" age = 454 num1 = 1 num2 = 2 #print(nam ...

  7. 网络安全系列 之 SQL注入学习总结

    目录 1. sql注入概述 2. sql注入测试工具 3. sql注入防御方法 3.1 问题来源 3.2 防御方法 4. SQL注入防御举例 4.1 使用JDBC时,SQL语句进行了拼接 4.2 使用 ...

  8. SQLAlchemy的out join

    我有一个app表,一个usergroup表,还有一个app_access_map表.用以实现对app访问的白名单控制. app和usergroup是多对多关系,app_access_map是中间表,不 ...

  9. 第二周课堂笔记2th

    ---恢复内容开始--- 1. 2.索引取单个值 取多个值叫切片, 切片:取多个值 从左到右取值: 原则:顾头不顾尾 1, a[0:3] abc 2, a[-5:-2] abc 3, a[0:-2] ...

  10. 1 环境搭建_及参考资料介绍_ASM驱动开发疑难解决

    1 环境搭建 > 这里和  周壑老师不同:大家觉得哪中适合当前的物理机,就配置那种. 如下: win7 32 位: vs 2010; visualDDk 驱动项目插件: V A --- 代码高亮 ...