After eyeing it for a while I finally decided to buy Advanced .NET Debugging by Mario Hewardt. I’ve been studying WinDbg
for some time and consider myself somewhere between beginner and intermediate level. To my dismay I got stuck on the first excercise! Luckily I didn’t give up and finally stumbled on a blog post that unblocked me. This series has for goal to make Advanced .NET Debugging more accessible to people - quite like me - that haven’t grasped all the concepts yet.
Prerequisites
- A hex viewer
- I used the hexdump for VSCode Visual Studio Code extension
- WinDbg
- Windows
The problem
In the section Loading Native Images, Mario explains how Windows is loading a native image using Notepad.exe
(%SystemRoot%\notepad.exe
) as an example. As the first step, Mario instructs the reader to:
go to file offset
0x108
where you will find theAddressOfEntryPoint
field
The book was written a few years ago and back then Mario was running Windows Vista
(most likely in 32-bit
too). If you look at the same file offset in Windows 10 64-bit
you’ll be disapointed:
OK, there are quite a few things to unpack in this screenshot.
Hexadecimal
Each white cell represents a byte
. A byte
has 256 different values (from 0
to 255
). If we want to represent the value 255
in base 2
(binary
), we would need 8 characters: 11111111
. The same value in base 10
(decimal
) still requires 3 characters: 255
. In base 16
(hexadecimal
) we only need 2 characters: FF
. Hence hexadecimal
strikes a good balance between brevity and not being too remote from the decimal base we human-beings use. You can use the Windows 10
calculator in Programmer
mode to convert between hexadecimal
and decimal
:
File offset
The byte
s are displayed from left to right and top to bottom. They are accessed via a file offset, represented by the blue numbers. The left column represents the 7 most significant digits while the top row represents the least significant digit.
The cell on the third row (00000020
) and last column (0F
) has a file offset of 0000002F
. File offsets are 4 byte
s long, so every time we’ll be looking for an offset or an address we know it’ll be encoded over 4 byte
s.
Endianness
Windows is a little-endian system. This means than the least significant byte
s will appear before the most significant ones. So if you were to find the following value: E0 93 01 00
, the address would be 00 01 93 E0
- only the byte
order is inverted, not the order within a byte
- which would commonly be written as 0x193E0
(0x
denotes a hex notation and the leading 0
s are dropped as they are not significant).
Figuring out the file offset of AddressOfEntryPoint
Now that we know how to read the hex
dump, we’re still left with the same problem: there is no address where it’s supposed to be. This is when I started to browse the Internet trying to understand where AddressOfEntryPoint
was supposed to be located. My quest initially took me to the PE Format specification, after reading it for a while I ended up being more confused than I initially was. The situation was dire and I needed some hope, and hope did appear in the person of Simon Cooper and his brilliant post Anatomy of a .NET Assembly – PE Headers. This illustration details the required steps to find the value of the AddressOfEntryPoint
:
Thanks to Simon’s detailed write-up I was able to figure out the file offset of the AddressOfEntryPoint
(0x120
), I also found its value: 0x193E0
. You can use the below formula to compute the AddressOfEntryPoint
file offset based of the signature file offset:
Signature file offset +
0x28
=AddressOfEntryPoint
file offset
If we look back at the screenshot above we can see than the signature file offset was 0xF8
. Hence 0xF8
+ 0x28
= 0x120
, which is exactly what we found without using the formula.
Relative virtual address
But we’re now faced with another issue, the address entry point (0x193E0
) resolves to some kind of wasteland:
The Portable Executable format has the concept of Relative Virtual Address (RVA
) which it defines like this:
In an image file, the address of an item after it is loaded into memory, with the base address of the image file subtracted from it.
As it turns out the AddressOfEntryPoint
is not a file offset, it is actually a RVA
.
So we need to load Notepad in memory which equates to running it. But we also need to be able to see the base address
of the image which is not something than the Task Manager
or any other basic tool will be able to provide us. To see this value we need a debugger. Open Notepad.exe
(%SystemRoot%\notepad.exe
) using WinDbg Preview:
Type the command List Loaded Modules:
The value we’re interested in is 00007ff6`f92f0000
, this is the start
(i.e. the base address
) of the notepad
module.
Armed with this information we’ll be able to look at the instructions located at the RVA
0x193E0
by using the Unassemble command:
Bingo!
Conclusion
I hope this post clarified how to find the entry point of a native Windows executable.