Process Environment Block (PEB) is a user mode data structure which applies over a whole process. It is designed to be used by the application-mode code in the operating system libraries, such as NTDLL.dll, Kernel32.dll. Through the use of PEB one can obtain the list of loaded modules, process startup arguments, ImageBaseAddress, heap address, check whether program is being debugged or not, find base addresses of any imported DLLs and a lot others. Malicious codes perform PEB enumeration and further walk through the export table of any module to get the function addresses.
To study more about PEB, I will use WinDbg and attach to any process (eg. notepad.exe). I am running WinDbg(64bit) on Windows 8. Finally, we will write an assembly code which does the same job without the use of debugger.
For attaching to any process in Windbg, we need to load the symbols first. You need a good internet connection if referring the symbols directly from the Microsoft website, else you have to download it into your local hard drive.
cache*;SRV*http://msdl.microsoft.com/download/symbols
;symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols
|
Address of PEB and address of ImageBaseAddress can be found using a single WinDbg command:
0:001> !peb
PEB at 000007f65cd26000 InheritedAddressSpace: No ReadImageFileExecOptions: No BeingDebugged: Yes ImageBaseAddress: 000007f65db90000 Ldr 000007fc6cd288c0 Ldr.Initialized: Yes Ldr.InInitializationOrderModuleList: 0000006b53a32200 . 0000006b53a39fa0 Ldr.InLoadOrderModuleList: 0000006b53a32360 . 0000006b53a39f80 Ldr.InMemoryOrderModuleList: 0000006b53a32370 . 0000006b53a39f90
|
In 64bit OS, PEB lies at offset 60h from Thread Execution Block. This can be confirmed here:
0:001> dt nt!_TEB
ntdll!_TEB +0x000 NtTib : _NT_TIB +0x038 EnvironmentPointer : Ptr64 Void +0x040 ClientId : _CLIENT_ID +0x050 ActiveRpcHandle : Ptr64 Void +0x058 ThreadLocalStoragePointer : Ptr64 Void +0x060 ProcessEnvironmentBlock : Ptr64 _PEB +0x068 LastErrorValue : Uint4B +0x06c CountOfOwnedCriticalSections : Uint4B
|
PEB can also be accessed using FS register. FS register can also help us find lots of information like pointer to current exception handler(FS:[0h]), process ID(FS:[20h]), environment pointer etc.
Now let’s have a look at the PEB structure.
0:001> dt nt!_PEB
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 IsPackagedProcess : Pos 5, 1 Bit +0x003 IsAppContainer : Pos 6, 1 Bit +0x003 SpareBits : Pos 7, 1 Bit +0x008 Mutant : Ptr64 Void +0x010 ImageBaseAddress : Ptr64 Void +0x018 Ldr : Ptr64 _PEB_LDR_DATA +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS +0x028 SubSystemData : Ptr64 Void +0x030 ProcessHeap : Ptr64 Void +0x038 FastPebLock : Ptr64 _RTL_CRITICAL_SECTION +0x040 AtlThunkSListPtr : Ptr64 Void +0x048 IFEOKey : Ptr64 Void +0x050 CrossProcessFlags : Uint4B +0x050 ProcessInJob : Pos 0, 1 Bit +0x050 ProcessInitializing : Pos 1, 1 Bit +0x050 ProcessUsingVEH : Pos 2, 1 Bit +0x050 ProcessUsingVCH : Pos 3, 1 Bit +0x050 ProcessUsingFTH : Pos 4, 1 Bit +0x050 ReservedBits0 : Pos 5, 27 Bits +0x058 KernelCallbackTable : Ptr64 Void +0x058 UserSharedInfoPtr : Ptr64 Void +0x060 SystemReserved : [1] Uint4B +0x064 AtlThunkSListPtr32 : Uint4B +0x068 ApiSetMap : Ptr64 Void +0x070 TlsExpansionCounter : Uint4B +0x078 TlsBitmap : Ptr64 Void +0x080 TlsBitmapBits : [2] Uint4B +0x088 ReadOnlySharedMemoryBase : Ptr64 Void +0x090 HotpatchInformation : Ptr64 Void +0x098 ReadOnlyStaticServerData : Ptr64 Ptr64 Void +0x0a0 AnsiCodePageData : Ptr64 Void +0x0a8 OemCodePageData : Ptr64 Void +0x0b0 UnicodeCaseTableData : Ptr64 Void +0x0b8 NumberOfProcessors : Uint4B +0x0bc NtGlobalFlag : Uint4B +0x0c0 CriticalSectionTimeout : _LARGE_INTEGER +0x0c8 HeapSegmentReserve : Uint8B +0x0d0 HeapSegmentCommit : Uint8B +0x0d8 HeapDeCommitTotalFreeThreshold : Uint8B +0x0e0 HeapDeCommitFreeBlockThreshold : Uint8B +0x0e8 NumberOfHeaps : Uint4B +0x0ec MaximumNumberOfHeaps : Uint4B +0x0f0 ProcessHeaps : Ptr64 Ptr64 Void +0x0f8 GdiSharedHandleTable : Ptr64 Void +0x100 ProcessStarterHelper : Ptr64 Void +0x108 GdiDCAttributeList : Uint4B +0x110 LoaderLock : Ptr64 _RTL_CRITICAL_SECTION +0x118 OSMajorVersion : Uint4B +0x11c OSMinorVersion : Uint4B +0x120 OSBuildNumber : Uint2B +0x122 OSCSDVersion : Uint2B +0x124 OSPlatformId : Uint4B +0x128 ImageSubsystem : Uint4B +0x12c ImageSubsystemMajorVersion : Uint4B +0x130 ImageSubsystemMinorVersion : Uint4B +0x138 ActiveProcessAffinityMask : Uint8B +0x140 GdiHandleBuffer : [60] Uint4B +0x230 PostProcessInitRoutine : Ptr64 void +0x238 TlsExpansionBitmap : Ptr64 Void +0x240 TlsExpansionBitmapBits : [32] Uint4B +0x2c0 SessionId : Uint4B +0x2c8 AppCompatFlags : _ULARGE_INTEGER +0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER +0x2d8 pShimData : Ptr64 Void +0x2e0 AppCompatInfo : Ptr64 Void +0x2e8 CSDVersion : _UNICODE_STRING +0x2f8 ActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA +0x300 ProcessAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP +0x308 SystemDefaultActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA +0x310 SystemAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP +0x318 MinimumStackCommit : Uint8B +0x320 FlsCallback : Ptr64 _FLS_CALLBACK_INFO +0x328 FlsListHead : _LIST_ENTRY +0x338 FlsBitmap : Ptr64 Void +0x340 FlsBitmapBits : [4] Uint4B +0x350 FlsHighIndex : Uint4B +0x358 WerRegistrationData : Ptr64 Void +0x360 WerShipAssertPtr : Ptr64 Void +0x368 pUnused : Ptr64 Void +0x370 pImageHeaderHash : Ptr64 Void +0x378 TracingFlags : Uint4B +0x378 HeapTracingEnabled : Pos 0, 1 Bit +0x378 CritSecTracingEnabled : Pos 1, 1 Bit +0x378 LibLoaderTracingEnabled : Pos 2, 1 Bit +0x378 SpareTracingBits : Pos 3, 29 Bits +0x380 CsrServerReadOnlySharedMemoryBase : Uint8B
|
PEB stores the list of all modules loaded in the current process’s address space. We follow the Ldr pointer, which takes us to another structure, _PEB_LDR_DATA. As we can see, offset 18h points to _PEB_LDR_DATA structure.
Structure of _PEB_LDR_DATA:
0:001> dt nt!_PEB_LDR_DATA 000007fc6cd288c0
ntdll!_PEB_LDR_DATA +0x000 Length : 0x58 +0x004 Initialized : 0x1 ” +0x008 SsHandle : (null) +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x0000006b`53a32360 – 0x0000006b`53a39f80 ] +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x0000006b`53a32370 – 0x0000006b`53a39f90 ] +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x0000006b`53a32200 – 0x0000006b`53a39fa0 ] +0x040 EntryInProgress : (null) +0x048 ShutdownInProgress : 0 ” +0x050 ShutdownThreadId : (null) |
The 3 doubly linked lists mentioned above store the list of DLLs in different orders. Here, the list of modules are stored in the order in which they were loaded, in the order they are located in memory and in the order in which they are initialized.
Let’s expand the InInitializationOrderModuleList field of _PEB_LDR_DATA structure as follows:
0:001> dt nt!_PEB_LDR_DATA 000007fc6cd288c0 InInitializationOrderModuleList.Flink /r1
ntdll!_PEB_LDR_DATA +0x030 InInitializationOrderModuleList : [ 0x0000006b`53a32200 – 0x0000006b`53a39fa0 ] +0x000 Flink : 0x0000006b`53a32200 _LIST_ENTRY [ 0x0000006b`53a32b90 – 0x000007fc`6cd288f0 ] |
Flink stores the address of the next loader structure. On expanding the Flink of this List which gives us the first memory address as 0x0000006b`53a32b90.
Dumping the data stored in this memory, we get the following:
0:001> dd 0x0000006b`53a32200
0000006b`53a32200 53a32b90 0000006b 6cd288f0 000007fc 0000006b`53a32210 6cbf0000 000007fc 00000000 00000000 0000006b`53a32220 001c0000 00000000 003c003a 00000000 0000006b`53a32230 53a320e0 0000006b 00140012 00000000 0000006b`53a32240 6cc5e048 000007fc 000002c4 0000ffff 0000006b`53a32250 6cd2e8f0 000007fc 6cd2e8f0 000007fc 0000006b`53a32260 51637f77 00000000 00000000 00000000 0000006b`53a32270 00000000 00000000 53a32300 0000006b
|
The memory address contains addresses of various modules loaded in the current process. So let’s check what addresses are being pointed by the 1st Ldr structure pointed by Flink.
0:001> dd 0000006b53a32b90
0000006b`53a32b90 53a327e0 0000006b 53a32200 0000006b 0000006b`53a32ba0 69e50000 000007fc 69e522f0 000007fc 0000006b`53a32bb0 000f3000 00000000 00460044 00000000 0000006b`53a32bc0 53a32b20 0000006b 001e001c 0000006b 0000006b`53a32bd0 53a32b48 0000006b 000802cc 0000ffff 0000006b`53a32be0 6cd2e7f0 000007fc 6cd2e7f0 000007fc 0000006b`53a32bf0 50988aa6 00000000 00000000 00000000 0000006b`53a32c00 00000000 00000000 53a32c90 0000006b |
The 3rd offset address 69e50000 000007fc(in Little Endian Format) should be the base address of Kernelbase.dll. Let’s check where in memory the Kernelbase.dll lies using “lm” command.
0:001> lm
start end module name 000007f6`5db90000 000007f6`5dbd0000 notepad (deferred) 000007fc`658c0000 000007fc`6593d000 WINSPOOL (deferred) 000007fc`65950000 000007fc`65bba000 COMCTL32 (deferred) 000007fc`67c80000 000007fc`67ca1000 dwmapi (deferred) 000007fc`68890000 000007fc`68976000 uxtheme (deferred) 000007fc`68c60000 000007fc`68cf6000 SHCORE (deferred) 000007fc`69880000 000007fc`698dc000 bcryptPrimitives (deferred) 000007fc`698e0000 000007fc`698ea000 CRYPTBASE (deferred) 000007fc`69e50000 000007fc`69f43000 KERNELBASE (deferred) 000007fc`69fe0000 000007fc`6b2c0000 SHELL32 (deferred) 000007fc`6b2c0000 000007fc`6b3f6000 KERNEL32 (deferred) |
Kernelbase.dll is important because it performs many functions that were earlier performed by kernel32.dll and advapi32.dll in Windows Vista and lower OS.
In assembly language, the code goes this way:
|
Step by step explanation of the program:
- PEB address 000007f65cd26000h is stored in eax.
- Store the _PEB_LDR_DATA structure address 000007fc6cd288c0 (i.e., value strored at location 000007f65cd26000h + 18h) in eax.
- Store PEB-> InInitializationOrderModuleList in ebx.
- Store the next Loader structure at address 0x0000006b`53a32b90 pointed by PEB-> InInitializationOrderModuleList.Flink in ebx.
- Finally we move the data stored on offset 0x20 (i.e., 000007fc, 69e50000) into eax register.
References:
3] Deep Drive into OS Internals using Windbg
No Comments Yet