TEB 基本知识 TEB 指线程环境块,该结构体包含进程中运行线程的各种信息,进程中的每个线程都对应一个 TEB 结构体。不同系统中 TEB 结构体的形态略微不同。
TEB 结构体的定义 1 2 3 4 5 6 7 8 9 10 typedef struct _TEB { BYTE Reserved1[1952 ]; PVOID Reserved2[412 ]; PVOID TlsSlots[64 ]; BYTE Reserved3[8 ]; PVOID Reserved4[26 ]; PVOID ReservedForOLE; PVOID Reserved5[4 ]; PVOID TlsExpansionSlots; } TEB, *PTEB;
TEB 结构体成员 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 +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : Ptr32 Void +0x020 ClientId : _CLIENT_ID :当前进程ID +0x028 ActiveRpcHandle : Ptr32 Void +0x02c ThreadLocalStoragePointer : Ptr32 Void +0x030 ProcessEnvironmentBlock : Ptr32 _PEB 当前进程的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
重要成员 TEB 结构体的成员多而复杂,在用户模式调试中起着重要作用的成员有 2 个,如下所示。
1 2 3 +0x000 NtTib : _NT_TIB ... +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
先看偏移 30 处的ProcessEnvironmenBlock
成员,它是指向 PEB(Process Environment Block,进程环境块)结构体的指针。PEB 是进程环境块,每个进程对应 1 个 PEB 结构体。
TEB 结构体的第一个成员为_NT_TIB
结构体(TIB 是 Thread information Block的简称,意为“线程信息块”),_NT_TIB
结构体的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef struct _NT_TIB { struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList ; PVOID StackBase; PVOID StackLimit; PVOID SubSystemTib; union { PVOID FiberData; DWORD Vresion; }; 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访问方法 我们通过 Windbg 内核调试器可以很容易地访问 TEB 结构体,但是在用户模式下我们就需要通过另一种方式了,就是通过系统提供的相关 API 访问。
Ntdll.NtCurrentTeb() Ntdll.NtCurrentTeb()
API 用来返回当前线程的 TEB 结构体的地址。我们通过调试工具来查看该函数内部是如何实现的。
在 OD 中右键菜单选择 Search for Name in all modules 菜单,在对话框中查找ntdll.NtCurrentTeb()
API。
双击跳转到该 API 的代码处:
从上图中可以看到,NtCurrentTeb()
函数的内部代码非常简单,只返回FS:[18]
地址值。在上图的代码注释窗口可以看到,FS:[18]
的实际地址为 0x317018。在内存窗口中进入 0x317018 地址,发现其值为 0x317000,即NtCurrentTeb()
API 返回 0x317018,该地址就是当前线程的 TEB 的地址。咨询观察 TEB 结构体的地址(0x317000),发现它与 FS 段寄存器所指的段内存的基址是一样的。也就是说,TEB 与 FS 段寄存器有着某种关联。
FS 段寄存器
其实,FS 段寄存器用来指示当前线程的 TEB 结构体。
32 位系统中进程的虚拟内存大小为 4GB,因而需要 32 位的指针才能访问整个内存空间。但是 FS 寄存器的大小只有 16 位,那么它如何表示进程内存空间中的 TEB 结构体的地址呢?实际上,FS 寄存器并非直接指向 TEB 结构体的地址,它持有 SDT 的索引,而该索引持有实际 TEB 地址。
注:SDT 位于内核内存区域,其地址存储在特殊的寄存器 GDTR(Global Descriptor Table Resiger,全局描述符表寄存器)中。
由于段寄存器实际存储的是 SDT 的索引,所以它也被称为 “段选择符”(Segment Selector)。上图可以看到,TEB 结构体位于 FS 段选择符所指的段内存的起始地址(base address)处。
如果掌握了上述内容,那么就很容易理解下面公式的含义。
FS:[0x18]
与[7FFDF018]
(->7FFDF000)具有相同含义。由结构体代码中的_NT_TIB
结构体的定义得知,结构体的最后一个Self
成员恰好位于从 TEB 结构体偏移018
的位置。Self
指针变量指向_NT_TIB
结构体的起始地址,也就是 TEB 的起始地址。
FS:[0x30]
与[7FFDF030]
(->7FFD3000)具有相同含义。也就是说,通过 TEB 的ProcessEnvironment Block
成员可以获取 PEB 结构体的起始地址。
PEB 基本知识 PEB 是存放进程信息的结构体,尺寸非常大,这里我们只需要它的几个重要字段。
PEB 访问方法 先了解访问 PEB 结构体的方法。在前面 TEB 结构体的学习中我们已经知道,TEB.Process-EnvironmentBlock
成员就是 PEB 结构体的地址。
TEB 结构体位于 FS 段选择符所指的段内存的起始地址处,且ProcessEnvironmentBlock
成员位于距 TEB 结构体偏移的 30 的位置。所以有如下等式成立:
1 FS:[30] = TEB.ProcessEnvironmentBlock = address of PEB
用如下汇编代码表示上述等式:
1 mov eax,dword ptr FS:[30] ;FS[30] = address of PEB
方法2:先获取 TEB 地址,再通过ProcessEnvironmentBlock
(+30偏移)获取 PEB 地址
1 2 mov eax,dword ptr FS:[18] ;FS[18] = address of TEB mov eax,dword ptr DS:[eax+30] ;DS[eax+30] = address of PEB
PEB 结构体的定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef struct _PEB { BYTE Reserved1[2 ]; BYTE BeingDebugged; BYTE Reserved2[1 ]; PVOID Reserved3[2 ]; PPEB_LDR_DATA Idr; PPTL_USER_PROCESS_PARAMETERS ProcessParameters; BYTE Reserved4[104 ]; PVOID Reserved5[52 ]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved6[128 ]; PVOID Reserved7[1 ]; ULONG SessionId; } PEB,*PPEB;
PEB 结构体的成员 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 +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar 调试标志 +0x003 SpareBool : UChar +0x004 Mutant : Ptr32 Void +0x008 ImageBaseAddress : Ptr32 Void 映像基址 +0x00c Ldr : Ptr32 _PEB_LDR_DATA 进程加载模块链表 +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : Ptr32 Void +0x018 ProcessHeap : Ptr32 Void +0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION +0x020 FastPebLockRoutine : Ptr32 Void +0x024 FastPebUnlockRoutine : Ptr32 Void +0x028 EnvironmentUpdateCount : Uint4B +0x02c KernelCallbackTable : Ptr32 Void +0x030 SystemReserved : [1 ] Uint4B +0x034 AtlThunkSListPtr32 : Uint4B +0x038 FreeList : Ptr32 _PEB_FREE_BLOCK +0x03c TlsExpansionCounter : Uint4B +0x040 TlsBitmap : Ptr32 Void +0x044 TlsBitmapBits : [2 ] Uint4B +0x04c ReadOnlySharedMemoryBase : Ptr32 Void +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void +0x058 AnsiCodePageData : Ptr32 Void +0x05c OemCodePageData : Ptr32 Void +0x060 UnicodeCaseTableData : Ptr32 Void +0x064 NumberOfProcessors : Uint4B +0x068 NtGlobalFlag : Uint4B +0x070 CriticalSectionTimeout : _LARGE_INTEGER +0x078 HeapSegmentReserve : Uint4B +0x07c HeapSegmentCommit : Uint4B +0x080 HeapDeCommitTotalFreeThreshold : Uint4B +0x084 HeapDeCommitFreeBlockThreshold : Uint4B +0x088 NumberOfHeaps : Uint4B +0x08c MaximumNumberOfHeaps : Uint4B +0x090 ProcessHeaps : Ptr32 Ptr32 Void +0x094 GdiSharedHandleTable : Ptr32 Void +0x098 ProcessStarterHelper : Ptr32 Void +0x09c GdiDCAttributeList : Uint4B +0x0a0 LoaderLock : Ptr32 Void +0x0a4 OSMajorVersion : Uint4B +0x0a8 OSMinorVersion : Uint4B +0x0ac OSBuildNumber : Uint2B +0x0ae OSCSDVersion : Uint2B +0x0b0 OSPlatformId : Uint4B +0x0b4 ImageSubsystem : Uint4B +0x0b8 ImageSubsystemMajorVersion : Uint4B +0x0bc ImageSubsystemMinorVersion : Uint4B +0x0c0 ImageProcessAffinityMask : Uint4B +0x0c4 GdiHandleBuffer : [34 ] Uint4B +0x14c PostProcessInitRoutine : Ptr32 void +0x150 TlsExpansionBitmap : Ptr32 Void +0x154 TlsExpansionBitmapBits : [32 ] Uint4B +0x1d4 SessionId : Uint4B +0x1d8 AppCompatFlags : _ULARGE_INTEGER +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER +0x1e8 pShimData : Ptr32 Void +0x1ec AppCompatInfo : Ptr32 Void +0x1f0 CSDVersion : _UNICODE_STRING +0x1f8 ActivationContextData : Ptr32 Void +0x1fc ProcessAssemblyStorageMap : Ptr32 Void +0x200 SystemDefaultActivationContextData : Ptr32 Void +0x204 SystemAssemblyStorageMap : Ptr32 Void +0x208 MinimumStackCommit : Uint4B
PEB 的重要成员 PEB 结构体非常庞大,且结构复杂,这里我们只学习几个重要的成员。
PEB.BeingDebugged Kernel32.dll
中有个名为Kernel32!IsDebuggerPresent()
的 API,但普通的应用程序开发中并不常用。
1 BOOL WINAPI IsDebuggerPresent (void ) ;
顾名思义,该 API 函数用于判断当前进程是否处于调试状态,并返回判断结果。该 API 通过检测PEB.IsDebuggerPresent
成员来确定是否正在调试进程(是,则返回1;否,则返回0)。
PEB.ImageBaseAddress PEB.ImageBaseAddress
成员用来表示进程的 ImageBase。
GetMouleHandle()
API 用来获取 imageBase,向lpModuleName
参数赋值为 NULL,调用GetModuleHandle()
函数将返回进程被加载的 ImageBase。
1 2 3 HMODULE WINAPI GetModuleHandle ( __in_opt LPCTSTR lpModuleName ) ;
PEB.Ldr PEB.Ldr
成员是指向_PEB_LDR_DATA
结构体的指针。借助 WinDbg 调试器可以查看_PEB_LDR_DATA
结构体成员。
1 2 3 4 5 6 7 8 9 +000 Length : Uint4B +004 Initialized : UChar +008 SsHandle : Ptr32 Void +00 c InLoadOrderModuleList : _LIST_ENTRY +014 InMemoryOrderModuleList : _LIST_ENTRY +01 c InInitializationOrderModuleList : _LIST_ENTRY +024 EntryInProgress : Ptr32 Void +028 ShutdownInProgress : UChar +02 c ShutdownThreadId : Ptr32 Void
当模块(DLL)加载到进程后,通过PEB.Ldr
成员可以直接获取该模块的加载基地址,所以PEB.Ldr
是非常重要的成员。_PEB_LDR_DATA
结构体成员中有 3 个_LIST_ENTRY
类型的成员(InLoadOrderModuleList
、InMemoryOrderModuleList
、InInitializationOrderModuleList
),_LIST_ENTRY
结构体的定义如下:
1 2 3 4 typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink ; struct _LIST_ENTRY *Blink ; }_LIST_ENTRY,*PLIST_ENTRY;
从上述结构体的定义可以看到,_LIST_ENTRY
结构体提供了双向链表机制。链表中保存着_LDR_DATA_TABLE_ENTRY
结构体的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 typedef struct _LDR_DATA_TABLE_ENTRY { PVOID Reserved1[2 ]; LIST_ENTRY InMemoryOrderLinks; PVOID Reserved2[2 ]; PVOID DllBase; PVOID EntryPoint; PVOID Reserved3; Unicode_STRING FullDllName; BYTE Reserved4[8 ]; PVOID Reserved5[3 ]; union { ULONG CheckSum; PVOID Reserved6; }; ULONG TimeDateStamp; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
每个加载到进程中的 DLL 模块都有与之对应的_LDR_DATA_TABLE_ENTRY
结构体,这些结构体相互链接,最终形成_LIST_ENTRY
双向链表。需要注意的是,_PEB_LDR_DATA
结构体中存在 3 种链表。也就是说,存在多个_LDR_DATA_TABLE_ENTRY
结构体,并且有 3 种链接方法可以将它们链接起来。
PEB.ProcessHeap & PEB.NtGlobalFlag PEB.ProcessHeap
与PEB.NtGlobalFlag
成员应用于反调试技术。若进程处于调试状态,则ProcessHeap
与NtGlobalFlag
成员就持有特定值。由于它们具有这么一个特征,所以常常应用于反调试技术。