Assembly Language Basics - Part 8

05 Jul 2016

In the last post, we dumped the NT header and looked at the file header and the optional header. We also compared the output generated by !dh command with the actual values in the structure dumped using dt command. By now, you already know all the details necessary to execute a program. To start execution of a program, these are the steps that a loader would perform:

  • Locate the DOS Header
  • Using the RVA in e_lfanew member of the DOS header, locate the NT Header.
  • From the NT header, get the optional header and locate the AddressOfEntryPoint member.
  • This is an RVA, so calculate the actual address and call that address.

Here is the pseudocode:

//-- loadedProgram is where the image was loaded to
IMAGE_DOS_HEADER* pImage = (IMAGE_DOS_HEADER*) loadedProgram;

//-- go to NT HEADERS
IMAGE_NT_HEADERS* pHeaders = (IMAGE_NT_HEADERS*)(loadedProgram + pImage->e_lfanew);

//-- get image base and entry point address from optional header
int base = pHeaders->OptionalHeader.ImageBase;
int entryPoint = pHeaders->OptionalHeader.AddressOfEntryPoint;

//-- entry point function is at base+entryPoint
void (*entryFunction) () = (entryPoint + base);

//-- call program entry point
entryFunction();

In our case, the base address was 00007ff63f030000 and AddressofEntryPoint was 0x193e0 (RVA). So the actual address was calculated as 00007ff63f0493e0. You can use the u and uF commands to unassemble code at a given address. For more details type .hh u to view the help file.

0:000> lmvm notepad
Browse full module list
start             end                 module name
00007ff6`3f030000 00007ff6`3f071000   notepad    (pdb symbols)          
--snip--

0:000> dt ntdll!_IMAGE_NT_HEADERS64 00007ff6`3f0300f8 OptionalHeader.AddressOfEntryPoint
   +0x018 OptionalHeader                     : 
      +0x010 AddressOfEntryPoint                : 0x193e0

0:000> ?00007ff6`3f030000 + 0x193e0
Evaluate expression: 140695595946976 = 00007ff6`3f0493e0

0:000> u 00007ff6`3f0493e0
notepad!WinMainCRTStartup:
00007ff6`3f0493e0 4883ec28        sub     rsp,28h
00007ff6`3f0493e4 e8c7070000      call    notepad!_security_init_cookie (00007ff6`3f049bb0)
00007ff6`3f0493e9 4883c428        add     rsp,28h
00007ff6`3f0493ed e902000000      jmp     notepad!__mainCRTStartup (00007ff6`3f0493f4)  >>>  jump 
00007ff6`3f0493f2 cc              int     3
00007ff6`3f0493f3 cc              int     3

0:000> uF notepad!__mainCRTStartup
notepad!__mainCRTStartup:
00007ff6`3f0493f4 48895c2408      mov     qword ptr [rsp+8],rbx
00007ff6`3f0493f9 48897c2410      mov     qword ptr [rsp+10h],rdi
00007ff6`3f0493fe 4156            push    r14
00007ff6`3f049400 4881ecb0000000  sub     rsp,0B0h
00007ff6`3f049407 8364242000      and     dword ptr [rsp+20h],0
00007ff6`3f04940c 488d4c2440      lea     rcx,[rsp+40h]
00007ff6`3f049411 ff1501140000    call    qword ptr [notepad!_imp_GetStartupInfoW (00007ff6`3f04a818)]
00007ff6`3f049417 90              nop
---snip---
//-- there is a lot of intermediate steps I dont intend to explain, finally there is a call to the WinMain function of notepad
00007ff6`3f04958e e8b1a3feff      call    notepad!WinMain (00007ff6`3f033944)   <------- call to the WinMain() 
00007ff6`3f049593 8905a7940000    mov     dword ptr [notepad!g_header_init_InitializeResultHeader+0xf (00007ff6`3f052a40)],eax
00007ff6`3f049599 833dc094000000  cmp     dword ptr [notepad!g_header_init_InitializeResultHeader+0x2f (00007ff6`3f052a60)],0
00007ff6`3f0495a0 7508            jne     notepad!__mainCRTStartup+0x1b6 (00007ff6`3f0495aa)  Branch

notepad!__mainCRTStartup+0x1ae:
00007ff6`3f0495a2 8bc8            mov     ecx,eax
00007ff6`3f0495a4 ff1546190000    call    qword ptr [notepad!_imp_exit (00007ff6`3f04aef0)]

As you can see - the Entry Function is not exactly main() or WinMain() - this is just a stub that will call the Runtime’s main function. In my case, WinMainCRTStartup() is the function that was called. After the run time performs its initialization, it calls your main function and proceeds from there.

The next part of you exploration of the PE - involves dumping members of the NT header. The dt command gives you hyperlinks to dump members, you can also dump embedded member structures using dt if their types are known using the syntax dt <type> <address> <member>.. If the member is not a structure but a pointer, then replace . with ->*, so the syntax becomes dt <type> <address> <member>->*.

0:000> dt ntdll!_IMAGE_NT_HEADERS64 00007ff6`3f0300f8
   +0x000 Signature        : 0x4550
   +0x004 FileHeader       : _IMAGE_FILE_HEADER
   +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER64

0:000> dt ntdll!_IMAGE_NT_HEADERS64 00007ff6`3f0300f8 OptionalHeader.
   +0x018 OptionalHeader  :             <-- offset 0x00007ff6`3f0300f8 + 0x18
      +0x000 Magic           : 0x20b
      +0x002 MajorLinkerVersion : 0xe ''
      +0x003 MinorLinkerVersion : 0xa ''
      +0x004 SizeOfCode      : 0x19000
      +0x008 SizeOfInitializedData : 0x25200
      +0x00c SizeOfUninitializedData : 0
      +0x010 AddressOfEntryPoint : 0x193e0  <-- offset 0x00007ff6`3f0300f8 + 0x18 + 0x10
      +0x014 BaseOfCode      : 0x1000
      +0x018 ImageBase       : 0x00007ff6`3f030000
      +0x020 SectionAlignment : 0x1000
      +0x024 FileAlignment   : 0x200
      +0x028 MajorOperatingSystemVersion : 0xa
      +0x02a MinorOperatingSystemVersion : 0
      +0x02c MajorImageVersion : 0xa
      +0x02e MinorImageVersion : 0
      +0x030 MajorSubsystemVersion : 0xa
      +0x032 MinorSubsystemVersion : 0
      +0x034 Win32VersionValue : 0
      +0x038 SizeOfImage     : 0x41000
      +0x03c SizeOfHeaders   : 0x400
      +0x040 CheckSum        : 0x4ac7b
      +0x044 Subsystem       : 2
      +0x046 DllCharacteristics : 0xc160
      +0x048 SizeOfStackReserve : 0x80000
      +0x050 SizeOfStackCommit : 0x11000
      +0x058 SizeOfHeapReserve : 0x100000
      +0x060 SizeOfHeapCommit : 0x1000
      +0x068 LoaderFlags     : 0
      +0x06c NumberOfRvaAndSizes : 0x10
      +0x070 DataDirectory   : [16] _IMAGE_DATA_DIRECTORY  <-- offset 0x00007ff6`3f0300f8 + 0x18 + 0x70

0:000> ?0x00007ff6`3f0300f8 + 0x18 + 0x70
Evaluate expression: 140695595843968 = 00007ff6`3f030180

The DataDirectory member is actually an array of 16 IMAGE_DATA_DIRECTORY structures. The offset of this member is at 0x00007ff63f0300f8 + 0x18 + 0x70 = 0x00007ff63f030180. To dump an array, we use the syntax: dt -a<number of elements> <type> <address>

0:000> dt -a16 ntdll!_IMAGE_DATA_DIRECTORY 00007ff6`3f030180
[0] @ 00007ff6`3f030180 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[1] @ 00007ff6`3f030188 
---------------------------------------------
   +0x000 VirtualAddress   : 0x1f4d8
   +0x004 Size             : 0x244

[2] @ 00007ff6`3f030190 
---------------------------------------------
   +0x000 VirtualAddress   : 0x26000
   +0x004 Size             : 0x19ce0

[3] @ 00007ff6`3f030198 
---------------------------------------------
   +0x000 VirtualAddress   : 0x25000
   +0x004 Size             : 0x8dc

[4] @ 00007ff6`3f0301a0 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[5] @ 00007ff6`3f0301a8 
---------------------------------------------
   +0x000 VirtualAddress   : 0x40000
   +0x004 Size             : 0x21c

[6] @ 00007ff6`3f0301b0 
---------------------------------------------
   +0x000 VirtualAddress   : 0x1e370
   +0x004 Size             : 0x54

[7] @ 00007ff6`3f0301b8 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[8] @ 00007ff6`3f0301c0 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[9] @ 00007ff6`3f0301c8 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[10] @ 00007ff6`3f0301d0 
---------------------------------------------
   +0x000 VirtualAddress   : 0x1a550
   +0x004 Size             : 0x100

[11] @ 00007ff6`3f0301d8 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[12] @ 00007ff6`3f0301e0 
---------------------------------------------
   +0x000 VirtualAddress   : 0x1a650
   +0x004 Size             : 0x978

[13] @ 00007ff6`3f0301e8 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[14] @ 00007ff6`3f0301f0 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

[15] @ 00007ff6`3f0301f8 
---------------------------------------------
   +0x000 VirtualAddress   : 0
   +0x004 Size             : 0

These directories each have a special meaning, Here’s the list of the directories and a brief description. I’ll add the remaining as I explore and learn.

Directory Description
Export table address and size Contains the export table, which lists the functions exported from this module
Import table address and size Contains the import table, which lists functions imported by this module
Resource table address and size Resources added to this module
Exception table address and size Exception handling information
Certificate table address and size  
Base relocation table address and size  
Debugging information starting address and size Symbols and debugging info
Architecture-specific data address and size  
Global pointer register relative virtual address  
Thread local storage (TLS) table address and size Thread local storages
Load configuration table address and size Loader configuration
Bound import table address and size  
Import address table address and size  
Delay import descriptor address and size  
The CLR header address and size Contains information for use by CLR if this is a .NET assembly
Reserved  

well, that’s it for now - see you in the next post!