TEB、PEB、SEH

一、TEB

  • 定义
    • 线程环境块(TEB),存放着进程中所有线程的各种信息每一个线程有一个TEB
  • 访问方法
    • ntdll.NtCurrentTeb()函数用来返回当前线程的TEB结构指针,即fs:[0x18]的值,保存FS段寄存器在内存中的镜像地址(即fs:[0]的地址)。(fs:[0x18]->fs:[0]->SEH chain
  • TEB结构(每个操作系统中稍有不同,这里以winXP为例)
  1. +0x000 NtTib : _NT_TIB

    • TEB结构体的第一个成员是TIB(线程信息块)
      1. typedef struct _NT_TIB //sizeof 1ch
      2. {
      3. //指向_EXCEPTION_REGISTRATION_RECORD结构体的链表指针(SEH)
      4. 00h struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
      5. //线程堆栈顶部
      6. 04h PVOID StackBase;
      7. //这里为线程堆栈底部
      8. 08h PVOID StackLimit;
      9. 0ch PVOID SubSystemTib;
      10. union {
      11. PVOID FiberData;
      12. 10h DWORD Version;
      13. };
      14. 14h PVOID ArbitraryUserPointer;
      15. //这为_NT_TIB结构体的自引用指针,即为NtCurrentTeb() 函数所读出的TEB结构体指针
      16. 18h struct _NT_TIB *Self;
      17. }NT_TIB;
  2. +0x020 ClientId : _CLIENT_ID

    • UniqueProcess:这个为当前进程的的Pid,可用函数 GetCurrentProcessId() 访问当前结构体成员获取进程标识符
  3. +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
    • PEB结构体的指针,所以说一般 fs:[0x30] 即为PEB的起始地址

二、PEB

  • 定义

    • 进程环境块(Process Envirorment Block),存放着进程信息的结构体
  • PEB结构

  1. +0x002 BeingDebugged : UChar(一个字节)

    • 当前进程是否处于调试状态,也就是函数IsDebuggerPresent() 所访问的结构体成员
  2. +0x008 ImageBaseAddress : Ptr32 Void

    • 自身的 ImageBase(映像基址)和PE结构中的 IMAGE_OPTIONAL_HEADER.ImageBase。可用函 GetModuleHandle (0) 获取自身模块句柄来访问这个结构体成员,调用该函数,当判断参数为0时,fs:[0x30](->eax)访问PEB,[eax+0x8]访问ImageBaseAddress。
  3. +0x00c Ldr : Ptr32 _PEB_LDR_DATA

    • 指向 _PEB_LDR_DATA 的结构体指针,当DLL加载到进程,可从 PEB.Ldr 中获取该模块的基址和其他信息(通用shellcode编写,查找API地址并加载)
  1. ntdll!_PEB_LDR_DATA
  2. +0x000 Length //结构体大小
  3. +0x004 Initialized //进程是否初始化完成
  4. +0x008 SsHandle
  5. //按加载顺序
  6. +0x00c InLoadOrderModuleList : _LIST_ENTRY //指向LIST_ENTRY的结构体指针
  7. //按在虚拟空间中的位置
  8. +0x014 InMemoryOrderModuleList : _LIST_ENTRY//指向LIST_ENTRY的结构体指针
  9. // 按初始化顺序
  10. +0x01c InInitializationOrderModuleList : _LIST_ENTRY//指向第一个初始化模块(ntdll)的_LDR_DATA_TABLE_ENTRY
  11. +0x024 EntryInProgress
  12. +0x028 ShutdownInProgress
  13. +0x02c ShutdownThreadId
  • _LIST_ENTRY是一个双向链表,链表中存放着_LDR_DATA_TABLE_ENTRY 的结构体信息

    1. //_LIST_ENTRY
    2. +0x000 Flink : Ptr32 _LIST_ENTRY
    3. +0x004 Blink : Ptr32 _LIST_ENTRY
  • _LDR_DATA_TABLE_ENTRY结构体就是包含进程加载的DLL模块的所有信息(包括三个链表的链表指针_LIST_ENTRY)

  1. +0x018 ProcessHeap : Ptr32 Void
    • 进程堆的句柄,也就是指向结构体HEAP的指针,可用函数 GetProcessHe**ap**()获取
    • 0x00c Flags :Uint48 0x0010 Flags :Uint48
      • 程序正常运行时,ProcessHeap.Flags的值为2 , ProcessHeap.ForceFlags 的值为0,也常用于反调试。
  2. +0x068 NtGlobalFlag : Uint4B
    • 调试状态时,PEB.NtGlobalFlag 的值为0x70


image.png**

  • 作用:在shellcode注入时获取程序加载后dll在内存中的基址,方便进一步dll导出函数的调用

image.png

三、SEH

1、定义

  • SEH(Structured Exception Handling),结构化异常处理,Windows操作系统提供的强大异常处理功能。而Visual C++中的try{}/finally{}和try{}/except{}结构本质上是对Windows提供的SEH的封装
  • SEH是基于线程的异常处理
  • 模拟异常处理19.jpg
    • 把EAX的值置为空指针,然后向空指针里写入值,引发 STATUS_ACCESS_VIOLATION(内存访问异常),然后在异常处理里面把EAX的值设置为变量dwTest的地址,然后返回 EXCEPTION_CONTINUE_EXECUTION 表示异常被处理,从异常处继续执行。
    • MSDN对于异常处理( Exception Handling )返回值的定义:
      1. #define EXCEPTION_EXECUTE_HANDLER 1 //表示异常被处理,从下一条指令开始执行
      2. #define EXCEPTION_CONTINUE_SEARCH 0 //表示异常未被处理,交由下一个SEH
      3. #define EXCEPTION_CONTINUE_EXECUTION -1 //表示异常被处理,从异常处开始执行

2、Windows下各种异常处理的优先级

  • 异常处理种类

    1. VEH(向量化异常处理,最顶端的异常处理)

      • 向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部,0为底部),异常处理中最先执行
        1. PVOID WINAPI AddVectoredExceptionHandler(
        2. _In_ ULONG FirstHandler,
        3. _In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
        4. );
    2. VCH(最低端的异常处理 )

      • 向进程里注册一个异常捕获函数,参数FirstHandler 决定插入到链表的位置(非0为头部, 0为底部 ,异常处理中最后执行
        1. PVOID WINAPI AddVectoredContinueHandler(
        2. _In_ ULONG FirstHandler,
        3. _In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
        4. );
    3. SEH(结构化异常处理,基于线程栈的异常处理)

      • SEH是基于线程的异常处理,因为SEH链指针是在TEB(线程信息块)的第一个结构体成员(NT_TIB)的头部:fs:[0]
    4. UEF(TopLevelEH,顶级异常处理)

      • TopLevelEH 为线程顶级异常处理器,通常可以处理到所有线程消息发生的异常。
        1. LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter
        2. _In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
        3. );
  • 优先级

    • 调试器>VEH(进程相关)>SEH(线程相关)>UEF(基于SEH)>VCH(进程相关)

3、EXCEPTION_POINTERS 结构体

  • 异常处理回调函数参数大都为 EXCEPTION_POINTERS 结构体,异常发生时,系统向引起异常的线程的堆栈里压入该结构

    1. typedef struct _EXCEPTION_POINTERS {
    2. PEXCEPTION_RECORD ExceptionRecord;
    3. PCONTEXT ContextRecord;
    4. } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
  • EXCEPTION_RECORD

    1. typedef struct _EXCEPTION_RECORD {
    2. DWORD ExceptionCode; //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
    3. DWORD ExceptionFlags; //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
    4. struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
    5. PVOID ExceptionAddress; //异常发生的地址
    6. DWORD NumberParameters; //下面ExceptionInformation所含有的dword数目
    7. ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加消息,如读或写冲突
    8. } 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;

  1. <a name="8nWPT"></a>
  2. #### 4、认识SEH链
  3. - SEH链表位于结构体NT_TIB的第一个结构体成员,而结构体NT_TIB也位于TEB的第一个结构体成员,一句话而言SEH链表指针位于寄存器FS:[0]的位置:即TIB结构体中的struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList为SEH链的头指针
  4. ```c
  5. typedef struct _EXCEPTION_REGISTRATION_RECORD {
  6. struct _EXCEPTION_REGISTRATION_RECORD *Next;
  7. PEXCEPTION_ROUTINE Handler;
  8. } EXCEPTION_REGISTRATION_RECORD;
  • 第一个成员 Next 为指向下一个链表的指针,直到遇到 0xFFFFFFFF 结束,而结构体成员 Handler 为SEH的异常处理函数指针
  • SEH异常处理函数 EXCEPTION_ROUTINE 的定义

    1. typedef _IRQL_requires_same_ _Function_class_(EXCEPTION_ROUTINE) EXCEPTION_DISPOSITION NTAPI EXCEPTION_ROUTINE (
    2. _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
    3. _In_ PVOID EstablisherFrame,
    4. _Inout_ struct _CONTEXT *ContextRecord,
    5. _In_ PVOID DispatcherContext
    6. );
  • 该异常处理函数是以 EXCEPTION_DISPOSITION 为返回值的回调函数

    1. typedef enum _EXCEPTION_DISPOSITION {
    2. ExceptionContinueExecution, //继续执行异常代码ExceptionContinueSearch,//运行下一个异常处理器
    3. ExceptionNestedException,//在OS内部使用
    4. ExceptionCollidedUnwind //在OS内部使用
    5. } 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 ;添加链表

  1. - 卸载SEH<br />pop dword ptr fs:[0] ;还原链表头<br />add esp,4 ;删除 异常处理器
  2. ![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)
  3. <a name="rhTdZ"></a>
  4. #### 7、异常的种类和常见的异常代码
  5. - STATUS_ACCESS_VIOLATION0xC0000005
  6. - 非法访问异常,试图访问不存在、没有访问权限,或是试图向没有写入权限的地址或是向内核区域写入发生的异常。
  7. - STATUS_BREAKPOINT0x80000003
  8. - 断点异常,INT 30xCC)断点
  9. - STATUS_ILLEGAL_INSTRUCTION0xC000001D
  10. - CPU遇到无法解析的指令时发生该异常
  11. - STATUS_INTEGER_DIVIDE_BY_ZERO0xC0000094
  12. - 除法中,分母为0时发生的异常
  13. - STATUS_SINGLE_STEP
  14. - 单步调试异常,在EFlag寄存器把TF标志位置1发生的单步调试异常
  15. <a name="x4Zs6"></a>
  16. #### 8、SEH异常处理实例
  17. ```c
  18. // SEHList.cpp : 定义控制台应用程序的入口点。
  19. //
  20. #include "stdafx.h"
  21. //
  22. #include <windows.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. DWORD dwTest;
  26. EXCEPTION_DISPOSITION NTAPI ExceptHandler(
  27. _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
  28. _In_ PVOID EstablisherFrame,
  29. _Inout_ struct _CONTEXT *ContextRecord,
  30. _In_ PVOID DispatcherContext){
  31. printf("进入异常处理\n");
  32. printf("异常地址:%X<异常代码:%X>\n", ExceptionRecord->ExceptionAddress,
  33. ExceptionRecord->ExceptionCode);
  34. ContextRecord->Eax = (DWORD)(&dwTest);
  35. return ExceptionContinueExecution;
  36. }
  37. int _tmain(int argc, _TCHAR* argv[])
  38. {
  39. printf("注册SEH\n");
  40. __asm{
  41. lea eax, ExceptHandler
  42. push eax
  43. push fs : [0]
  44. mov dword ptr fs : [0], ESP
  45. }
  46. __asm{
  47. xor eax,eax
  48. mov dword ptr[eax],1234h
  49. }
  50. printf("删除SEH\n");
  51. __asm{
  52. pop dword ptr fs : [0]
  53. add esp, 4
  54. }
  55. printf("dwTest=%X\n", dwTest);
  56. getchar();
  57. return 0;
  58. }