3

How is SizeOfImage in the PE optional header computed?

Trying to learn the PE format, I've come across the SizeOfImage field in the optional header.

To quote the documentation:

The size (in bytes) of the image, including all headers, as the image is loaded in memory. It must be a multiple of SectionAlignment.

However, I've experienced that if I set this field wrongly, then the executable won't run and an error 193 (badly formatted excutable) is displayed:

enter image description here

How do I compute the SizeOfImage field, and why won't an executable run if its set wrong (e.g. the executable runs if it's set to 0x00003000 but not 0x00004000 or 0x00002000)?

Shuzheng
  • 11,288
  • 20
  • 88
  • 186
  • 2
    Check the `checksum` field (no pun intented). – Margaret Bloom Aug 18 '16 at 16:31
  • 1
    You are asking two distinct questions: `1` How is a certain header value calculated? `2` Why does the system reject an image that lies? Which one do you really need help with? – IInspectable Aug 18 '16 at 17:15
  • 2
    @IInspectable: It is completely OK to ask two questions. What is your problem? – Elmue Jul 04 '20 at 23:12
  • @elm With a *Peer Pressure* badge and no *Informed* badge on your profile I will question that you understand how this site is meant to work. Maybe take the [tour]? – IInspectable Jul 04 '20 at 23:14
  • I have even seen people asking more than 2 questions here. I have no problem with that. If it is a problem for you, you don't need to answer them. – Elmue Jul 06 '20 at 22:53

1 Answers1

4

The safest way that I know of is to loop through each section and find the section to be loaded last in memory (i.e. the highest address). You can almost always assume this is the last section and just skip directly to that section if you trust your PE file (such as if you are using a standard linker, etc). You begin with calculating the end-of-data pointer of that section as follows:

pEndOfLastSection = pLastSection->VirtualAddress + pLastSection->Misc.VirtualSize + pOptionalHeader->ImageBase

pEndOfLastSection now represents the end of the actual section's data (as it exists in the file, padded to file alignment, but not padded to memory alignment) and doesn't include any padding the loader must add to ensure the section fits exactly within the granularity of the memory section alignment.

Despite the other fields that might seem to store the end of the section rounded up to the next nearest "memory" alignment, you must you must perform this calculation yourself on the pEndOfLastSection pointer. I wrote the following function which so far has worked for my purposes:

//
// peRoundUpToAlignment() - rounds dwValue up to nearest dwAlign
//
DWORD peRoundUpToAlignment(DWORD dwAlign, DWORD dwVal)
{
    if (dwAlign)
    {
        //do the rounding with bitwise operations...

        //create bit mask of bits to keep
        //  e.g. if section alignment is 0x1000                        1000000000000
        //       we want the following bitmask      11111111111111111111000000000000
        DWORD dwMask = ~(dwAlign-1);

        //round up by adding full alignment (dwAlign-1 since if already aligned we don't want anything to change),
        //  then mask off any lower bits
        dwVal = (dwVal + dwAlign-1) & dwMask;
    }

    return(dwVal);

} //peRoundUpToAlignment()

Now take your pEndOfLastSecion and pass it to the rounding function as follows:

//NOTE: we are rounding to memory section alignment, not file
pEndOfLastSectionMem = peRoundUpToAlignment(pOptionalHeader->SectionAlignment,pEndOfLastSection)

Now you have a "simulated" pointer to the end of the PE file as it would be loaded in memory. NOTE: the end pointers calculated above actually point 1 byte past the last byte of the last section; this allows you to subtract them from their base to get the size. Once you have the end pointer of the last section as it would be loaded in memory, you can subtract this from the loader base and get the size of the PE file as it should be loaded in memory, and this size is obviously the same regardless of where the loader might relocate the PE file:

uCalcSizeOfFile = pEndOfLastSectionMem - pOptionalHeader->ImageBase

Unless the image has been tampered with, the calculation of uCalcSizeOfFile above should be equal to the pOptionalHeader->SizeOfImage field.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
byteptr
  • 1,275
  • 11
  • 15