本文最后更新于:2022年5月27日 下午
第47章 PEB
本章将学习有关PEB( Process Environment Block,进程环境块)的知识。PEB与前面学过的TEB都属于高级调试的基础知识,希望大家认真学习,理解并掌握相关概念。
47.1 PEB
PEB是存放进程信息的结构体,尺寸非常大,其大部分内容都已被文档化。本章只讲解它的几个重要成员,后面的调试中会经常接触。
47.1.1 PEB访问方法
先了解访问PEB结构体的方法。在前面TEB结构体的学习中我们已经知道,TEB.ProcessEnvironmentBlock成员就是PEB结构体的地址。TEB结构体位于FS段选择符所指的段内存的起始地址处,且ProcessEnvironmentBlock成员位于距TEB结构体Offset 30的位置。所以有如下等
式成立:
FS:[30] = TEB.ProcessEnvironmentBlock = address of PEB
用如下汇编代码表示上述等式:
方法#1:直接获取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
|
方法#2是方法# 1的展开形式,它们都引用了TEB.ProcessEnvironmentBlock成员的值。
请注意:下面示例中出现的地址会随用户环境的不同而不同。
接下来使用OllyDbg工具查看PEB结构体。打开Notepad.exe程序后,在EP代码处输入汇编指令(快捷键:Space空格键),如图47-1所示(也可以打开其他任意一个非notepad.exe的程序)。
执行上面输入的汇编指令(StepIn(F7)或StepOver(F8)),EAX寄存器中存入FS:[30]的值,即
PEB结构体的地址,如图47-2所示。
在Dump窗口中查看该PEB的地址(7FFD6000),如图47-3所示。
下面了解PEB结构体各成员。
47.1.2 PEB结构体的定义
不同OS下PEB结构体成员略有不同,许多成员都已被文档化。MSDN中关于PEB的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID Reserved4[3]; PVOID AtlThunkSListPtr; PVOID Reserved5; ULONG Reserved6; PVOID Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; PVOID Reserved9[45]; BYTE Reserved10[96]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved11[128]; PVOID Reserved12[1]; ULONG SessionId; } PEB, *PPEB;
|
借助WinDbg调试器可以详细查看PEB结构体成员。
47.1.3 PEB结构体的成员
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
| +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
|
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
| dt ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 BitField : UChar +0x003 ImageUsesLargePages : Pos 0, 1 Bit +0x003 IsProtectedProcess : Pos 1, 1 Bit +0x003 IsLegacyProcess : Pos 2, 1 Bit +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit +0x003 SpareBits : Pos 5, 3 Bits +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 AtlThunkSListPtr : Ptr32 Void +0x024 IFEOKey : Ptr32 Void +0x028 CrossProcessFlags : Uint4B +0x028 ProcessInJob : Pos 0, 1 Bit +0x028 ProcessInitializing : Pos 1, 1 Bit +0x028 ProcessUsingVEH : Pos 2, 1 Bit +0x028 ProcessUsingVCH : Pos 3, 1 Bit +0x028 ProcessUsingFTH : Pos 4, 1 Bit +0x028 ReservedBits0 : Pos 5, 27 Bits +0x02c KernelCallbackTable : Ptr32 Void +0x02c UserSharedInfoPtr : Ptr32 Void +0x030 SystemReserved : [1] Uint4B +0x034 AtlThunkSListPtr32 : Uint4B +0x038 ApiSetMap : Ptr32 Void +0x03c TlsExpansionCounter : Uint4B +0x040 TlsBitmap : Ptr32 Void +0x044 TlsBitmapBits : [2] Uint4B +0x04c ReadOnlySharedMemoryBase : Ptr32 Void +0x050 HotpatchInformation : 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 _RTL_CRITICAL_SECTION +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 ActiveProcessAffinityMask : 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 _ACTIVATION_CONTEXT_DATA +0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP +0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA +0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP +0x208 MinimumStackCommit : Uint4B +0x20c FlsCallback : Ptr32 _FLS_CALLBACK_INFO +0x210 FlsListHead : _LIST_ENTRY +0x218 FlsBitmap : Ptr32 Void +0x21c FlsBitmapBits : [4] Uint4B +0x22c FlsHighIndex : Uint4B +0x230 WerRegistrationData : Ptr32 Void +0x234 WerShipAssertPtr : Ptr32 Void +0x238 pContextData : Ptr32 Void +0x23c pImageHeaderHash : Ptr32 Void +0x240 TracingFlags : Uint4B +0x240 HeapTracingEnabled : Pos 0, 1 Bit +0x240 CritSecTracingEnabled : Pos 1, 1 Bit +0x240 SpareTracingBits : Pos 2, 30 Bits
|
47.2 PEB的重要成员
PEB结构体非常庞大,且结构复杂,我们只简单讲解其中几个与代码逆向分析相关的重要成员。
1 2 3 4 5
| +0x002 BeingDebugged : UChar +0x008 ImageBaseAddress : Ptr32 Void +0x00c Ldr : Ptr32 _PEB_LDR_DATA +0x018 ProcessHeap : Ptr32 Void +0x068 NtGlobalFlag : Uint4B
|
47.2.1 PEB.BeingDebugged
Kernel32.dll中有个名为Kernel32!IsDebuggerPresent()的API,但普通的应用程序开发中并不常用。
1
| BOOL WINAPI IsDebuggerPresent(void)
|
顾名思义,该API函数用于判断当前进程是否处于调试状态,并返回判断结果。该API通过检测PEB.BeingDebugged成员来确定是否正在调试进程(是,则返回1;否,则返回0)。图47~4中
显示IsDebuggerPresent() API的代码。
Windows 7中,IsDebuggerPresent() API 是在 Kernelbase.dll 中实现的。而在 Windows
XP及以前版本的操作系统中,它是在kernel32.dll中实现的。
在图47-4中先获取FS:[18]的TEB地址,然后通过DS:[TEB+30]处的TEB.ProcessEnvironmentBlock成员访问PEB结构体。这与直接使用FS:[30]访问PEB结构体是一样的(如上所述,原则上要先获取TEB结构体)。我的电脑环境中,PEB结构体的地址为7FFD5000,所以PEB.BeingDebugged成员的地址为7FFDF002,其值为1(TRUE),如图47-5所示,表示当前进程处于调试状态。
该值在代码逆向分析领域主要用于反调试技术。检测该值,若进程处于调试中,则终止进程。如各位所见,这是一种非常简单的基础反调试技术,关于反调试技术的更多内容将在第50章详细讲解。
47.2.2 PEB.ImageBaseAddress
PEB.ImageBaseAddress成员用来表示进程的ImageBase,如图47-6所示。
GetModuleHandle() API用来获取ImageBase。
1 2 3
| HMODULE GetModuleHandle( LPCTSTR lpModuleName );
|
向IpModuleName参数赋值为NULL,调用GetModuleHandle()函数将返回进程被加载的
ImageBase。图47-7显示了GetModuleHandle() API的部分代码。
向IpModuleName参数赋人NULL值后,调用GetModuleHandle()函数时将执行上图中的代码。
从中可以看到,PEB.ImageBaseAddress成员的值被设置到EAX寄存器(函数的返回值)。
47.2.3 PEB.Ldr
PEB.Ldr成员是指向_PEB_LDR_DATA结构体的指针。借助WinDbg调试器查看_PEB_LDR_DATA结构体成员,如代码47-5所示。
1 2 3 4 5 6 7 8 9 10
| lkd> dt ntdll!_PEB_LDR_DATA +0x000 Length : Uint4B +0x004 Initialized : UChar +0x008 SsHandle : Ptr32 Void +0x00c InLoadOrderModuleList : _LIST_ENTRY +0x014 InMemoryOrderModuleList : _LIST_ENTRY +0x01c InInitializationOrderModuleList : _LIST_ENTRY +0x024 EntryInProgress : Ptr32 Void +0x028 ShutdownInProgress : UChar +0x02c ShutdownThreadId : Ptr32 Void
|
当模块(DLL)加载到进程后,通过PEB.Ldr成员可以直接获取该模块的加载基地址,所以PEB.Ldr是非常重要的成员。_PEB_LDR_DATA结构体成员中有3个_LIST_ENTRY类型的成员
(InLoadOrderModuleList、InMemoryOrderModuleList 、InlnitializationOrderModuleList),_LIST_ENTRY结构体的定义如代码47-6所示。
1 2 3 4 5
| typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY;
|
从上述结构体的定义可以看到,_LIST_ENTRY结构体提供了双向链表机制。那么链表中保存着哪些信息呢? 是_LDR_DATA_TABLE_ENTRY结构体的信息。该结构体的定义如代码47-7所示。
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结构体,这些结构体相互链接,最终形双向链表(参考代码47-6)。需要注意的是, _PEB_LDR_DATA结构体中存在3种链表。也就是说,存在多个_LDR_DATA_TABLE_ENTRY结构体,并且有3种链接方法可以将它们链接起来。
47.2.4 PEB.ProcessHeap & PEB.NtGlobalFlag
PEB.ProcessHeap与PEB.NtGlobalFlag成员(像PEB.BeingDebugged成员一样)应用于反调试技术。若进程处于调试状态,则ProcessHeap与NtGlobalFlag成员就持有特定值。由于它们具有这一个特征,所以常常应用于反调试技术(详解请参考第50章)。
47.3 小结
我们学习了有关TEB、PEB的知识,后面的调试中会经常遇到。即使各位现在还不能完全理解也不要着急,通过后面的调试练习不断重复学习,最终都会理解的。