本文最后更新于:2022年5月27日 下午
第46章 TEB
本章将学习有关TEB ( Thread Environment Block,线程环境块)的知识,它们是我们后面要学习的高级调试技术的基础,请大家认真学习,理解并掌握相关概念。
46.1 TEB TEB指线程环境块,该结构体包含进程中运行线程的各种信息,进程中的每个线程都对应一个TEB结构体。不同OS中TEB结构体的形态略微不同,有关TEB结构体的详细说明都已被文档化,各位可以直接查看并参考。
46.1.1 TEB结构体的定义 首先看看MSDN中关于TEB结构体的说明。
1 2 3 4 5 6 7 8 9 10 11 12 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
正如大家所见,MSDN对TEB结构体的说明太过简单。要想查看关于TEB结构体的更多细节,必须借助类似于WinDbg的内核调试器(Kernel Debugger )才行。
安装并运行WinDbg的方法请参考“WinDbg” 一章。
46.1.2 TEB结构体成员 使用WinDbg调试器获取TEB结构体的组成成员,如下所示。
获取下面信息的方法:dt ntdll!_TEB(注意配置加载正确的符号文件)
若要详细信息
输入命令:dt -r1 ntdll!_TEB
Windows XP SP3中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 dt ntdll!_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 ActivationContextStackPointer : Ptr32 _ACTIVATION_CONTEXT_STACK +0x1ac SpareBytes : [36 ] UChar +0x1d0 TxFsContext : Uint4B +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 ] Wchar +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 HardErrorMode : Uint4B +0xf2c Instrumentation : [9 ] Ptr32 Void +0xf50 ActivityId : _GUID +0xf60 SubProcessTag : Ptr32 Void +0xf64 EtwLocalData : Ptr32 Void +0xf68 EtwTraceData : Ptr32 Void +0xf6c WinSockData : Ptr32 Void +0xf70 GdiBatchCount : Uint4B +0xf74 CurrentIdealProcessor : _PROCESSOR_NUMBER +0xf74 IdealProcessorValue : Uint4B +0xf74 ReservedPad0 : UChar +0xf75 ReservedPad1 : UChar +0xf76 ReservedPad2 : UChar +0xf77 IdealProcessor : UChar +0xf78 GuaranteedStackBytes : Uint4B +0xf7c ReservedForPerf : Ptr32 Void +0xf80 ReservedForOle : Ptr32 Void +0xf84 WaitingOnLoaderLock : Uint4B +0xf88 SavedPriorityState : Ptr32 Void +0xf8c SoftPatchPtr1 : Uint4B +0xf90 ThreadPoolData : Ptr32 Void +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void +0xf98 MuiGeneration : 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 FlsData : Ptr32 Void +0xfb8 PreferredLanguages : Ptr32 Void +0xfbc UserPrefLanguages : Ptr32 Void +0xfc0 MergedPrefLanguages : Ptr32 Void +0xfc4 MuiImpersonation : Uint4B +0xfc8 CrossTebFlags : Uint2B +0xfc8 SpareCrossTebBits : Pos 0 , 16 Bits +0xfca SameTebFlags : Uint2B +0xfca SafeThunkCall : Pos 0 , 1 Bit +0xfca InDebugPrint : Pos 1 , 1 Bit +0xfca HasFiberData : Pos 2 , 1 Bit +0xfca SkipThreadAttach : Pos 3 , 1 Bit +0xfca WerInShipAssertCode : Pos 4 , 1 Bit +0xfca RanProcessInit : Pos 5 , 1 Bit +0xfca ClonedThread : Pos 6 , 1 Bit +0xfca SuppressDebugMsg : Pos 7 , 1 Bit +0xfca DisableUserStackWalk : Pos 8 , 1 Bit +0xfca RtlExceptionAttached : Pos 9 , 1 Bit +0xfca InitialThread : Pos 10 , 1 Bit +0xfca SpareSameTebBits : Pos 11 , 5 Bits +0xfcc TxnScopeEnterCallback : Ptr32 Void +0xfd0 TxnScopeExitCallback : Ptr32 Void +0xfd4 TxnScopeContext : Ptr32 Void +0xfd8 LockCount : Uint4B +0xfdc SpareUlong0 : Uint4B +0xfe0 ResourceRetValue : Ptr32 Void
如上所示,借助WinDbg的符号文件,我们查看了TEB结构体的所有成员。仔细比较代码46-2与46-3可以发现,Windows 7下的TEB结构体比Windows XP下的TEB结构体大。
46.1.3重要成员 如上所示,TEB结构体的成员多而复杂,在用户模式调试中起着重要作用的成员有2个,如代码46-4所示。
1 2 +0x000 NtTib : _NT_TIB +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
ProcessEnvironmentBlock成员
先看Offset 30处的ProcessEnvironmentBlock成员,它是指向PEB(Process Environment Block,进程环境块)结构体的指针。PEB是进程环境块,每个进程对应1个PEB结构体,下一章将详细讲解。
NtTib成员
TEB结构体的第一个成员S_NT_TIB结构体(TIB是Thread Information Block的简称,意为“线程信息块”),_NT_TIB结构体的定义如下所示:
1 2 3 4 5 6 7 8 9 typedef struct _NT_TIB { struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList ; PVOID StackBase; PVOID StackLimit; PVOID SubSystemTib; PVOID ArbitraryUserPointer; struct _NT_TIB *Self ; } NT_TIB;typedef NT_TIB *PNT_TIB;
ExceptionList成员指向_EXCEPTION_REGISTRATION_RECORD结构体组成的链表,它用于Windows OS的SEH。Self成员是_NT_TIB结构体的自引用指针,也是TEB结构体的指针(因为TEB结构体的第一个成员就是_NT_TIB结构体)。那么接下来的问题是,该如何在用户模式下访问TEB结构体呢?只有访问它才能使用相应信息。下一节将学习如何在用户模式下访问TEB结构体。
46.2 TEB访问方法 前面讲解过,借助WinDbg内核调试器可以很容易地访问TEB结构体。那么,该如何在用户模式下访问它呢?答案就是,通过OS提供的相关API访问。
请注意:下面示例中出现的地址会随用户计算机环境的不同而不同。
46.2.1 Ntdll.NtCurrentTeb() Ntdll.NtCurrentTeb() API用来返回当前线程的TEB结构体的地址。该函数内部是如何实现的呢?下面使用OllyDbg工具查看。首先在OllyDbg中打开Notepad.exe程序(也可以打开其他任一程序),然后在鼠标右键菜单中选择Search for Name in all modules菜单,在Name in all modules对话框中查找ntdll.NtCurrentTeb()API,如图46-1所示(单击Name栏,按Name排序后更易查找)。
如图46-1所示,查找到NtCurrentTeb函数后,使用鼠标双击即可跳转到该API的代码处,如图46-2所示。
从上图可以看到,NtCurrentTeb()函数的内部代码非常简单,只返回FS:[18]地址值。在图46-2 的OllyDbg的代码注释窗口中可以看到FS:[18]的实际地址为7FFDF018。在内存窗口中进入7FFDF018地址,发现其值为7FFDF000,即NtCurrentTeb() API返回7FFDF000,该地址就是当前线程的TEB的地址。仔细观察图46-2中TEB结构体的地址(7FFDF000),发现它与FS段寄存器所指的段内存的基址是一样的。也就是说,TEB与FS段寄存器有着某种关联。
46.2.2 FS段寄存器 SDT(Segment Descriptor Table,段描述符表)
其实,FS段寄存器用来指示当前线程的TEB结构体。
IA-32系统中进程的虚拟内存大小为4GB,因而需要32位的指针才能访问整个内存空间。但 是FS寄存器的大小只有16位,那么它如何表示进程内存空间中的TEB结构体的地址呢?实际上,FS寄存器并非直接指向TEB结构体的地址,它持有SDT的索引,而该索引持有实际TEB地址。
SDT位于内核内存区域,其地址存储在特殊的寄存器GDTR(Global Descriptor Table Resiger,全局描述符表寄存器)中
借助示意图描述上述过程,如图46-3所示。
由于段寄存器实际存储的是SDT的索引,所以它也被称为“段选择符”(Segment Selector)。从图46-3中可以看到,TEB结构体位于FS段选择符所指的段内存的起始地址(base address)处。
如果掌握了上述内容,那么就很容易理解下面公式的含义。
FS:[0x18] = TEB.NtTib.Self = address of TIB = address of TEB = FS:0 = 7FFDF000
FS:0是段内存的起始地址,FS寄存器指向(Indexing) 一个段描述符,而该描述符又指向段内存的起始地址。
从图46-2中可知,FS:[0x18]与[7FFDF018](→7FFDF000)具有相同含义。由代码46-5中的_NT_TIB结构体的定义得知,结构体的最后一个Self成员恰好位于从TEB结构体偏移018的位置(再次提醒TEB结构体的第一个成员就是_NT_TIB结构体)。Self指针变量指向偏移018的起始地址,也就是TEB的起始地址。
根据代码46-2与代码46-3,FS:[0x30]可表示为如下等式:
FS:[0x30] = TEB.ProcessEnvironmentBlock = address of PEB
从图46-2中可以知道,FS:[0x30]与[7FFDF030](→7FFD3000)具有相同含义。也就是说,通过TEB的ProcessEnvironment Block成员可以获取PEB结构体的起始地址。PEB结构体多用于反调试,下一章将详细讲解。
此外还要了解一下FS:[0]。
FS:[0] = TEB.NtTib.ExceptionList = address of SEH
从图46-2中可以知道,FS:[0]与[7FFDF000](→1DFF64)具有相同含义。
SEH是Wiondows操作系统中的结构化异常处理机制,常用于反调试技术,详细内容请参考第48章。
46.3小结 本章我们学习了FS:[0]、FS:[0xl8]、FS:[0x30]的含义,调试中会经常见到。只要理解了“FS:[0xl8]指向TEB结构体的起始地址”,就能轻松掌握它们表示的含义。为便于说明,本章并未做复杂讲解,大家具备了一定的水平与实力后;我们会另外学习IA-32内存模型的知识。刚开始学习时虽然有些枯燥乏味,但还是要先认真整理这些相关概念,随着各位对IA-32 CPU与Windows OS理解的逐渐深人,再逐步学习更高级的代码逆向分析技术。