Two Offset Both Resolves to _IMAGE_DATA_DIRECTORY
This is the question of the day.
Background
To write custom shellcode, break free from tools, and do more.
One way of achieving that is to dynamically resolve symbols from kernel32.dll. And to do that, I have to get reference to _IMAGE_DATA_DIRECTORY
structure, step by step.
One of the very first step is to resolve the address of the _IMAGE_NT_HEADERS
.
Interestingly, two offsets can both do the same thing.
Description
First I checked kernel32 module base address.
lm m kernel32
Then I dumped the _IMAGE_DOS_HEADER
.
Here, I found that two of the addresses 0x3C, and 0xF8 (0n248) can both resolve to _IMAGE_DATA_DIRECTORY
.
The difference is the value after colon.
Best Guesses and Leads
-
It has got something to do with relative virtual memory address or virtual memory address conversion.
-
What is the address part doing in
dt
command in WinDBG? Something information missing? -
Look into the values behind the
:
, there must be some kind of conversion going on in the back (wrong!).
Final Answer
This is kind of embarrassing, since I haven't thought about the process carefully. Or, it's indeed a deviation from dt
command and the actual operation that the code does.
The code for resolving the _IMAGE_DATA_DIRECTORY
is as follows:
mov eax, [ebx + 0x3c]
mov edi, [ebx + eax + 0x18 + 0x60]
add edi, ebx
First of all, ebx
contains the base address of kernel32.dll, assuming it's 0x76d00000
.
Then, the first line.
mov eax, [ebx + 0x3c]
The []
operator retrieves what's in that address, which is the offset to PE header, 0xf8
. So, that being said, 0x3c
is the offset to the offset to PE header.
This can be confirmed by single stepping through the code.
eax
now contains 0xf8
.
Then, the second line.
mov edi, [ebx + eax + 0x18 + 0x60]
The code retrieves what's in address 0x76d00000 + 0xf8 + 0x18 + 0x60
, this is the relative virtual address (RVA) of _IMAGE_DATA_DIRECROTY
. In this case, 0x75480
.
And it can be confirmed with dt
.
dt _IMAGE_DATA_DIRECTORY (0x76d00000 + 0xf8 + 0x18 + 0x60)
And last line of code.
add edi, ebx
It gets the virtual memory address (VMA) of _IMAGE_DATA_DIRECROTY
by adding the base address to the RVA above.
If I do a dt
command on the VMA, I got
which indicates there's no offset from base address anymore, I am right at the data directory (correct me if I'm wrong on this).
Do a dd
and check if I can read that address.
I can, which indicates a valid memory address.
And from now on, I can retrieve the fields in the data directory by adding the offsets according to this post.
_IMAGE_EXPORT_DIRECTORY
function prototype.
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions;
DWORD AddressOfNames;
DWORD AddressOfNameOrdinals;
}
And look at this. It's confirmed that I'm accessing the _IMAGE_DATA_DIRECROTY
since the value match.
I think I was wrong about the conversion behind the scene if I use 0x3c
to dump the _IMAGE_DATA_DIRECROTY
structure using dt
command. There is no conversion at all. Using 0x3c
is simply wrong.
dt
command will dump any symbol accurately only with a correct starting vma. As the next picture shows, it can resolve the symbol even without an address, it's just the two fields are not calculated.
And with 0x76d00000 + 0x3c + 0x18 + 0x60
, I got this result.
And the address at 0x76d00000 + 0xd556e6c2
is not accessible.
Summary
That concludes this question. There's some misunderstanding about the dt
command. The most reliable way is to debug through the code and see how things work.