What I am trying to do is calculate the size of a PE through it's headers. I am using WinDbg's Javascripting and in this case, it will mostly be for drivers. The idea is to dump a driver from memory through WinDbg and I can do it by dumping the BaseAddress to BaseAddress+ImageSize. The ImageSize isn't really the actual size of it on disk and I would like it to be as similar as possible, almost like copy/pasting.

I found this post: https://stackoverflow.com/questions/29587560/self-inspection-of-the-pe-format I am able to get the full size on one driver, easily:

0: kd> !dh HEVD
  .reloc name
      14 virtual size
   8B000 virtual address
     200 size of raw data
    6E00 file pointer to raw data

0: kd> ? 8b000 + 14
Evaluate expression: 569364 = 00000000`0008b014
0: kd> ? 6e00 + 200
Evaluate expression: 28672 = 00000000`00007000

The person mentioned that you should add the highest value for PointerToRawData against the SizeOfRawData, which is usually the last section. The size for this particular driver is 28,672 bytes but this doesn't always work for all drivers.

Here's an example of where it doesn't work:

0: kd> !dh RTCore64
400 size of headers
    INIT name
     258 virtual size
    5000 virtual address
     400 size of raw data
    1600 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
E2000020 flags
         (no align specified)
         Execute Read Write
0: kd> ? 1600 + 400
Evaluate expression: 6656 = 00000000`00001a00

The file size for this particular driver is 16,384 bytes on disk and 14,024 bytes regular size.

Then I found this post: https://stackoverflow.com/questions/34684660/how-to-determine-the-size-of-an-pe-executable-file-from-headers-and-or-footers It was saying to add the size of headers against the SizeOfRawData for each section. That didn't work.

0: kd> ? 400 + c00 + 200 + 200 + 200 + 400
Evaluate expression: 6656 = 00000000`00001a00

I also tried this answer: https://stackoverflow.com/questions/8197134/how-do-i-determine-exact-pe-image-file-size-using-its-headers Basically it was said to add the VirtualAddress against VirtualSize, but that doesn't work. It actually gives me a number that's higher than what the actual size is.

0: kd> ? 5000 + 258
Evaluate expression: 21080 = 00000000`00005258

I also tried with this post: https://stackoverflow.com/questions/8193862/the-size-of-a-pe-header That also didn't work. There were other posts I looked at, but they all boil down to some variation of those.

Can I get help on why I am unable to? I'm not sure what I'm missing.

Thank you in advance

Also, if you would recommend a better way of doing so, I'm all ears!


This is quickly thrown together, it's late.

// Grab first section in the image and initialize our file dump data.
auto sec = IMAGE_FIRST_SECTION(nt);
auto img = std::vector<uint8_t>();

// Reserve (more than) enough space for the file and zero initialize it.
img.reserve(nt->OptionalHeaders.SizeOfImage, 0);

// Grab base address for both virtual memory and file dump.
auto img_mem = 0xDEADBEEF;
auto img_dmp = img.data();

// Save PE header to the file dump.
memcpy(img_dmp, img_mem, sec->PointerToRawData);

// Save each sections data to the file dump, permission checks should be done here as well.
for (auto i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++sec)
     memcpy(img_dmp + sec->PointerToRawData, img_mem + sec->VirtualAddress, sec->SizeOfRawData);

Furthermore, a simple way to get the file size of a PE is just to get the highest sections PointerToRawData and add its SizeOfRawData.

  • Thank you, but what I did find is that won't always work. The sizes will be different doing it that way compared to what the file size is on the actual driver properties. I must be missing something, but it's one of those that made me go down the rabbit hole and now I just can't leave it alone.
    – Jon
    Oct 29 '19 at 18:08
  • What exactly is causing your issue? Some sections might have a virtual size but no raw size (i.e from a packer) and virtual size may be larger than raw size (i.e only part of the section has initialized data) but those are easy to fix. Each section has a VirtualAddress field (rva from base when in memory), a PointerToRawData field (file offset where section data starts), and SizeOfRawData (size of section on disk). What else do you need? The highest section file offset plus raw size is the size of your image. You may also need to make sure it's aligned with FileAlignment. Oct 29 '19 at 21:21
  • Sorry, just noticed you edited OP. If you want to upload the file (or just PE) to that RTCore driver I can take a look at it. Something weird might be going on, like maybe they embed a bunch of data after the last section. Although I don't really know why since it wouldn't be mapped anyway if it's not a part of SizeOfRawData. Oct 29 '19 at 22:07
  • Hey no worries! Yea man, that's what I was wondering too. I wasn't entirely sure what was going on so I was just like ummm...okayyy. You can snag the driver here: github.com/ch3rn0byl/Driver-Puller-Thingy/blob/master/Drivers/… I'm not entirely sure if the size of it will get in the way of what I'm trying to do but im basically pulling an obfuscated driver from memory to be able to view it statically through IDA. I am doing it on a driver that I know of that way I know it works first. So me wondering if that will be an issue led me down this rabbit hole. Thank you!!
    – Jon
    Oct 29 '19 at 22:43
  • Ah, looks like the remaining bytes are all the embedded signed certificate for the driver. In this case the data isn't in a section but rather pointed to by IMAGE_DIRECTORY_ENTRY_SECURITY in the PE header. Oct 30 '19 at 1:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.