TEB(线程环境块)学习

文章目录

  • TEB
    • TEB结构中的两个重要成员
      • NtTib成员
      • PEB成员
        • PEB.BeingDebugged
        • PEB.ImageBaseAddress
        • PEB.Ldr
        • PEB.ProcessHeap & PEB.NtGolbalFlag

TEB

TEB结构中的两个重要成员

+0x000 NtTib 	  :_NT_TIB
.
.
.
+0x30 ProcessEnvironmentBlock       :Ptr32_PEB

NtTib成员

TEB结构体的第一个成员为_NT_TIB结构体(TIB意为线程信息块)
_NT_TIB结构体

typedef struct	_NT_TIB{
	 struct	_EXCEPTION_REGISTRATION_RECORD *ExceptionList;
	 PVOID StackBase;
	 PVOID StackLimit; 
	 PVOID SubSystemTib; 
	 union {
		PVOID FiberData; 
		DWORD Version;
	 }; 
	 PVOID ArbitraryUserPointer;
 	 struct	_NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;

ExceptionList成员指向_EXCEPTION_REGISTRATION_RECORD结构体组成的链表,它用于WindowsOS的SEH, SEH是Wiondows操作系统中的结构化异常处理机制,常用于反调试技术。Self成员是_NT_TIB结构体的自引用指针,它指向_NT_TIB结构体,又因为_NT_TIB是TEB结构体的第一个成员,所以它也是指向TEB结构体的指针(它里面存着TEB结构体的地址)。
用户模式下使用Ntdll.NtCurrentTeb()API访问TEB结构体,它返回当前线程的的TEB结构体的地址,它的实现方法就是返回FS:[0x18]处的地址值(FS段寄存器用来指示当前线程的TEB结构体),而FS:0x18处正是Self指针,其含有的也正是TEB结构体的地址。
用一个公式总结:
FS:[0x18] = TEB.NtTib.Self = address of TIB = address of TEB = FS:0(注意:xx:[]是该处含有的值)

  • FS:[0x18] = TEB起始地址
  • FS:[0x30] = PEB的起始地址
    FS:[0x30] = TEB.ProcessEnvironmentBlock = address of PEB
    通过TEB的ProcessEnvironment Block成员可以获得PEB结构体的起始地址。
  • FS:[0] = TEB.NtTib.ExceptionList = address of SEH

PEB成员

使用汇编获取PEB地址的两种方法
方法1

MOV	EAX,DWORD PTR FS:[30]    ;FS:[30] = address of PEB

方法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中重要的几个成员

+002	BeingDebugged 	;Uchar(可用于反调试技术)
...
+008	ImageBaseAddress 	 ;Ptr32 Void
...
+00c	Ldr 	 ;Ptr32 _PEB_LDR_DATA(可用于反调试技术)
...
+018	ProcessHeap 	 ;Ptr32 Void(可用于反调试技术)
...
+068	NtGlobalFlag 	  ;uint4B(可用于反调试技术)

PEB.BeingDebugged

BeingDebugged的作用就是标识当前进程是否处于调试状态,Kernel32.dll中的IsDebuggerPresent() API就是用来获取该处的值的(是,则返回1;否,则返回0)。
IsDebuggerPresent() API的汇编代码形式

MOV	EAX,DWORD PTR FS:[18]
MOV	EAX,DWORD PTR DS:[EAX+30]
MOVZX	EAX,BYTE PTR DS:[EAX+2]
RETN

该值在代码逆向分析领域主要用于反调试技术。检测该值,若进程处于调试中,则终止进程。

  • 破解之法
    只要借助OllyDbg调试器的编辑功能,将PEB.BeingDebugged的值修改为0(FALSE)即可。

PEB.ImageBaseAddress

PEB.ImageBaseAddress成员用来表示进程的ImageBase
GetModuleHandle()API用来获取ImageBase。

HMODULE WINAPI GetModuleHandle(
	__in_opt LPCTSTR lpModuleName
);

将lpModuleName参数赋值为NULL,调用GetModuleHandle()函数将返回进程被加载的ImageBase
GetModuleHandle() API的部分代码

MOV	EAX,DWORD PTR FS:[18]         ;FS:[18] = TEB
MOV	EAX,DWORD PTR DS:[EAX+30]     ;DS:[EAX+30] = PEB
MOV	EAX,DWORD PTR DS:[EAX+8]      ;DS:[EAX+8] = PEB.ImageBaseAddress

函数的最终返回值将被存到EAX中

PEB.Ldr

PEB.Ldr成员是指向_PEB_LDR_DATA结构体的指针,_PEB_LDR_DATA结构体如下。

+000 Length                         :Uint4B
+004 Initialized                    :UChar
+008 SsHandle                       :Ptr32 Void
+00c InLoadOrderModulelist          :_LIST_ENTRY
+014 InMemoryOrderModulelist        :_LIST_ENTRY
+01c InInitializationOrderModulelist:_LIST_ENTRY
+024 EntryInProgress                :Ptr32 Void
+028 ShutdownInProgress             :UChar
+02c ShutdownThreadId               :Ptr32 Void

当模块(DLL)加载到进程后,通过PEB.Ldr成员可以直接获得该模块的加载基址,_PEB_LDR_DATA结构体中含有3个_LIST_ENTRY类型的成员,_LIST_ENTRY结构体如下。

typedef struct LIST_ENTRY{
struct _LIST_ENTRY *Flink; 
struct _LIST_ENTRY *Bink;
}LIST_ENTRY,*PLIST_ENTRY;

从上述结构体可以看出,_LIST_ENTRY结构体提供双向链表机制。链表中保存的是_LDR_DATA_TABLE_ENTRY结构体的信息,给结构体如下。

typedef struct _LDR_DATA_TABLE_ENTRY {
	PVOID Reserved1[2];
	LIST_ENTRY InMemoryOrderLinks; 
	PVOID Reserved2[2]; 
	PVOID D1lBase; 
	PVOID EntryPoint; 
	PVOID Reserved3;
	Unicode_STRING Ful1D1lName; 
	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种_LIST_ENTRY双向链表,也就是说,存在多个_LDR_DATA_TABLE_ENTRY结构体,并且有三种链接方法可以将它们链接起来。

PEB.ProcessHeap & PEB.NtGolbalFlag

PEB.ProcessHeap与PEB.NtGlobalFlag成员(像PEB.BeingDebugged成员一样)应用于反调试技术。若进程处于调试状态,则ProcessHeap与NtGlobalFlag成员就持有特定值。由于它们具有这一个特征,所以常常应用于反调试技术。
PEB.ProcessHeap成员是指向HEAP结构体的指针,HEAP结构体如下。

+0×000 Entry	:_HEAP_ENTRY
+0×008 Signature	:Uint4B
+0×00c Flags	:Uint4B
+0×010 ForceFlags	:Uint4B
+0×014 VirtualMemoryThreshold 	:Uint4B
+0×018 SegmentReserve	:Uint4B
+0×01c SegmentCommit	:Uint4B
+0×020 DeCommitFreeBlockThreshold	 :Uint4B
...

进程处于被调试状态时,Flags(+0xC)与Force Flags(+ox10)成员被设置成特定的值。
PEB.ProcessHeap(PEB结构体中偏移0x18的位置)成员既可以从PEB结构体中直接获得,也可以通过GetProcessHeap() API获得。
GetProcessHeap() API代码的汇编形式基本类似于IsDebuggerPresent(),按照TEB→PEB→PEB.ProcessHeap顺序访问的ProcessHeap
汇编代码如下

MOV	EAX,DWORD PTR FS:[18]	;FS:[18] = TEB
MOV	EAX,DWORD PTR DS:[EAX+30]	;DS:[EAX+30] = PEB
MOV	EAX,DWORD PTR DS:[EAX+18]	;DS:[EAX+18] = PEB.ProcessHeap
RETN

当进程运行正常时Heap.Flagsh成员的值为0x2,Heap. ForceFlags成员的值位0x0,进程处于被调试状态时这些值也会随之改变

  • 破解之法
    只要将HEAP.Flags与HEAP.ForceFlags的值重新设置为2与0即可(HEAP.Flags=2,HEAP.ForceFlags=0)。
    注意:该方法仅在WindowsXP系统中有效,Windows7系统不存在以上特征。此外,将运行中的进程附加到调试器时,也不会出现上述特征。

你可能感兴趣的:(PE)