SEH X86
( windows 提供的异常处理机制实际上只是一个简单的框架,一般情况下开发人员都不会直接用到。我们通常所用的异常处理(比如 C++ 的 throw、try、catch)都是编译器在系统提供的异常处理机制上进行加工了的增强版本。这里先抛开增强版的不提,主要叙述原始版本。)
0x01 系统如何找到异常链表
结构化异常处理是以线程为基础的。线程的内核数据结构体现是 _ETHREAD,从它开始进入,通过windbg,找到所关注的异常链表(win7 x86):

查看 _KTHREAD结构(dt _KTHREAD):

查看TEB:

查看TIB:

_NT_TIB 的第一个域成员 ExceptionList 就是异常链表头。
但是系统不是这么一步一步找的,而是借助 FS 寄存器来加速寻找。先来说说系统对 FS 的使用。
在应用层,FS 寄存器“指向”当前执行线程的 _TEB 结构体。在内核层,FS 寄存器“指向”另一个跟 CPU 相关的结构体:_KPCR,来看看它的结构,
与 _TEB 一样,它的第一个域成员也是 _NT_TIB,只不过此时是 nt!_NT_TIB,而在应用层是 ntdll!_NT_TIB,但它们的结构是一样的。
这样,不论在应用层还是在内核层,系统都可以使用 FS:[0] 找到异常链表。
0x02 异常处理相关的结构
(1)_EXCEPTION_REGISTRATION_RECORD
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
EXCEPTION_REGISTRATION_RECORD 结构就是登记信息。数据成员:
1. EXCEPTION_REGISTRATION_RECORD::Next 域指向下一个 EXCEPTION_REGISTRATION_RECORD,由此构成一个异常登记信息(从字面上说,应该叫做“异常注册记录”更恰当)链表。链表中的最后一个结点会将 Next 置为 EXCEPTION_CHAIN_END,表示链表到此结束。
2. EXCEPTION_REGISTRATION_RECORD::Handler 指向异常处理函数。
(2)异常的回调函数
EXCEPTION_DISPOSITION
__cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext);
函数参数:
1..第一个参数ExceptionRecord,是一个指向EXCEPTION_RECORD结构的指针
2.第二个参数是一个指向establisher帧结构的指针。这个异常帧结构就是_EXCEPTION_REGISTRATION_RECORD。
3.第三个参数是一个指向CONTEXT结 构的指针。此结构在WINNT.H中定义,它代表某个特定线程的寄存器值。当用于SEH时,CONTEXT结构表示 异常发生时寄存器的值。顺便说一下,这个CONTEXT结构就是GetThreadContext和SetThreadContext这两个API中使用 的那个CONTEXT结构。
4.第四个参数是_DispatcherContext,用于存放下一个异常帧,全局展开在执行当前异常帧局部展开中发生嵌套展开时使用。
typedef struct _CONTEXT {
//
// The flags values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//
DWORD ContextFlags;
//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
//
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
//
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
//
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_INTEGER.
//
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_CONTROL.
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
typedef CONTEXT *PCONTEXT;
(3)EXCEPTION_RECORD结构
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //操作系统提供给异常的一个数,代表异常发生的原因。
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
参数 ExceptionCode 是操作系统分配给异常的号。在 WINNT.H 文件中查找开头为“STATUS_” 的宏就能找到一大堆这样的异常 代号。例如,大家熟知的 STATUS_ACCESS_VIOLATION 的代号就是 0xC0000005。更为完整的异常代号可以 从 Windows NT DDK 中的 NTSTATUS.H 文件里找到。
EXCEPTION_RECORD 结构体的第四个元素是异常发生处的地 址。其余的 EXCEPTION_RECORD 域目前都可以忽略掉。
0x03 处理过程
当接收到异常后,系统找到当前线程(前面说过异常是线程相关的。系统接收到的异常就是当前正在运行的线程触发的。其实这个说法还不准确,DPC 也会触发异常,而它是线程无关的,这里为了方便理解,当前先只考虑线程)的异常链表,从链表中的第一个结点开始遍历,找到一个 EXCEPTION_REGISTRATION_RECORD 就调用它的 Handler,并把该异常(由第一个类型为 EXCEPTION_RECORD 的参数表示)传递给该 Handler,Handler 处理并返回一个类型为 EXCEPTION_DISPOSITION 的枚举值。该返回值指示系统下一步该做什么:
ExceptionContinueExecution ——已修正了此异常的故障,重新执行异常产生出代码。
ExceptionContinueSearch ——没有处理此异常,请继续搜索其他的解决方案
ExceptionNestedException 和 ExceptionCollidedUnwind 暂不解释。
这样系统根据不同的返回值来继续遍历异常链表或者回到触发点继续执行。
(1)内核处理流程
在windows kernel中,存在一张中断描述符表(IDT, Interupt Descriptor Table). IDT是一张位于内核态物理内存中的线性表,其有256个表项。IDT中的每个表项叫做门描述符(Gate Descriptor)。门描述符的基本作用就是将CPU异常对应的中断号与其对应的异常处理函数KiTrapXX关联起来。
例如,0号中断(即除0错误)对应的处理例程为nt!KiTrap00
可以在windbg中用!idt -a命令查看。

首先,CPU 执行的指令触发了异常,CPU 改执行 IDT 中 KiTrapXX,KiTrapXX 会调用 KiDispatchException。该函数原型如下:
VOID
KiDispatchException (
IN PEXCEPTION_RECORD ExceptionRecord,
// 指向 ExceptionRecord的指针
IN PKEXCEPTION_FRAME ExceptionFrame,
// 对于X86 为空
IN PKTRAP_FRAME TrapFrame,
//函数异常记录块指针,由调用者 KiKernelTrapHandler 传递
IN KPROCESSOR_MODE PreviousMode,
// 指示内核态还是用户态
IN BOOLEAN FirstChance
// 是否是第一次尝试
);
SEH X86的更多相关文章
- Windows SEH学习 x86
windows 提供的异常处理机制实际上只是一个简单的框架.我们通常所用的异常处理(比如 C++ 的 throw.try.catch)都是编译器在系统提供的异常处理机制上进行加工了的增强版本.这里先抛 ...
- Kingsoft Office Writer 2012 8.1.0.3385 - (.wps) Buffer Overflow Exploit (SEH)
#!/usr/bin/python # Exploit Title: Kingsoft Office Writer v2012 8.1.0.3385 .wps Buffer Overflow Expl ...
- 第25章 SEH结构化异常处理_未处理异常及向量化异常
25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...
- 第23章 SEH结构化异常处理(1)_系统SEH机制
23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...
- KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态获取 《寒江独钓》内核学习笔记(5)
目录 . 相关阅读材料 . <加密与解密3> . [经典文章翻译]A_Crash_Course_on_the_Depths_of_Win32_Structured_Exception_Ha ...
- KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态
KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态获取 <寒江独钓>内核学习笔记(5) 继续我们的线程相关的数据结构的学习.接下来我们学习 KTH ...
- Windows x86 下的 静态代码混淆
0x00 前言 静态反汇编之王,毫无疑问就是Ida pro,大大降低了反汇编工作的门槛,尤其是出色的“F5插件”Hex-Rays可以将汇编代码还原成类似于C语言的伪代码,大大提高了可读性.但个人觉得 ...
- SEH分析笔记(X64篇)
SEH分析笔记(X64篇) v1.0.0 boxcounter 历史: v1.0.0, 2011-11-4:最初版本. [不介意转载,但请注明出处 www.boxcounter.com 附件里有本文 ...
- SEH结构
首先有几点问题 1.在后文中看到的PE的节中的配置信息表Load configuration是对SEH回调函数的注册,那么Exception Table是加载的什么信息. 2.什么时候走进系统异常,什 ...
随机推荐
- spring boot + vue + element-ui全栈开发入门——前后端整合开发
一.配置 思路是通过node的跨域配置来调用spring boot的rest api. 修改config\index.js文件,设置跨域配置proxyTable: proxyTable: { '/ap ...
- 一个简单好用的日志框架NLog
之前我介绍过如何使用log4net来记录日志,但最近喜欢上了另一个简单好用的日志框架NLog. 关于NLog和log4net的比较这里就不多讨论了,感兴趣的朋友可以参看.NET日志工具介绍和log4n ...
- hdu5029 Relief grain
题目链接 树剖+线段树 将区间修改转化为单点修改,因为如果按DFS序进行修改,那么一定会对DFS序更大的点造成影响 #include<iostream> #include<vecto ...
- hdu4916 Count on the path
调了好久.... •把树视为以1为根的有向树,然后将1删除 •原树变为一个森林,并且任一棵树的根节点均为原树中1的子节点 •只需要考虑最小编号前3小的三棵树 •记f[x][y]为去掉x和y两棵树后的最 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- Linux下系统时间函数、DST等相关问题总结(转)
Linux下系统时间函数.DST等相关问题总结 下面这个结构体存储了跟时区相关的位移量(offset)以及是否存在DST等信息,根据所在的时区信息,很容易找到系统时间与UTC时间之间的时区偏移,另外根 ...
- P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)
P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...
- MySQL教程 3.3
本实验是将数据加载到表中,具体步骤如下: 先创建数据库menagerie,再启用数据库,然后在库中创建表pet. 运行如下代码,将文本文件pet.txt加载到pet表中,但出现ERROR 1148.查 ...
- SSM登录跳转到登录页,登录页不能加载js和样式
SSM登录跳转到登录页,登录页不能加载js和样式选用jsppage添加根路径. <% String rootPath = request.getContextPath(); %> < ...
- 微信小程序(一)快递查询
2007 年 1 月 9 日,乔布斯在旧金山莫斯科尼会展中心发布了首款 iPhone,而在十年后的 1 月 9 日,微信小程序正式上线.张小龙以这样的形式,向乔布斯致敬. 小程序在哪里? 小程序功能模 ...