Как вычисляется SizeOfImage в необязательном заголовке PE?

Как вычисляется SizeOfImage в необязательном заголовке PE?

Пытаясь выучить формат PE, я наткнулся на SizeOfImage поле в необязательном заголовке.

Цитировать документацию:

Размер (в байтах) изображения, включая все заголовки, поскольку изображение загружается в память. Он должен быть кратным SectionAlignment.

Тем не менее, я столкнулся с тем, что если я неправильно установил это поле, то исполняемый файл не запустится и error 193 (плохо отформатированный исполняемый файл) отображается:

Как мне вычислить SizeOfImage поле, и почему исполняемый файл не запускается, если его значение установлено неверно (например, исполняемый файл запускается, если для него установлено значение 0x00003000, но не 0x00004000 или 0x00002000)?

1 ответ

Самый безопасный способ, который я знаю, - это циклически проходить по каждому разделу и находить раздел, который будет загружен последним в памяти (т. Е. Самый высокий адрес). Вы можете почти всегда предполагать, что это последний раздел, и просто перейти непосредственно к этому разделу, если вы доверяете своему файлу PE (например, используете ли вы стандартный компоновщик и т. Д.). Вы начинаете с вычисления указателя конца данных этого раздела следующим образом:

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

pEndOfLastSection теперь представляет конец фактических данных раздела (так как он существует в файле, дополнен выравниванием файла, но не дополнен выравниванием памяти) и не включает никаких отступов, которые должен добавить загрузчик, чтобы гарантировать, что раздел точно вписывается в гранулярность выравнивания раздела памяти.

Несмотря на то, что другие поля могут показывать конец раздела, округленного до следующего ближайшего выравнивания "память", вы должны выполнить этот расчет самостоятельно над указателем pEndOfLastSection. Я написал следующую функцию, которая работает в моих целях:

//
// 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()

Теперь возьмите ваш pEndOfLastSecion и передайте его функции округления следующим образом:

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

Теперь у вас есть "имитированный" указатель на конец PE-файла, который будет загружен в память. ПРИМЕЧАНИЕ: конечные указатели, вычисленные выше, на самом деле указывают на 1 байт после последнего байта последнего раздела; это позволяет вычесть их из базы, чтобы получить размер. Если у вас есть указатель конца последнего раздела, как он будет загружен в память, вы можете вычесть его из базы загрузчика и получить размер PE-файла так, как он должен быть загружен в память, и этот размер, очевидно, один и тот же, независимо от того, где загрузчик может переместить PE-файл:

uCalcSizeOfFile = pEndOfLastSectionMem - pOptionalHeader->ImageBase

Если изображение не было изменено, вычисленный выше uCalcSizeOfFile должен быть равен полю pOptionalHeader->SizeOfImage.

Другие вопросы по тегам