Question about Resolving _IMAGE_DATA_DIRECTORY

Two Offset Both Resolves to _IMAGE_DATA_DIRECTORY

This is the question of the day.


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.


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

  1. It has got something to do with relative virtual memory address or virtual memory address conversion.

  2. What is the address part doing in dt command in WinDBG? Something information missing?

  3. 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.



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.