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链的头指针```ctypedef 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 ;删除 异常处理器<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, ExceptHandlerpush eaxpush fs : [0]mov dword ptr fs : [0], ESP}__asm{xor eax,eaxmov dword ptr[eax],1234h}printf("删除SEH\n");__asm{pop dword ptr fs : [0]add esp, 4}printf("dwTest=%X\n", dwTest);getchar();return 0;}
