大部分内容引用于:
http://bbs.pediy.com/thread-119827.htm
以下内容基于x86系统,x64请查看 使用WinDbg验证PEB 基于x64
首先,来了解下什么是TEB和PEB。
TEB(Thread Environment Block,线程环境块):
系统在此TEB
中保存频繁使用的线程相关的数据。位于用户地址空间。进程中的每个线程都有自己的一个TEB
。一个进程的所有TEB
都以堆栈的方式,存放在从0x7FFDE000
(WinXP SP1)开始的线性内存中,每4KB
为一个完整的TEB
,不过该内存区域是向下扩展的。
在用户模式下,当前线程的TEB
位于独立的4KB
段,可通过CPU的FS寄存器
来访问该段,存储在[FS:0]
。
在用户态下WinDbg
中可用命令!teb
取得TEB
地址。
x64 下的TEB
在Windows x64
中,TEB
的寄存器换做了GS寄存器
,使用[GS:0x30]
访问
在Visual C++
中可以使用函数
NtCurrentTeb();
// x86
__readfsqword(0x18);
// x64
__readgsqword(0x30);
为什么是0x18
请看下文
以下内容以x86为例,x64请使用WinDbg自行修正
PEB(Process Environment Block,进程环境块)
存放进程信息,每个进程都有自己的PEB
信息。
位于用户地址空间。在Win XP SP1
下,进程环境块的地址对于每个进程来说是固定的,在0x7FFDF000
处,这是用户地址空间,所以程序能够直接访问。
准确的PEB
地址应从系统的EPROCESS
结构的0x1b0
偏移处获得,但由于EPROCESS
在系统地址空间,访问这个结构需要有ring0
的权限。
在Windows SP2
之后这个地址是随机映射的,所以PEB
地址只能通过FS:[0x30]
、GS:[0x60]
来取。
// x64
__readgsqword(0x60);
// x86
__readfsqword(0x30);
为什么是0x30
,请往下看
TEB的结构
因为winnt.h
中并未提供TEB的结构,所以借助于WinDbg工具来观察 TEB 结构。
dt _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
以下是一份x86
的_NT_TEB
结构实现,可供参考
typedef struct _NT_TEB
{
NT_TIB Tib; // 00h
PVOID EnvironmentPointer; // 1Ch
CLIENT_ID Cid; // 20h
PVOID ActiveRpcInfo; // 28h
PVOID ThreadLocalStoragePointer; // 2Ch
PPEB Peb; // 30h
ULONG LastErrorValue; // 34h
ULONG CountOfOwnedCriticalSections; // 38h
PVOID CsrClientThread; // 3Ch
PVOID Win32ThreadInfo; // 40h
ULONG Win32ClientInfo[0x1F]; // 44h
PVOID WOW32Reserved; // C0h
ULONG CurrentLocale; // C4h
ULONG FpSoftwareStatusRegister; // C8h
PVOID SystemReserved1[0x36]; // CCh
PVOID Spare1; // 1A4h
LONG ExceptionCode; // 1A8h
ULONG SpareBytes1[0x28]; // 1ACh
PVOID SystemReserved2[0xA]; // 1D4h
GDI_TEB_BATCH GdiTebBatch; // 1FCh
ULONG gdiRgn; // 6DCh
ULONG gdiPen; // 6E0h
ULONG gdiBrush; // 6E4h
CLIENT_ID RealClientId; // 6E8h
PVOID GdiCachedProcessHandle; // 6F0h
ULONG GdiClientPID; // 6F4h
ULONG GdiClientTID; // 6F8h
PVOID GdiThreadLocaleInfo; // 6FCh
PVOID UserReserved[5]; // 700h
PVOID glDispatchTable[0x118]; // 714h
ULONG glReserved1[0x1A]; // B74h
PVOID glReserved2; // BDCh
PVOID glSectionInfo; // BE0h
PVOID glSection; // BE4h
PVOID glTable; // BE8h
PVOID glCurrentRC; // BECh
PVOID glContext; // BF0h
NTSTATUS LastStatusValue; // BF4h
UNICODE_STRING StaticUnicodeString; // BF8h
WCHAR StaticUnicodeBuffer[0x105]; // C00h
PVOID DeallocationStack; // E0Ch
PVOID TlsSlots[0x40]; // E10h
LIST_ENTRY TlsLinks; // F10h
PVOID Vdm; // F18h
PVOID ReservedForNtRpc; // F1Ch
PVOID DbgSsReserved[0x2]; // F20h
ULONG HardErrorDisabled; // F28h
PVOID Instrumentation[0x10]; // F2Ch
PVOID WinSockData; // F6Ch
ULONG GdiBatchCount; // F70h
ULONG Spare2; // F74h
ULONG Spare3; // F78h
ULONG Spare4; // F7Ch
PVOID ReservedForOle; // F80h
ULONG WaitingOnLoaderLock; // F84h
PVOID StackCommit; // F88h
PVOID StackCommitMax; // F8Ch
PVOID StackReserve; // F90h
PVOID MessageQueue; // ???
} NT_TEB, *PNT_TEB;
_NT_TIB的结构
在上例中,第一行是一个_NT_TIB
结构,占据0x1c
的长度,结构在winnt.h
中有实现:
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
#if defined(_MSC_EXTENSIONS)
union {
PVOID FiberData;
DWORD Version;
};
#else
PVOID FiberData;
#endif
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;
可以看出最后一个成员是*Self
,也就是在0x18
的位置
所以可以这么取FS:[0x18]
,从而得到TEB
的地址
在winnt.h
中是这么实现的:
// x86
return (struct _TEB *)__readfsqword(FIELD_OFFSET(NT_TIB, Self));
x64 和 x86 中_NT_TIB结构的区别
//
// 32 and 64 bit specific version for wow64 and the debugger
//
typedef struct _NT_TIB32 {
DWORD ExceptionList;
DWORD StackBase;
DWORD StackLimit;
DWORD SubSystemTib;
#if defined(_MSC_EXTENSIONS)
union {
DWORD FiberData;
DWORD Version;
};
#else
DWORD FiberData;
#endif
DWORD ArbitraryUserPointer;
DWORD Self;
} NT_TIB32, *PNT_TIB32;
typedef struct _NT_TIB64 {
DWORD64 ExceptionList;
DWORD64 StackBase;
DWORD64 StackLimit;
DWORD64 SubSystemTib;
#if defined(_MSC_EXTENSIONS)
union {
DWORD64 FiberData;
DWORD Version;
};
#else
DWORD64 FiberData;
#endif
DWORD64 ArbitraryUserPointer;
DWORD64 Self;
} NT_TIB64, *PNT_TIB64;
取PEB地址
看到没有 :
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
PPEB Peb; // 30h
TEB
偏移的0x30
地址就是PEB
的地址,所以上例使用FS:[0x30]
即可取到,下面使用WinDbg
来看看PEB
的结构:
dt _PEB
内容太长,略过
结构的实现如下
typedef struct _PEB
{
UCHAR InheritedAddressSpace; // 00h
UCHAR ReadImageFileExecOptions; // 01h
UCHAR BeingDebugged; // 02h 这里就表示程序是否是在被调试模式
UCHAR Spare; // 03h
PVOID Mutant; // 04h
PVOID ImageBaseAddress; // 08h
PPEB_LDR_DATA Ldr; // 0Ch
PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
PVOID SubSystemData; // 14h
PVOID ProcessHeap; // 18h
PVOID FastPebLock; // 1Ch
PPEBLOCKROUTINE FastPebLockRoutine; // 20h
PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
ULONG EnvironmentUpdateCount; // 28h
PVOID* KernelCallbackTable; // 2Ch
PVOID EventLogSection; // 30h
PVOID EventLog; // 34h
PPEB_FREE_BLOCK FreeList; // 38h
ULONG TlsExpansionCounter; // 3Ch
PVOID TlsBitmap; // 40h
ULONG TlsBitmapBits[0x2]; // 44h
PVOID ReadOnlySharedMemoryBase; // 4Ch
PVOID ReadOnlySharedMemoryHeap; // 50h
PVOID* ReadOnlyStaticServerData; // 54h
PVOID AnsiCodePageData; // 58h
PVOID OemCodePageData; // 5Ch
PVOID UnicodeCaseTableData; // 60h
ULONG NumberOfProcessors; // 64h
ULONG NtGlobalFlag; // 68h
UCHAR Spare2[0x4]; // 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG HeapSegmentReserve; // 78h
ULONG HeapSegmentCommit; // 7Ch
ULONG HeapDeCommitTotalFreeThreshold; // 80h
ULONG HeapDeCommitFreeBlockThreshold; // 84h
ULONG NumberOfHeaps; // 88h
ULONG MaximumNumberOfHeaps; // 8Ch
PVOID** ProcessHeaps; // 90h
PVOID GdiSharedHandleTable; // 94h
PVOID ProcessStarterHelper; // 98h
PVOID GdiDCAttributeList; // 9Ch
PVOID LoaderLock; // A0h
ULONG OSMajorVersion; // A4h
ULONG OSMinorVersion; // A8h
ULONG OSBuildNumber; // ACh
ULONG OSPlatformId; // B0h
ULONG ImageSubSystem; // B4h
ULONG ImageSubSystemMajorVersion; // B8h
ULONG ImageSubSystemMinorVersion; // C0h
ULONG GdiHandleBuffer[0x22]; // C4h
PVOID ProcessWindowStation; // ???
} PEB, *PPEB;
ImageBaseAddress
就是基址了
WinDbg
下可以使用!peb
查看当前进程的PEB
地址
OllyDbg查看
Windows XP Sp1为例:
我这里写一个小程序里面就调用一个函数GetModuleHandle。
跟进这个函数去看看
这个就是这个函数的执行体了,咱们要关心的就是7C80B764这个地址的一句:
mov eax,dword ptr fs:[18]
fs:[0]
寄存器里面存的就是指向TEB
的地址,看看是什么地址啊,
看到没
eax
指向的就是TEB
的地址,接着执行,teb
地址偏移30
由上面的头文件就可以看出是peb
的地址了此时可以确定peb的地址就是
0x7FFDE000
了
BeingDebugged
的偏移是0x2
,值为1
,代表正在被调试中ImageBaseAddress
的偏移是0x8
,值为00400000
,基址
使用以下汇编代码判断程序是否被调试
mov eax, fs:[30h] ;指向 PEB
movzx eax, byte ptr [eax + 2h]
or al, al
jz _Fine
图文结构
PEB结构
PEB 链表结构
根据上面的结构,结合上面两幅图诠释了TEB的内部结构:
- 取PEB地址指针有两种手段:
手段一:FS:[0x18]+0x30
手段二:FS:[0x30]
-
TIB
结构实际被包含在TEB
的首部 -
FS:[0]
存放的是指向TIB
结构的Exception List
成员的指针 -
PEB
首地址加上偏移0xC
的位置存放的指向PEB_LDR_DATA
的指针 -
PEB_LDR_DATA
结构尾部实际上存放的是LIST_ENTRY
的数组 -
InLoadOrderModuleList
的FLINK
存放的是指向LDR_MODULE
的指针 -
LDR_MODULE
结构的首部存放的是LIST_ENTRY
结构的数组
异常链表结构如下
typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext
);
typedef EXCEPTION_ROUTINE *PEXCEPTION_ROUTINE;
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
typedef EXCEPTION_REGISTRATION_RECORD *PEXCEPTION_REGISTRATION_RECORD;