View Full Version : Deep Dive into Process Environment Block with Windbg

01-07-2012, 12:16 PM
We will understand how to walk through a PEB data structure in win32 environment.

Process Environment Block is an important data structure from an exploiter's perspective. A shellcode executes a set of function APIs and for this it must locate and load them.

A common way of doing this is to make use of LoadLibraryA function which is exported by kernel32.dll OS module.

I showed in my previous article about IATs and EATs, that we can parse the EAT of a loaded module to find out the address of a function API exported by the module.

Similarly, if we can find the base address of kernel32.dll then we can parse it's Export Address Table and locate LoadLibraryA or LoadLibraryW functions which can further be used to load and execute the functions used by shellcode.

This is not a new technique and there are snippets available on the net which show you how to do this using assembly language code. What I felt after reading them is, there is not a good explanation provided along with those codes to describe what they are doing.

So, I will make use of windbg to walk through the assembly language code which is used to locate the base address of kernel32.dll

First let's explore the Process Environment Block. I attach my windbg to notepad.exe on a Win XP SP3 platform.

PEB is located within the virtual address space of the loaded process. This address is most often, 7ffda000 but not always. There are different ways to get the PEB base address in the process VA space.

01-07-2012, 12:21 PM
Method 1: Windbg provides pseudo registers like $peb which point to the base address of PEB data structure within the process VA space. Let's read this value by prefixing it with @ symbol.

0:001> dt @$peb
output: 7ffda000

Method 2: You can use the !peb command to read this value. !peb will display the complete PEB data structure with values.

The first few lines of output:

0:001> !peb
PEB at 7ffda000 <-------- The base address of PEB
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes

Method 3:

Now let's say I am debugging in kernel mode and I want to inspect the user mode state of the processes and grab the address of PEB. So, here's how to do it:

lkd> !process -0 0

PROCESS 8290b020 SessionId: 0 Cid: 081c Peb: 7ffda000 ParentCid: 058c
DirBase: 0e6c0240 ObjectTable: e23f1388 HandleCount: 41.
Image: notepad.exe

This will display the information about all the processes running on the local system right now. Along with other pieces of useful information like DirBase, it also displays the location of PEB.

Method 4:

PEB is a data structure in the user mode and specific to an application process running in the user mode. Similarly in the kernel mode, we have the _EPROCESS data structure which points to the PEB.

Using the Process number grabbed from the above output, I can display the _EPROCESS structure for our notepad.exe user mode process.

lkd> dt nt!_EPROCESS 8290b020

+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x1b0 Peb : 0x7ffda000 _PEB <--------- Pointer to PEB

We can see that at offset, 0x1b0 in the EPROCESS data structure, we have a 32 bit pointer to the PEB in user mode.

Method 5:

We have so far seen how to do all of this using Windbg. However, when we are writing a shellcode, we have to find a way to reference the base address of PEB using assembly language code. This is done using the concept that, in any Windows NT operating system, PEB is always located at an offset 30 to fs segment register.


fs:[30] -> points to PEB.

So, by using a simple MOV instruction, we can get a pointer to PEB in a register as follows:

mov edx, fs:[30]

More on this later.

Method 6:

Yet another way to locate the Process Environment Block in the user mode is by using the Thread Environment Block.

Using the !teb command will display the thread environment block for the current executing thread in our process. This has a pointer to the PEB as shown below:

0:001> !teb
TEB at 7ffdc000
ExceptionList: 0096ffe4
StackBase: 00970000
StackLimit: 0096f000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdc000
EnvironmentPointer: 00000000
ClientId: 00000a14 . 00000a44
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffda000 <------------ Pointer to PEB
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks: 0
HardErrorMode: 0

Method 7:

Similar to Method 1 above, we have the pseudo register, $teb which stores the address of Thread Environment Block.

0:001> dt @$teb
output: 7ffdc000

Now using this, I can display the complete TEB structure as follows:

0:001> dt nt!_TEB @$teb
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x030 ProcessEnvironmentBlock : 0x7ffda000 _PEB <---------- Pointer to PEB

So, at offset 0x030 in the TEB, we have a pointer to PEB.

01-07-2012, 12:26 PM
This also means that we can reference the TEB using, fs:[0] and since we have PEB at offset 0x030 in the TEB, so PEB can be located using, fs:[30]. Putting all the pieces of information together, this makes more sense now :)

Now, let's look at the shellcode which is used to retrieve the base address of kernel32.dll

1. xor ebx, ebx ; clear ebx
2. mov ebx, fs:[ 0x30 ] ; get a pointer to the PEB
3. mov ebx, [ ebx + 0x0C ]
4. mov ebx, [ ebx + 0x1C ]
5. mov ebx, [ ebx ]
6. mov ebx, [ ebx + 0x08 ]

The first two instructions are straight forward and so a comment is enough to explain what they do. For the second instruction, I have elaborated in the methods above that how we derive that PEB is at offset 0x30 to fs segment register.

For the next 4 instructions, I will explain in detail since they are not so easy to understand.

After instruction 2, I have the pointer to PEB in ebx register.

Instruction 3:

mov ebx, [ebx+0x0c]

I am moving the value stored at offset 0x0c of PEB into the register ebx. Using windbg, let's understand it.

0:001> dt nt!_PEB @$peb
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0x1 ''
+0x003 SpareBool : 0 ''
+0x004 Mutant : 0xffffffff
+0x008 ImageBaseAddress : 0x01000000
+0x00c Ldr : 0x001a1e90 _PEB_LDR_DATA <----- We are storing this value in the register, ebx

As we can see, at offset, 0x00c we have a pointer to the _PEB_LDR_DATA structure.

So, register ebx now has 0x001a1e90 memory address stored in it.

Instruction 4:

We are moving the value stored at offset, 0x1c in the _PEB_LDR_DATA structure into the register ebx.

We can view the structure along with the values by passing it the address, 0x001a1e90

0:001> dt nt!_PEB_LDR_DATA 0x001a1e90
+0x000 Length : 0x28
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x1a1ec0 - 0x1a2e90 ]
+0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x1a1ec8 - 0x1a2e98 ]
+0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x1a1f28 - 0x1a2ea0 ]
+0x024 EntryInProgress : (null)

We can see 3 linked lists stored in this structure.

Windows OS Loader maintains information about how the DLLs were loaded into the memory in 3 ways:

Based on the order in which they were loaded: InLoadOrderModuleList
Based on the order in which they appear in memory: InMemoryOrderModuleList
Based on the order in which they were initialized: InInitializationOrderModuleList

In our case, we are referencing offset, 0x1c into the _PEB_LDR_DATA structure which points to the InInitializationOrderModuleList.

Before we look further into these entries and what is stored in them, let's first understand these lists.

All these lists are of type, _LIST_ENTRY.

Using windbg, I can see the structure of _LIST_ENTRY as:

0:001> dt nt!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY

So it is a set of Forward Pointer and Backward Pointer. It's a double linked list keeping track of both the previous node and the next node. However, we have to
understand what data items are they pointing to.

Let's expand the InInitializationOrderModuleList field of _PEB_LDR_DATA structure as follows:

0:001> dt nt!_PEB_LDR_DATA 0x001a1e90 InInitializationOrderModuleList.Flink /r1
+0x01c InInitializationOrderModuleList : [ 0x1a1f28 - 0x1a2ea0 ]
+0x000 Flink : 0x001a1f28 _LIST_ENTRY [ 0x1a1fc8 - 0x1a1eac ]

I am expanding the Flink of this List which gives me the first memory address as, 0x001a1f28

This memory address is moved into the ebx register.

Instruction 5:

Now, we are reading the data stored at this memory address. Let's dump this data using dd command as follows:

dd 0x001a1f28

0:001> dd 0x001a1f28
001a1f28 001a1fc8 001a1eac 7c900000 7c912afc
001a1f38 000b2000 02080036 7c980048 00140012

we are storing the address, 001a1fc8 into ebx

Instruction 6:

We are moving the value stored at offset 0x8 from 001a1fc8 into the register ebx.

Let's dump the contents of 001a1fc8 address.

0:001> dd 001a1fc8
001a1fc8 001a2248 001a1f28 7c800000 7c80b64e
001a1fd8 000f6000 003e003c 001a1f70 001a0018

At offset 0: 001a2248
At offset 4: 001a1f28
At offset 8: 7c800000

So, we are moving the address 7c800000 into ebx register and this should be the base address of kernel32.dll

Let us confirm this using lm command.

0:001> lm
start end module name
01000000 01014000 notepad (deferred)
5ad70000 5ada8000 UxTheme (deferred)
5cb70000 5cb96000 ShimEng (deferred)
6f880000 6fa4a000 AcGenral (deferred)
73000000 73026000 WINSPOOL (deferred)
74720000 7476c000 MSCTF (deferred)
755c0000 755ee000 msctfime (deferred)
76390000 763ad000 IMM32 (deferred)
763b0000 763f9000 comdlg32 (deferred)
769c0000 76a74000 USERENV (deferred)
76b40000 76b6d000 WINMM (deferred)
77120000 771ab000 OLEAUT32 (deferred)
773d0000 774d3000 COMCTL32 (deferred)
774e0000 7761e000 ole32 (deferred)
77be0000 77bf5000 MSACM32 (deferred)
77c00000 77c08000 VERSION (deferred)
77c10000 77c68000 msvcrt (deferred)
77dd0000 77e6b000 ADVAPI32 (deferred)
77e70000 77f03000 RPCRT4 (deferred)
77f10000 77f59000 GDI32 (deferred)
77f60000 77fd6000 SHLWAPI (deferred)
77fe0000 77ff1000 Secur32 (deferred)
7c800000 7c8f6000 kernel32 (deferred)
7c900000 7c9b2000 ntdll (pdb symbols)
7c9c0000 7d1d8000 SHELL32 (deferred)
7e410000 7e4a1000 USER32 (deferred)

From the above list of loaded modules, we can confirm that, 7c800000 is indeed the base address of kernel32.dll

01-07-2012, 12:29 PM
In the above method, I have dumped the contents of memory addresses (Flinks) and used the offsets to see what is there. But to understand better, we need to look
deeper into the double linked lists.

The Flinks of the lists stored in _PEB_LDR_DATA structure actually point to a data structure, _LDR_DATA_TABLE_ENTRY.

Let's view the structure.

0:001> dt nt!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
+0x01c EntryPoint : Ptr32 Void
+0x020 SizeOfImage : Uint4B
+0x024 FullDllName : _UNICODE_STRING
+0x02c BaseDllName : _UNICODE_STRING
+0x034 Flags : Uint4B
+0x038 LoadCount : Uint2B
+0x03a TlsIndex : Uint2B
+0x03c HashLinks : _LIST_ENTRY
+0x03c SectionPointer : Ptr32 Void
+0x040 CheckSum : Uint4B
+0x044 TimeDateStamp : Uint4B
+0x044 LoadedImports : Ptr32 Void
+0x048 EntryPointActivationContext : Ptr32 Void
+0x04c PatchInformation : Ptr32 Void

As can be seen, the Lists in the data structure _PEB_LDR_DATA are pointing to Links in the _LDR_DATA_TABLE_ENTRY data structure.

We saw before that, _LIST_ENTRY is a set of two pointers, flink and blink. But we could not see the data item of the double linked list. We will use the concept of macro CONTAINING_RECORD of linked lists to read the real data elements of the list.

Let's look only at the fields of type, _LIST_ENTRY of the two structures:

0:001> dt nt!_PEB_LDR_DATA
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY

0:001> dt nt!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY

There is a one to one correspondence between the _LIST_ENTRY fields of the two structures. For instance, the InLoadOrderModuleList.Flink points to InLoadOrderLinks entry of _LDR_DATA_TABLE_ENTRY structure at offset 0.

InMemoryOrderModuleList.Flink field of _PEB_LDR_DATA points into InMemoryOrderLinks field of _LDR_DATA_TABLE_ENTRY at offset 0x08

InInitializationOrderModuleList.Flink field of _PEB_LDR_DATA points into InInitializationOrderLinks field of _LDR_DATA_TABLE_ENTRY at offset 0x010.

In windbg, if you want to display the offsets of fields in a structure you can use the following command,

#FIELD_OFFSET(Structure Name, Field Name)

Now, let's again take the above example shellcode and look into it:

Instruction 4:

We got that the Flink of InInitializationOrderModuleList points to 0x001a1f28 which is stored in ebx.

Instruction 5:

We take the next Flink entry, 001a1fc8 of InInitializationOrderModuleList and move it into ebx register.

Now, let's use the above structures to see what data element of the linked list it points to.

0:001> dt nt!_LDR_DATA_TABLE_ENTRY (001a1fc8 - @@(#FIELD_OFFSET(_LDR_DATA_TABLE_ENTRY,InInitializ ationOrderLinks)))

+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x1a2058 - 0x1a1f18 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x1a2060 - 0x1a1f20 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x1a2248 - 0x1a1f28 ]
+0x018 DllBase : 0x7c800000
+0x01c EntryPoint : 0x7c80b64e
+0x020 SizeOfImage : 0xf6000
+0x024 FullDllName : _UNICODE_STRING "C:\WINXP\system32\kernel32.dll"
+0x02c BaseDllName : _UNICODE_STRING "kernel32.dll"
+0x034 Flags : 0x80084004
+0x038 LoadCount : 0xffff
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x7c97e2d0 - 0x7c97e2d0 ]
+0x03c SectionPointer : 0x7c97e2d0
+0x040 CheckSum : 0x7c97e2d0
+0x044 TimeDateStamp : 0x49c4f2bb
+0x044 LoadedImports : 0x49c4f2bb
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)

At offsets 0x024 and 0x02c, we can see FullDllName and BaseDllName respectively which give us the name of DLL. This shows us that the second entry in the
InInitializationOrderModuleList contains information about the kernel32.dll module.