如何计算PE可选标头中的SizeOfImage?
尝试学习PE格式,我在可选标题中遇到SizeOfImage
字段。
引用文档:
图像的大小(以字节为单位),包括所有标题,作为图像 被加载到内存中。它必须是SectionAlignment的倍数。
但是,我经历过,如果我错误地设置了这个字段,那么可执行文件将不会运行,并显示error 193
(格式错误的可删除):
如何计算SizeOfImage
字段,如果设置错误,为什么不会运行可执行文件(例如,如果设置为0x00003000而不是0x00004000或0x00002000,则可执行文件会运行)?
答案 0 :(得分: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字段。