GS Cookie 和异常处理程序

逆向工程 x86 缓冲区溢出 例外
2021-07-04 04:50:14

我试图了解这个 GS cookie 实现是如何工作的。从我读到的关于这个主题的内容来看,在序言期间设置了一个 cookie,然后在尾声中再次检查。好吧,我可以看到正在设置的 cookie,但这与我在网上看到的示例不同。

序幕:

push ebp
mov ebp,esp
push FFFFFFFF
push sdk.FAB99E9               ; New Exception handler
mov eax,dword ptr fs:[0]       ; Old Exception handler
push eax
sub esp,14                     ; Allocate 5 DWORDs
push ebx
push esi
push edi
mov eax,dword ptr ds:[FAC635C] ; The GS cookie (.data section)
xor eax,ebp
push eax                       ; The GS Cookie
lea eax,dword ptr ss:[ebp-C]
mov dword ptr fs:[0],eax       ; Set new Exception Handler
mov edi,ecx                    ; Address of a vector passed as arg1
mov dword ptr ss:[ebp-20],edi  ; local variable
mov dword ptr ss:[ebp-1C],0    ; local variable
mov dword ptr ds:[edi],0       ; zero the vector
mov dword ptr ds:[edi+4],0
mov dword ptr ds:[edi+8],0
mov dword ptr ss:[ebp-4],0     ; sets the 0xFFFFFFFF above to 0x0
mov dword ptr ss:[ebp-1C],1
mov byte ptr ss:[ebp-D],0

结语:

mov eax,edi
mov ecx,dword ptr ss:[ebp-C]   ; Old Exception handler
mov dword ptr fs:[0],ecx       ; Restore Exception handler
pop ecx                        ; Pop the GS Cookie
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 

注释为“新异常处理程序”的函数确实包含 Cookie 检查。但我一直无法踏入其中。

任何人都可以解释它是如何工作的以及它在 C++ 中的样子吗?或者,更具体到我的需求,这是原始代码中的 try/catch 块还是由编译器添加的?

1个回答

注释为“新异常处理程序”的函数确实包含 Cookie 检查。但我一直无法踏入其中。

由于异常处理程序仅在异常期间被调用,因此在正常执行期间不会被调用,因此这是预期行为。

至于结语中缺少的 cookie 检查,显然您的函数没有使用普通的旧GScookie(通过简单比较的返回地址覆盖保护),而仅使用改进的EH+GScookie(局部变量 + EH 数据 + 带有额外 XOR 检查的返回地址保护) .

注释函数序言(注释指的是帧中的最终偏移量):

 push    ebp             ; [EBP+0] save old EBP
 mov     ebp, esp        ; set up ebp frame (ESP=EBP)
 push    0FFFFFFFFh      ; [EBP-4] push initial state
 push    offset SEH_1000BF50 ; [EBP-8] SEH handler for the function
 mov     eax, large fs:0 ; read SEH chain list head
 push    eax             ; [EBP-C] pointer to previous SEH record
 sub     esp, 14h        ; allocate space for local vars and move to the saved registers area.
 push    ebx             ; [EBP-18] save ebx
 push    esi             ; [EBP-1C] save esi
 push    edi             ; [EBP-20] save edi
 mov     eax, ___security_cookie
 xor     eax, ebp        ; xor the cookie to make it harder to forge
 push    eax             ; EBP-24 push XOR'ed cookie
 lea     eax, [ebp-0Ch] ; eax = &SEH_record
 mov     large fs:0, eax ; insert our SEH entry at start of list

最终堆栈框架布局:

EBP-30 xored EH cookie  
EBP-2C saved edi
EBP-28 saved esi
EBP-24 saved ebx
EBP-20 <local variables>
EBP-0C SEH pointer to previous record\
EBP-08 SEH handler                    | extended SEH record
EBP-04 EH state                      /
EBP+00 saved EBP
EBP+04 return address

在发生异常的情况下,在跳转到___CxxFrameHandler3执行 C++ 异常处理或堆栈展开之前在特定于函数的 SEH 处理程序中进行检查:

SEH_1000BF50:
  mov     edx, [esp+8]    ; (1) get pointer to current SEH record
  lea     eax, [edx+0Ch]  ; edx <- original frame pointer (function's EBP)
  mov     ecx, [edx-24h]  ; get xor'ed cookie at orig_ebp-30h
  xor     ecx, eax        ; xor them together
  call    @__security_check_cookie@4 ; check expected value
  mov     eax, offset stru_10034344
  jmp     ___CxxFrameHandler3   ; handle C++ exceptions/unwinding

(1) 获取 SEH 处理程序 ( EstablisherFrame)的第二个参数,该参数指向存储在EBP-C函数中的活动 SEH 记录,因此我们对其进行调整0Ch以获取EBP用于与 cookie 值进行异或的原始值(最初存储在[EBP-30h],或来自 SEH 记录的 -24h),并将异或结果与预期值进行比较__security_check_cookie()

我猜编译器决定不需要只保护返回地址,因为函数中没有缓冲区(请参阅MSDN 中的“GS 缓冲区” ),并且任何其他覆盖都会破坏 EH cookie 并在以下情况下被检测到例外。

顺便说一句,state变量 at[ebp-4]唯一标识了函数执行中的状态。这允许C ++异常处理程序(例如__CxxFrameHandler)以破坏任何自动对象离开其范围,由于该异常和放松的状态下,以起始一个(通常值-1)。因此,此异常处理程序设置并不对应于“try/catch 块”,而是由编译器为整个函数添加的。但是,状态转换(写入 try 级别变量可能对应于 try 块边界(或仅作用域边界)。

有关 Visual C++ 异常处理实现(C 样式 SEH 和 C++ EH)的更多背景信息,请查看关于该主题的文章(及其参考资料)。