我所知道的最安全的方法是遍历每个部分并找到要在内存中最后加载的部分(即最高地址)。您几乎总是可以假设这是最后一部分,如果您信任您的 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
字段。