windows栈溢出部分关于SEH的利用 (本文不探究safeSEH相关机制,不绕过safeSEH机制对SEH异常处理函数指针的检查,也就是不讨论覆盖SEH函数指针的情况;而是去绕过__except_handler4异常处理函数内部的检测,实现伪造__except或__finally函数)
先看一个编译好的程序main函数入口处代码。SEH 的使用范围是线程相关的,每个线程都有自己的函数(SEH链表是局部链表,在堆栈中)
Copy . text : 004010B0 push ebp
. text : 004010B1 mov ebp , esp
. text : 004010B3 push 0FFFFFFFEh // ebp - 4
. text : 004010B5 push offset _EH4_SCOPETABLE_addr // ebp - 8
. text : 004010BA push offset __except_handler4 // ebp - c
. text : 004010BF mov eax , large fs : 0
. text : 004010C5 push eax // ebp - 10
. text : 004010C6 add esp , 0FFFFFF40h
. text : 004010CC mov eax , ___security_cookie
. text : 004010D1 xor [ ebp - 8 ], eax // 加密scopeTable
. text : 004010D4 xor eax , ebp
. text : 004010D6 mov [ ebp - 1Ch ], eax // 放入 GS
. text : 004010D9 push ebx
. text : 004010DA push esi
. text : 004010DB push edi
. text : 004010DC push eax
. text : 004010DD lea eax , [ ebp - 10h ]
. text : 004010E0 mov large fs : 0 , eax 先看push offset _EH4_SCOPETABLE_addr这条指令,在main函数入口处被压入栈中。_EH4_SCOPETABLE为SEH的scope table结构,它保存了当前函数 中__try块相匹配的 __except 或 __finally的地址值.
_EH4_SCOPETABLE通常被保存在程序的.rdata段。
细看_EH4_SCOPETABLE,它的C结构如下:
其中FilterFunc与FinallyFunc就是我们自定义的__except 或 __finally函数的地址。
紧接着下面三条指令,作用是在栈中为当前线程添加异常处理。
科普 :
1.TIB结构 TIB,又称线程信息块,是保存线程基本信息的数据结构,它位于TEB的头部。TEB是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TEB。
TIB结构如下:
在这个结构中与异常处理有关的第一个成员:指向_EXCEPTION_REGISTRATION_RECORD结构的Exceptionlist指针
2.EXCEPTION_REGISTRATION_RECORD 结构
该结构主要用于描述线程异常处理过程的地址,多个该结构的链表描述了多个线程异常处理过程的嵌套层次关系
结构如下:
结构如图所示:
fs寄存器指向TEB结构
所以 上面lea eax, [ebp-10h]与mov large fs:0, eax指令也就是在栈中插入一个SEH异常处理结构体到TIB顶部, __except_handler4就是添加的系统默认异常处理回调函数,当发生异常时会首先执行它。
我们跟进__except_handler4中
里面又嵌套调用了except_handler4_common函数
里面会检查栈中放入的GS值,会根据securityCookies解密_EH4_SCOPETABLE的地址,最终会调用到_EH4_SCOPETABLE里面的FilterFunc与FilterFunc函数,也就是我们自定义的__except 或 __finally函数的地址。
如果我们能够查询伪造一个_EH4_SCOPETABLE结构,里面的FilterFunc函数指针写成自己的,其他字段不改变,覆盖栈中的_EH4_SCOPETABLE_addr为伪造地址,就能实现任意地址函数调用。
不过由于
指令中的[ebp-8]是_EH4_SCOPETABLE_addr, 所以我们还需要计算new_EH4_SCOPETABLE_addr=fake__EH4_SCOPETABLE_addr ^ ___security_cookie才行,___security_cookie的实际值需要leak。
由于
覆盖存入栈ebp-0x1c的GS值时也应该注意这点, 也需要先leak出ebp与___security_cookie值后 再计算new_GS=___security_cookie ^ ebp的值 再进行覆盖。
所以要实现这种SEH利用,要泄露的地方其实挺多的。