---
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)

```
; 取得當前 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 程式碼

* 最終是呼叫到 vcruntime140.dll!_except_handler4_common
* 看一下 scopetable

* 其結構如下
```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 對應原始碼的程式


# 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