TEB简介
TEB(Thread Environment Block,线程环境块)指线程环境块,该结构体包含进程中运行线程的各种信息,进程中的每个线程都对应着一个TEB结构体。不同OS中TEB结构体的形态略微有点不同
TEB结构体定义
MSDN
typedef struct _TEB {
PVOID Reserved1[12];
PPEB ProcessEnvironmentBlock;
PVOID Reserved2[399];
BYTE Reserved3[1952];
PVOID TlsSlots[64];
BYTE Reserved4[8];
PVOID Reserved5[26];
PVOID ReservedForOle;
PVOID Reserved6[4];
PVOID TlsExpansionSlots;
} TEB, *PTEB;
Windows XP SP3
nt!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
+0x0c8 FpSoftwareStatusRegister : Uint4B
+0x0cc SystemReserved1 : [54] Ptr32 Void
+0x1a4 ExceptionCode : Int4B
+0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1 : [24] UChar
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
+0x6b4 RealClientId : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : Ptr32 Void
+0x6c0 GdiClientPID : Uint4B
+0x6c4 GdiClientTID : Uint4B
+0x6c8 GdiThreadLocalInfo : Ptr32 Void
+0x6cc Win32ClientInfo : [62] Uint4B
+0x7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Uint2B
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapVirtualAffinity : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar
Windows 7
如上所示,借助winDbg的符号文件,查看了PEB结构体的所有成员。可以发现,Windows 7下的PEB结构体比Windows XP下的PEB结构体大
重要成员
在用户模式调试中起着重要作用的成员有2个,如下:
+0x000 NtTib : _NT_TIB
………………………………………………………………………………
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
ProcessEnvironmentBlock成员:
先看Offset 30处的ProcessEnvironmentBlock成员,它是指向PEB(Process Environment Block,进程环境块)结构体指针。PEB是进程环境块,每个进程对应1个PEB结构体。
NtTib成员
TEB
结构体的第一个成员为_NT_TIB
结构体(TIB
是Thread Information Block的简称,意为“线程信息块”),_NT_TIB
结构体的定义如下所示:
typedef struct _NT_TIB //sizeof 1ch
{
00h struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; //SEH链入口
04h PVOID StackBase; //堆栈基址
08h PVOID StackLimit; //堆栈大小
0ch PVOID SubSystemTib;
union {
PVOID FiberData;
10h DWORD Version;
};
14h PVOID ArbitraryUserPointer;
18h struct _NT_TIB *Self; //本NT_TIB结构自身的线性地址
}NT_TIB;
typedef NT_TIB *PNT_TIB;
ExceptionList成员指向_EXCEPTION_REGISTRATION_RECORD结构体组成的链表,它用于Windows OS的SEH。Self成员是_NT_TIB结构体的自引用指针,也是TEB结构体的指针(因为TEB结构体的第一个成员就是_NT_TIB结构体)。那么我们如何在用户模式下访问到TEB结构体呢?只有访问它才能使用相应信息。
TEB访问方法
借助winDbg内核调试器可以容易地访问TEB结构体。那么该如何在用户模式下访问它呢?通过OS提供的相关API
Ntdll.NtCurrentTeb()
Ntdll.NtCurrentTeb()API用来返回当前线程的TEB结构体地址。该函数内部是如何实现的呢。随便拖动一个exe进入OD里面,然后单击鼠标右键如下操作
找到NtCurrentTeb函数后,使用鼠标双击即可跳转到该API的代码处,如下
从上图可以看到,NtCurrentTeb函数的内部代码就两行代码,返回FS:[18]地址值。如上图,FS:[18]的实际地址为0x33E0018。在内存窗口中进入0x33E0018地址,发现其值是33E000,即NtCurrentTeb()返回33E000,该地址就是当前TEB线程的TEB的地址。
仔细观察上图中TEB结构体的地址(33E000),发现它与FS段寄存器所指的段内存的基址是一样的。即TEB与FS段寄存器有着联系
FS段寄存器
其实,FS段寄存器用来只是当前线程的TEB结构体。
IA-32系统中进程的虚拟内存大小为4GB,因而需要32位的指针才能访问整个内存空间,但是FS寄存器的大小只有16位,那么它如何表示进程内存空间的TEB结构体的地址呢?实际上,FS寄存器并非直接指向TEB结构体的地址,它持有SDT 的索引,而该索引持有实际TEB地址。
提示:
SDT位于内核区域,其地址存储在特殊的寄存器GDTR(Global Descriptior Table Register,全局描述符表寄存器)中。
解释:
GDTR寄存器里面存放一个地址值,即SDT值,找到地址值所在的位置后,然后加上FS段寄存器里面的值(16位寄存器)后,得到一个内存单元,这个内存单元里面存放了一个指针,这个指针指向了TEB结构体的地址,TEB结构体的地址=[FS+SDT],下图可助理解
这里的段寄存器跟王爽老师里面所说的段寄存器作用不太一样,这里可以把FS段寄存器值看做是一个 偏移值 就好,而起始点存储在GDTR寄存器里面。
由于段寄存器实际存储的SDT的索引,所以它也被称为“段选择符”,TEB结构体位于FS段选择符所指的段内存的起始地址处。
总结:
TEB起始地址=[SDT+FS]
FS:[0x18]=TEB起始地址
(_ NT _TIB结构体的最后一个Self成员恰好位于从TEB结构体的0x18位置,Self指针变量指向 _ NT _TIB结构体的起始地址,也就是TEB的起始地址)
FS:[0x30]=PEB起始地址
FS:[0]=SEH起始地址