Structured Exception Handling
(结构化异常处理)也就是**SEH
,是 Microsoft 对 C 和 C++ 语言的一个扩展,用于适当地处理某些异常代码情况,包括软件异常和硬件异常。由于SEH
是存储在栈帧内,当我们无法直接溢出控制eip/rip
,我们就可以尝试通过控制SEH
来劫持程序。下文都基于32位程序的SEH
**。
SEH Stack Layout
存储在栈上的结构体是_EH3_EXCEPTION_REGISTRATION
。
_EH3_EXCEPTION_REGISTRATION
1 | struct _EH3_EXCEPTION_REGISTRATION |
我们通过汇编来观察程序是如何加载这个结构体到栈上的。
程序源码
1 |
|
由于栈是从地址高处往低处增长,所以是倒着放入对应的成员变量。
ScopeTable
对应的结构体为**_EH4_SCOPETABLE
**,也说明了PSCOPETABLE_ENTRY
是_EH4_SCOPETABLE
的指针。
1 | struct _EH4_SCOPETABLE |
_EH4_SCOPETABLE_RECORD
1 | struct _EH4_SCOPETABLE_RECORD |
_EH4_SCOPETABLE_RECORD
中的FilterFunc
对应着__except(...)
里小括号的代码,并且FilterFunc
的返回值必须为如下的值,是windows
用来判断是否要执行__except(...) {...}
大括号里的代码,也就是HandlerFunc
、还是已经处理完异常选择继续执行发生异常后的代码、还是这个SEH
无法处理传递到下一个SEH
来处理
1 | // Defined values for the exception filter expression |
对应的FilterFunc
和HandlerFunc
ExceptionHandler
_EXCEPTION_RECORD
1 | struct _EXCEPTION_RECORD |
_EXCEPTION_REGISTRATION_RECORD
1 | struct _EXCEPTION_REGISTRATION_RECORD |
细心点就会发现这个_EXCEPTION_REGISTRATION_RECORD
就是_EH3_EXCEPTION_REGISTRATION
的前两个成员变量。
_except_handler4_common
_except_handler4_common
在vcruntime140.dll
里,通过msdn
的pdb
和逆向还原,大致函数流程如下,无法保证正确性
1 | int __cdecl _except_handler4_common( |
__exception_handler4_common
最终会执行FilterFunc
和HandlerFunc
,且没有验证这两个函数的合法性,所以我们可以通过伪造栈上的_EH3_EXCEPTION_REGISTRATION
来实现任意函数执行。
异常处理流程
异常都是通过ntdll
里的KiUserExceptionDispatcher
函数来进行异常调度。
KiUserExceptionDispatcher
KiUserExceptionDispatcher
调用RtlDispatchException
来完成实际的调度。
1 | VOID NTAPI KiUserExceptionDispatcher (EXCEPTION_RECORD *ExceptionRecord, CONTEXT *Context) |
RtlDispatchException
开启SEHOP
和SafeSEH
会校验ExceptionChain
和ExceptionHandler
的合法性。
校验完后,最终执行ExceptionHandler
。
1 | void RtlDispatchException(...) // NT 6.3.9600 |
RtlIsValidHandler
1 | BOOL RtlIsValidHandler(Handler) // NT 6.3.9600 |
RtlpIsValidExceptionChain
大致意思是SEH
的地址要在栈上,ExceptionHandler
不能在栈上,且最后一个SEH
的Handler
要为RtlpFinalExceptionHandler
。
1 | char __fastcall RtlpIsValidExceptionChain( |