Skip to content →

TEB和PEB

PEB全称为Process Environment Block,即进程环境块,每个进程都有自己的PEB,用于存放进程信息,位于用户地址空间。TEB全称为Thread Environment Block,即线程环境块,进程中的每个线程都有自己的TEB,用于保存频繁使用的线程相关的数据,位于用户地址空间,在比 PEB 所在地址低的地方。

一、线程环境块TEB

一个进程的所有TEB都以堆栈的方式,存放在从0x7FFDE000开始的线性内存中,每4KB为一个完整的TEB,不过该内存区域是向下扩展的。在用户模式,当前线程的TEB位于独立的4KB段,可通过CPU的FS寄存器来访问该段,一般存储在[FS:0]。在用户态下WinDbg中可用命令$thread取得TEB地址。

需要注意的是,FS:0指向线程环境块TEB,是FS段寄存器的基址。在FS:0所指向的TEB结构中,第一个元素的值为FS:[0],指向当前线程的结构化异常处理结构SEH。

typedef struct _TEB {
+000h    NT_TIB              Tib;
+01Ch    PVOID               EnvironmentPointer;
+020h    CLIENT_ID           Cid;
+028h    PVOID               ActiveRpcInfo;
+02Ch    PVOID               ThreadLocalStoragePointer;
+030h    PPEB                Peb;
+034h    ULONG               LastErrorValue;
+038h    ULONG               CountOfOwnedCriticalSections;
+03Ch    PVOID               CsrClientThread;
+040h    PVOID               Win32ThreadInfo;
+044h    ULONG               Win32ClientInfo[0x1F];
+0C0h    PVOID               WOW32Reserved;
+0C4h    ULONG               CurrentLocale;
+0C8h    ULONG               FpSoftwareStatusRegister;
+0CCh    PVOID               SystemReserved1[0x36];
+1A4h    PVOID               Spare1;
+1A8h    LONG                ExceptionCode;
+1ACh    BYTE                SpareBytes1[0x28];
+1D4h    PVOID               SystemReserved2[0xA];
+1FCh    GDI_TEB_BATCH       GdiTebBatch;
+6DCh    ULONG               GdiRgn;
+6E0h    ULONG               GdiPen;
+6E4h    ULONG               GdiBrush;
+6E8h    CLIENT_ID           RealClientId;
+6F0h    PVOID               GdiCachedProcessHandle;
+6F4h    ULONG               GdiClientPID;
+6F8h    ULONG               GdiClientTID;
+6FCh    PVOID               GdiThreadLocaleInfo;
+700h    PVOID               UserReserved[0x5];
+714h    PVOID               GLDispatchTable[0x118];
+B74h    ULONG               GLReserved1[0x1A];
+BDCh    PVOID               GLReserved2;
+BE0h    PVOID               GLSectionInfo;
+BE4h    PVOID               GLSection;
+BE8h    PVOID               GLTable;
+BECh    PVOID               GLCurrentRC;
+BF0h    PVOID               GLContext;
+BF4h    NTSTATUS            LastStatusValue;
+BF8h    UNICODE_STRING      StaticUnicodeString;
+C00h    WCHAR               StaticUnicodeBuffer[0x105];
+E0Ch    PVOID               DealLocationStack;
+E10h    PVOID               TlsSlots[0x40];
+F10h    LIST_ENTRY          TlsLinks;
+F18h    PVOID               Vdm;
+F1Ch    PVOID               ReservedForNtRpc;
+F20h    PVOID               DbgSsReserved[0x2];
+F28h    ULONG               HardErrorDisabled;
+F2Ch    PVOID               Instrumentation[0x10];
+F6Ch    PVOID               WinSockData;
+F70h    ULONG               GdiBatchCount;
+F74h    ULONG               Spare2;
+F78h    ULONG               Spare3;
+F7Ch    ULONG               Spare4;
+F80h    PVOID               ReservedForOle; //Windows 2000 only
+F84h    ULONG               WaitingOnLoaderLock;
+F88h    PVOID               StackCommit;
+F8Ch    PVOID               StackCommitMax;
+F90h    PVOID               StackReserve;
+F94h    PVOID               MessageQueue;
} TEB, *PTEB;

注:从偏移0x38开始之后的字段有待验证。

  • 线程信息块TIB
typedef struct _NT_TIB {
+000h    _EXCEPTION_REGISTRATION_RECORD* ExceptionList;
+004h    PVOID               StackBase;
+008h    PVOID               StackLimit;
+00Ch    PVOID               SubSystemTib; 
#if defined(_MSC_EXTENSIONS)
union {
+010h    PVOID               FiberData;
+010h    DWORD               Version; 
};
#else
+010h    PVOID               FiberData;
#endif
+014h    PVOID               ArbitraryUserPointer;
+018h    TEB*                Self;
} NT_TIB, *PNT_TIB;
  • CLIENT_ID结构
typedef struct _CLIENT_ID {
+000h    ULONG               UniqueProcess;
+004h    ULONG               UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

二、进程环境块PEB

准确的PEB地址应从EPROCESS结构的0x1b0偏移处获得,但由于EPROCESS位于系统地址空间,访问这个结构需要有ring0的权限,所以可从TEB结构的偏移0x30处获得PEB的位置,FS段寄存器指向当前的TEB结构,FS:[0x30]即PEB地址。在用户态下WinDbg中,可用命令$proc取得PEB地址。

结构体PEB的定义在头文件winternl.h中给出,从微软官方SDK文档给出的定义可知,大量的成员变量都被隐藏了,无法知晓这些变量的具体含义,但可以使用调试器打印出PEB的详细信息,从而猜测各变量含义。关于PEB的参考资料:

typedef struct _PEB {
+000h    UCHAR               InheritedAddressSpace;
+001h    UCHAR               ReadImageFileExecOptions;
+002h    UCHAR               BeingDebugged;
+003h    UCHAR               SpareBool;
+004h    HANDLE              Mutant;
+008h    PVOID               ImageBaseAddress;    // 程序加载的基地址
+00Ch    PPEB_LDR_DATA       Ldr;                 // 进程加载的模块链表
+010h    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+014h    PVOID               SubSystemData;
+018h    PVOID               ProcessHeap;
+01Ch    PVOID               FastPebLock;
+020h    PPEBLOCKROUTINE     FastPebLockRoutine;
+024h    PPEBLOCKROUTINE     FastPebUnlockRoutine;
+028h    ULONG               EnvironmentUpdateCount;
+02Ch    PVOID*              KernelCallbackTable; // 用于从内核回调用户空间的函数
+030h    PVOID               EventLogSection;
+034h    PVOID               EventLog;
+038h    PPEB_FREE_BLOCK     FreeList;
+03Ch    ULONG               TlsExpansionCounter;
+040h    PVOID               TlsBitmap;           // TLS位图
+044h    ULONG               TlsBitmapBits[0x2];
+04Ch    PVOID               ReadOnlySharedMemoryBase;
+050h    PVOID               ReadOnlySharedMemoryHeap;
+054h    PVOID*              ReadOnlyStaticServerData;
+058h    PVOID               AnsiCodePageData;
+05Ch    PVOID               OemCodePageData;
+060h    PVOID               UnicodeCaseTableData;
+064h    ULONG               NumberOfProcessors;
+068h    ULONG               NtGlobalFlag;
+06Ch    BYTE                Spare2[0x4];
+070h    LARGE_INTEGER       CriticalSectionTimeout;
+078h    ULONG               HeapSegmentReserve;
+07Ch    ULONG               HeapSegmentCommit;
+080h    ULONG               HeapDeCommitTotalFreeThreshold;
+084h    ULONG               HeapDeCommitFreeBlockThreshold;
+088h    ULONG               NumberOfHeaps;
+08Ch    ULONG               MaximumNumberOfHeaps;
+090h    PVOID**             ProcessHeaps;
+094h    PVOID               GdiSharedHandleTable;
+098h    PVOID               ProcessStarterHelper;
+09Ch    PVOID               GdiDCAttributeList;
+0A0h    PVOID               LoaderLock;
+0A4h    ULONG               OSMajorVersion;
+0A8h    ULONG               OSMinorVersion;
+0ACh    ULONG               OSBuildNumber;
+0B0h    ULONG               OSPlatformId;
+0B4h    ULONG               ImageSubSystem;
+0B8h    ULONG               ImageSubSystemMajorVersion;
+0BCh    ULONG               ImageSubSystemMinorVersion;
+0C0h    ULONG               ImageProcessAffinityMask;
+0C4h    ULONG               GdiHandleBuffer[0x22];
+14Ch    ULONG               PostProcessInitRoutine;
+150h    ULONG               TlsExpansionBitmap;
+154h    ULONG               TlsExpansionBitmapBits[0x20];
+1D4h    ULONG               SessionId;
} PEB, *PPEB;

注:靠后的字段有待验证。

  • PEB_LDR_DATA结构

PEB_LDR_DATA结构的最后三个成员与_LDR_MODULE结构中前三项一一对应,分别是按照加载顺序、按照在内存中地址顺序、按照初始化顺序进行排列的模块信息结构的双向链表。_LDR_MODULE结构,其实就是_LDR_DATA_TABLE_ENTRY结构。

在初始化过程中,一般是先初始内核模块,然后到应用层,而在内存中一般先是用户层模块,之后是内核模块。

typedef struct _PEB_LDR_DATA {
+000h    ULONG               Length;
+004h    BOOLEAN             Initialized; // 此处需字节对齐
+008h    PVOID               SsHandle;
+00Ch    LIST_ENTRY          InLoadOrderModuleList;
+014h    LIST_ENTRY          InMemoryOrderModuleList;
+01Ch    LIST_ENTRY          InInitializationOrderModuleList;
+024h    PVOID               EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
  • 模块链表
typedef struct _LIST_ENTRY {
+001h    _LIST_ENTRY*        Flink; // 指向下一个节点
+004h    _LIST_ENTRY*        Blink; // 指向前一个节点
} LIST_ENTRY, *PLIST_ENTRY;
  • 模块信息结构
typedef struct _LDR_MODULE {
+000h    LIST_ENTRY          InLoadOrderModuleList;           // 按加载模块顺序的模块链表
+008h    LIST_ENTRY          InMemoryOrderModuleList;         // 按在内存中顺序的模块链表
+010h    LIST_ENTRY          InInitializationOrderModuleList; // 按初始化顺序
+018h    PVOID               BaseAddress; // 模块的基地址
+01Ch    PVOID               EntryPoint;  // 模块的入口
+020h    ULONG               SizeOfImage; // 模块镜像大小
+024h    UNICODE_STRING      FullDllName; // 模块的路径名
+028h    UNICODE_STRING      BaseDllName; // 模块名
+02Ch    ULONG               Flags;
+030h    SHORT               LoadCount;   // 引用计数
+032h    SHORT               TlsIndex;
+034h    LIST_ENTRY          HashTableEntry; 
+03Ch    ULONG               TimeDateStamp; 
} LDR_MODULE, *PLDR_MODULE;

模块链表图解:

 

Published in 未分类