Как вычисляется 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.