Results 1 to 4 of 4

Thread: Deep Dive into Process Environment Block with Windbg Share/Save - My123World.Com!

  1. #1

    Deep Dive into Process Environment Block with Windbg

    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.

  2. #2
    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.

  3. #3
    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
    Last edited by c0d3inj3cT; 01-07-2012 at 02:00 PM.

  4. #4
    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,InInitializationOrderLinks)))
       +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.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts