TEB、PEB、SEH
一、TEB
- 定义
- 线程环境块(TEB),存放着进程中所有线程的各种信息,每一个线程有一个TEB。
- 访问方法
- ntdll.NtCurrentTeb()函数用来返回当前线程的TEB结构指针,即fs:[0x18]的值,保存FS段寄存器在内存中的镜像地址(即fs:[0]的地址)。(fs:[0x18]->fs:[0]->SEH chain)
- TEB结构(每个操作系统中稍有不同,这里以winXP为例)
+0x000 NtTib : _NT_TIB
- TEB结构体的第一个成员是TIB(线程信息块)
typedef struct _NT_TIB //sizeof 1ch
{
//指向_EXCEPTION_REGISTRATION_RECORD结构体的链表指针(SEH)
00h struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
//线程堆栈顶部
04h PVOID StackBase;
//这里为线程堆栈底部
08h PVOID StackLimit;
0ch PVOID SubSystemTib;
union {
PVOID FiberData;
10h DWORD Version;
};
14h PVOID ArbitraryUserPointer;
//这为_NT_TIB结构体的自引用指针,即为NtCurrentTeb() 函数所读出的TEB结构体指针
18h struct _NT_TIB *Self;
}NT_TIB;
- TEB结构体的第一个成员是TIB(线程信息块)
+0x020 ClientId : _CLIENT_ID
- UniqueProcess:这个为当前进程的的Pid,可用函数 GetCurrentProcessId() 访问当前结构体成员获取进程标识符
- +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
- PEB结构体的指针,所以说一般 fs:[0x30] 即为PEB的起始地址
二、PEB
定义
- 进程环境块(Process Envirorment Block),存放着进程信息的结构体
PEB结构
+0x002 BeingDebugged : UChar(一个字节)
- 当前进程是否处于调试状态,也就是函数IsDebuggerPresent() 所访问的结构体成员
+0x008 ImageBaseAddress : Ptr32 Void
- 自身的 ImageBase(映像基址)和PE结构中的 IMAGE_OPTIONAL_HEADER.ImageBase。可用函 GetModuleHandle (0) 获取自身模块句柄来访问这个结构体成员,调用该函数,当判断参数为0时,fs:[0x30](->eax)访问PEB,[eax+0x8]访问ImageBaseAddress。
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
- 指向 _PEB_LDR_DATA 的结构体指针,当DLL加载到进程,可从 PEB.Ldr 中获取该模块的基址和其他信息(通用shellcode编写,查找API地址并加载)
ntdll!_PEB_LDR_DATA
+0x000 Length //结构体大小
+0x004 Initialized //进程是否初始化完成
+0x008 SsHandle
//按加载顺序
+0x00c InLoadOrderModuleList : _LIST_ENTRY //指向LIST_ENTRY的结构体指针
//按在虚拟空间中的位置
+0x014 InMemoryOrderModuleList : _LIST_ENTRY//指向LIST_ENTRY的结构体指针
// 按初始化顺序
+0x01c InInitializationOrderModuleList : _LIST_ENTRY//指向第一个初始化模块(ntdll)的_LDR_DATA_TABLE_ENTRY
+0x024 EntryInProgress
+0x028 ShutdownInProgress
+0x02c ShutdownThreadId
_LIST_ENTRY是一个双向链表,链表中存放着_LDR_DATA_TABLE_ENTRY 的结构体信息
//_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY
_LDR_DATA_TABLE_ENTRY结构体就是包含进程加载的DLL模块的所有信息(包括三个链表的链表指针_LIST_ENTRY)
- +0x018 ProcessHeap : Ptr32 Void
- 进程堆的句柄,也就是指向结构体HEAP的指针,可用函数 GetProcessHe**ap**()获取
- 0x00c Flags :Uint48 0x0010 Flags :Uint48
- 程序正常运行时,ProcessHeap.Flags的值为2 , ProcessHeap.ForceFlags 的值为0,也常用于反调试。
- +0x068 NtGlobalFlag : Uint4B
- 调试状态时,PEB.NtGlobalFlag 的值为0x70
**
- 作用:在shellcode注入时获取程序加载后dll在内存中的基址,方便进一步dll导出函数的调用
三、SEH
1、定义
- SEH(Structured Exception Handling),结构化异常处理,Windows操作系统提供的强大异常处理功能。而Visual C++中的try{}/finally{}和try{}/except{}结构本质上是对Windows提供的SEH的封装
- SEH是基于线程的异常处理
- 模拟异常处理
- 把EAX的值置为空指针,然后向空指针里写入值,引发 STATUS_ACCESS_VIOLATION(内存访问异常),然后在异常处理里面把EAX的值设置为变量dwTest的地址,然后返回 EXCEPTION_CONTINUE_EXECUTION 表示异常被处理,从异常处继续执行。
- MSDN对于异常处理( Exception Handling )返回值的定义:
#define EXCEPTION_EXECUTE_HANDLER 1 //表示异常被处理,从下一条指令开始执行
#define EXCEPTION_CONTINUE_SEARCH 0 //表示异常未被处理,交由下一个SEH
#define EXCEPTION_CONTINUE_EXECUTION -1 //表示异常被处理,从异常处开始执行
2、Windows下各种异常处理的优先级
异常处理种类
VEH(向量化异常处理,最顶端的异常处理)
- 向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部,0为底部),异常处理中最先执行
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
- 向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部,0为底部),异常处理中最先执行
VCH(最低端的异常处理 )
- 向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部, 0为底部 ,异常处理中最后执行
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
- 向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部, 0为底部 ,异常处理中最后执行
SEH(结构化异常处理,基于线程栈的异常处理)
- SEH是基于线程的异常处理,因为SEH链指针是在TEB(线程信息块)的第一个结构体成员(NT_TIB)的头部:fs:[0]
UEF(TopLevelEH,顶级异常处理)
- TopLevelEH 为线程顶级异常处理器,通常可以处理到所有线程消息发生的异常。
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
_In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
- TopLevelEH 为线程顶级异常处理器,通常可以处理到所有线程消息发生的异常。
优先级
- 调试器>VEH(进程相关)>SEH(线程相关)>UEF(基于SEH)>VCH(进程相关)
3、EXCEPTION_POINTERS 结构体
异常处理回调函数参数大都为 EXCEPTION_POINTERS 结构体,异常发生时,系统向引起异常的线程的堆栈里压入该结构
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
EXCEPTION_RECORD
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
DWORD ExceptionFlags; //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
PVOID ExceptionAddress; //异常发生的地址
DWORD NumberParameters; //下面ExceptionInformation所含有的dword数目
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加消息,如读或写冲突
} EXCEPTION_RECORD;
CONTEXT:通过修改CONTEXT结构中的成员,可以设置线程的环境,将调试寄存器清零,使断点失效,以达到反跟踪的目的。 ```c typedef struct _CONTEXT {
// // The flags values within this flag control the contents of // a CONTEXT record. // // If the context record is used as an input parameter, then // for each portion of the context record controlled by a flag // whose value is set, it is assumed that that portion of the // context record contains valid context. If the context record // is being used to modify a threads context, then only that // portion of the threads context will be modified. // // If the context record is used as an IN OUT parameter to capture // the context of a thread, then only those portions of the thread’s // context corresponding to set flags will be returned. // // The context record is never used as an OUT only parameter. //
DWORD ContextFlags;
// // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL. //
DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7;
// // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. //
FLOATING_SAVE_AREA FloatSave;
// // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_SEGMENTS. //
DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs;
// // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_INTEGER. //
DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax;
// // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_CONTROL. //
DWORD Ebp; DWORD Eip; DWORD SegCs; // MUST BE SANITIZED DWORD EFlags; // MUST BE SANITIZED DWORD Esp; DWORD SegSs;
// // This section is specified/returned if the ContextFlags word // contains the flag CONTEXT_EXTENDED_REGISTERS. // The format and contexts are processor specific //
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
<a name="8nWPT"></a>
#### 4、认识SEH链
- SEH链表位于结构体NT_TIB的第一个结构体成员,而结构体NT_TIB也位于TEB的第一个结构体成员,一句话而言SEH链表指针位于寄存器FS:[0]的位置:即TIB结构体中的struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList为SEH链的头指针
```c
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
- 第一个成员 Next 为指向下一个链表的指针,直到遇到 0xFFFFFFFF 结束,而结构体成员 Handler 为SEH的异常处理函数指针
SEH异常处理函数 EXCEPTION_ROUTINE 的定义
typedef _IRQL_requires_same_ _Function_class_(EXCEPTION_ROUTINE) EXCEPTION_DISPOSITION NTAPI EXCEPTION_ROUTINE (
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext
);
该异常处理函数是以 EXCEPTION_DISPOSITION 为返回值的回调函数
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution, //继续执行异常代码ExceptionContinueSearch,//运行下一个异常处理器
ExceptionNestedException,//在OS内部使用
ExceptionCollidedUnwind //在OS内部使用
} EXCEPTION_DISPOSITION;
5、SEH处理机制
- SEH接收到异常然后处理,处理失败返回 ExceptionContinueSearch(1) 继续运行下一个Handler处理,直到返回ExceptionContinueSearch(0),若是一直处理不了直到遇到0xFFFFFFFF 把异常交给UEF处理。
6、SEH的注册及SEH的删除
- SEH的异常处理的定义为:EXCEPTION_ROUTINE
- SEH的注册:新建一个_EXCEPTION_REGISTRATION_RECORD,插入fs:[0]指向的链表表头
```c
; 构成了一个新的_EXCEPTION_REGISTRATION_RECORD结构,此时它的位置就在栈顶,即esp指向的位置
;1、PEXCEPTION_ROUTINE Handler
push @_except_handler
;2、_EXCEPTION_REGISTRATION_RECORD *Next push dwod ptr fs:[0]
; esp(也就是最新的链表头)保存到fs:[0]中也就是修改TIB结中的ExceptionList,相当于向链表中插入了一个新节点 mov dwod ptr fs:[0],esp ;添加链表
- 卸载SEH<br />pop dword ptr fs:[0] ;还原链表头<br />add esp,4 ;删除 异常处理器
![21.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/1756019/1615358786473-e252912d-3646-46c4-9349-ea353b499e5c.jpeg#align=left&display=inline&height=458&margin=%5Bobject%20Object%5D&name=21.jpg&originHeight=458&originWidth=1256&size=66971&status=done&style=none&width=1256)
<a name="rhTdZ"></a>
#### 7、异常的种类和常见的异常代码
- STATUS_ACCESS_VIOLATION(0xC0000005)
- 非法访问异常,试图访问不存在、没有访问权限,或是试图向没有写入权限的地址或是向内核区域写入发生的异常。
- STATUS_BREAKPOINT(0x80000003)
- 断点异常,INT 3(0xCC)断点
- STATUS_ILLEGAL_INSTRUCTION(0xC000001D)
- CPU遇到无法解析的指令时发生该异常
- STATUS_INTEGER_DIVIDE_BY_ZERO(0xC0000094)
- 除法中,分母为0时发生的异常
- STATUS_SINGLE_STEP
- 单步调试异常,在EFlag寄存器把TF标志位置1发生的单步调试异常
<a name="x4Zs6"></a>
#### 8、SEH异常处理实例
```c
// SEHList.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
DWORD dwTest;
EXCEPTION_DISPOSITION NTAPI ExceptHandler(
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext){
printf("进入异常处理\n");
printf("异常地址:%X<异常代码:%X>\n", ExceptionRecord->ExceptionAddress,
ExceptionRecord->ExceptionCode);
ContextRecord->Eax = (DWORD)(&dwTest);
return ExceptionContinueExecution;
}
int _tmain(int argc, _TCHAR* argv[])
{
printf("注册SEH\n");
__asm{
lea eax, ExceptHandler
push eax
push fs : [0]
mov dword ptr fs : [0], ESP
}
__asm{
xor eax,eax
mov dword ptr[eax],1234h
}
printf("删除SEH\n");
__asm{
pop dword ptr fs : [0]
add esp, 4
}
printf("dwTest=%X\n", dwTest);
getchar();
return 0;
}