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!