--- title: Windows SEH 機制探索 tags: security lang: zh_tw --- # Windows SEH 機制探索 [TOC] # Source Code ```cpp #include <Windows.h> #include <stdio.h> // 异常处理器: 由关键字 __try 和 __except 构成 // 如果 __try 中产生了异常,会执行过滤表达式中的内容 // 应该在过滤表达式提供的过滤函数中处理想要处理的异常 // 异常过滤表达式中最常见的情况就是编写一个异常过滤函数,对异常进行处理 DWORD ExceptionFilter(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo) { printf("ExceptionCode: %X\n", ExceptionCode); // 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常 if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO) { // 在这里对寄存器执行的所有修改都会直接被应用到程序中 ExceptionInfo->ContextRecord->Eax = 1; ExceptionInfo->ContextRecord->Ecx = 1; // 如果异常被处理了,那么就返回重新执行当前的代码 return EXCEPTION_CONTINUE_EXECUTION; } // 如果不是自己能够处理的异常,就不处理只报告 return EXCEPTION_EXECUTE_HANDLER; } int main() { int number = 0; __try { // __try 中的是可能产生异常的代码 // idiv eax, ecx number /= 0; } // 通常会为异常过滤表达式提供一个异常处理函数用于处理异常,并返回处理结果 // GetExceptionCode: 用于获取异常的类型,能在过滤表达式和异常处理器中使用 // GetExceptionInformation: 用于获取异常的信息,只能写在过滤表达式中 // 异常过滤表达式 __except (ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { // 异常处理器,只有 __except 返回 EXCEPTION_EXECUTE_HANDLER 才会执行 printf("__try 中产生了异常,但是并没有处理异常 %X\n", GetExceptionCode()); } printf("numebr = %d\n", number); return 0; } ``` * 來自 https://zhuanlan.zhihu.com/p/103258431 # Dynamic Analyze (32-bit) ![](https://i.imgur.com/Rzx5VVX.png) ``` ; 取得當前 Thread TIB (Thread Information Block) mov eax, dword ptr fs:[0] ``` * TEB 的開頭存著 TIB 位址 * fs:\[0\] 也存著 TIB 位址 * TIB 結構如下 ```c typedef struct _NT_TIB { struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; PVOID StackBase; PVOID StackLimit; PVOID SubSystemTib; union { PVOID FiberData; DWORD Version; }; PVOID ArbitraryUserPointer; struct _NT_TIB *Self; } NT_TIB; ``` ``` lea eax, dword ptr ss:[ebp-10] mov dword ptr fs:[0], eax ``` * 從 function 的開頭來看, stack 長相如下 | Register | Address | Value | | -------- | -------- | -------- | | | ... | ... | | | ebp - 10 | TIB address | | | ebp - c | seh_test.391133 | | | ebp - 8 | seh_test.392548 | | | ebp - 4 | 0xFFFFFFFE | | ebp -> | ebp - 0 | old ebp value | * 將新的 \_EXCEPTION_REGISTRATION_RECORD 填入 TIB, 位址是 ebp - 10 * \_EXCEPTION_REGISTRATION_RECORD 結構如下 ```c typedef struct _EXCEPTION_REGISTRATION_RECORD { struct _EXCEPTION_REGISTRATION_RECORD *Prev; PEXCEPTION_ROUTINE Handler; } EXCEPTION_REGISTRATION_RECORD; ``` * VC 將其擴展成以下 ```c struct VC_EXCEPTION_REGISTRATION { VC_EXCEPTION_REGISTRATION* prev; FARPROC handler; scopetable_entry* scopetable; int _index; DWORD _ebp; } ``` * 對應一下 * Prev: TIB address * Handler: seh_test.391133 * scopetable: seh_test.392548 * \_index: 0xFFFFFFFE * \_ebp: old ebp value * 現在來看一下 Handler 程式碼 ![](https://i.imgur.com/0GxnG6S.png) * 最終是呼叫到 vcruntime140.dll!_except_handler4_common * 看一下 scopetable ![](https://i.imgur.com/c12pJ6F.png) * 其結構如下 ```c struct _EH4_SCOPETABLE { DWORD GSCookieOffset; DWORD GSCookieXOROffset; DWORD EHCookieOffset; DWORD EHCookieXOROffset; _EH4_SCOPETABLE_RECORD ScopeRecord[]; }; ``` * 推估是以 ScopeRecord.EnclosingLevel 和 \_index 去比對說是否為對應的 except 區塊, HandlerFunc 回傳值決定了是否會再繼續往下個 \_EH4_SCOPETABLE (scopetable_entry) 尋找 * 4种返回值及含义 1. ExceptionContinueExecution(0):回调函数处理了异常,可以从异常发生的指令处重新执行。 2. ExceptionContinueSearch(1):回调函数不能处理该异常,需要要SEH链中的其他回调函数处理。 3. ExceptionNestedException(2):回调函数在执行中又发生了新的异常,即发生了嵌套异常 4. ExceptionCollidedUnwind(3):发生了嵌套的展开操作 * 可以看到第一個 scopetable_entry 的 FilterFunc 和 HandlerFunc 對應原始碼的程式 ![](https://i.imgur.com/kpfV3Ch.png) ![](https://i.imgur.com/kkKp7Ms.png) # Reference * https://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf * https://www.cnblogs.com/yilang/p/11233935.html * https://zh.wikipedia.org/wiki/%E7%BB%93%E6%9E%84%E5%8C%96%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86