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的参考资料:
- Undocumented Functions
- 《Windows内核情景分析》P247
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;
模块链表图解: