Windows逆向工程提升之x86结构化异常SEH处理机制
- 公开视频 -> 链接点击跳转公开课程
- 博客首页 -> 链接点击跳转博客主页
目录
SEH的派发流程
异常发生
遍历 SEH 链表
异常处理程序
栈展开
SEH的底层机制
异常存储
异常记录
上下文记录
SEH的存储方式
ScopeTable
tryLevel
示例代码
SEH的派发流程
异常发生
- 操作系统检测到异常,例如非法内存访问、除零等。
- 调用内核的专有机制(如 KiUserExceptionDispatcher),将异常信息传递给用户模式。
遍历 SEH 链表
- 系统通过 FS:[0] 找到当前 SEH 链表头节点。
- 遍历链表,每遇到一个节点时调用它的 Handler 函数,并将异常上下文作为参数传递给过滤器 __except。
异常处理程序
- 如果异常处理程序 (Handler) 返回 EXCEPTION_EXECUTE_HANDLER,则表示该处理器愿意处理异常,跳转到 __except 块。
- 如果没有找到处理器,则退出当前进程。
栈展开
- 如果处理器未完全解决异常,栈数据会逐层展开。
- 如果处理完成,则控制权返回到调用者。
SEH的底层机制
异常存储
- 链式结构:通过EXCEPTION_REGISTRATION_RECORD结构体链表实现,每个线程的FS:[0]指向当前处理链头部。
- 每个线程维护一个异常链表,用于记录当前线程中所有已注册的 SEH 处理器。
- 异常链表结构存储在栈中,当异常发生时,操作系统会遍历该链表以找到合适的异常处理程序。
struct _EXCEPTION_REGISTRATION_RECORD {_EXCEPTION_REGISTRATION_RECORD* Next; //下一个节点(链表)PEXCEPTION_ROUTINE Handler; //异常处理函数
};
异常记录
- 当异常发生时,操作系统生成一个 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, *PEXCEPTION_RECORD;typedef enum _EXCEPTION_DISPOSITION
{ExceptionContinueExecution,ExceptionContinueSearch,ExceptionNestedException,ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
-
ExceptionCode:标识异常类型。
-
ExceptionFlags:标识异常处理标志,是否可恢复等。
-
ExceptionAddress:异常发生的指令地址。
-
NumberParameters:异常附加参数个数。
上下文记录
- 保存并传递发生异常时的CPU状态(寄存器、堆栈等)。
- 例如代码执行前后的寄存器状态。
SEH的存储方式
- Windows SEH采用处理器分离模式,而不是直接存储用户的except块地址,主要出于以下考虑:
[ebp - 18]0x0073FCA4 58 fc 73 00 esp[ebp - 14]0x0073FCA8 42 f2 d3 60 exceptioninformation[ebp - 10]0x0073FCAC 28 fd 73 00 next node[ebp - 0c]0x0073FCB0 e2 15 be 00 handler(oep)[ebp - 08]0x0073FCB4 08 66 be 00 scopetable[ebp - 04]0x0073FCB8 ff ff ff ff trylevel[ebp + 00]0x0073FCBC dc fc 73 00 old ebp[ebp + 04]0x0073FCC0 13 1d be 00 ret addr
-
统一处理机制:
__except_handler3
作为通用入口点,为所有异常提供一致的处理框架 -
支持复杂逻辑:允许实现过滤器表达式评估、嵌套异常和异常转发
-
优化代码大小:避免为每个try/except块生成独立的处理函数
-
安全性考虑:通用处理器可以执行额外验证和安全检查
ScopeTable
- 定义:ScopeTable是编译器生成的静态数据表,每个条目描述一个__try块的作用域和处理逻辑。__except_handler3通过此表匹配当前执行的代码位置与异常处理块。
typedef struct _SCOPETABLE_ENTRY {DWORD EnclosingLevel; // 外层tryLevel(类似作用域链)DWORD FilterFunc; // 过滤器函数地址(__except括号内的表达式)DWORD HandlerFunc; // 异常处理代码地址(__except块内的代码)
} SCOPETABLE_ENTRY;
tryLevel
- 定义:tryLevel是一个栈帧局部变量(通常位于[ebp-4]),用于标识当前活动的__try块层级。
- 值范围:
- -1(0xFFFFFFFF):表示无活动的__try块。
- 0, 1, ...:索引值,对应ScopeTable中的条目,表示当前处于哪一层__try块。
示例代码
#include <stdio.h>
#include <Windows.h>int main()
{/*EXCEPTION_REGISTRATION_RECORDNext;Handler;scopeTabletryLevel[ebp - 18]0x0073FCA4 58 fc 73 00 esp[ebp - 14]0x0073FCA8 42 f2 d3 60 exceptioninformation[ebp - 10]0x0073FCAC 28 fd 73 00 next node[ebp - 0c]0x0073FCB0 e2 15 be 00 handler(oep)[ebp - 08]0x0073FCB4 08 66 be 00 scopetable[ebp - 04]0x0073FCB8 ff ff ff ff trylevel[ebp + 00]0x0073FCBC dc fc 73 00 old ebp[ebp + 04]0x0073FCC0 13 1d be 00 ret addr00E11C30 55 push ebp 00E11C31 8B EC mov ebp,esp 00E11C33 6A FF push 0FFFFFFFFh 00E11C35 68 08 66 E1 00 push 0E16608h 00E11C3A 68 E2 15 E1 00 push offset __except_handler3 (0E115E2h) 00E11C3F 64 A1 00 00 00 00 mov eax,dword ptr fs:[00000000h] 00E11C45 50 push eax 00E11C46 64 89 25 00 00 00 00 mov dword ptr fs:[0],esp [esp + 0] seh.node[esp + 4] __except_handler300E11C4D 83 C4 B8 add esp,0FFFFFFB8h 00E11C50 53 push ebx 00E11C51 56 push esi 00E11C52 57 push edi 00E11C53 89 65 E8 mov dword ptr [ebp-18h],esp 00E11C56 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0 00E11C5D C7 45 FC FF FF FF FF mov dword ptr [ebp-4],0FFFFFFFFh */int a = 1;__try{// [ebp - 04] trylevel = 0/* 0x01016608 ff ff ff ff ....0x0101660C 60 19 01 01 `...0x01016610 66 19 01 01 f... */a = 2;__try{// [ebp - 04] trylevel = 1/* 0x01016614 00 00 00 00 ....0x01016618 40 19 01 01 @...0x0101661C 46 19 01 01 F... */a = 3;__try{// [ebp - 04] trylevel = 2/*0x01016620 01 00 00 00 ....0x01016624 20 19 01 01 ...0x01016628 26 19 01 01 &...*/a = 4;}__except (EXCEPTION_EXECUTE_HANDLER){a = 5;}}__except (EXCEPTION_EXECUTE_HANDLER){a = 6;}}__except (EXCEPTION_EXECUTE_HANDLER){a = 7;}return 0;
}