2

PE 可选标头中的 SizeOfImage 是如何计算的?

尝试学习 PE 格式时,我遇到SizeOfImage了可选标题中的字段。

引用文档:

图像在内存中加载时的大小(以字节为单位),包括所有标头。它必须是 SectionAlignment 的倍数。

但是,我经历过,如果我错误地设置了这个字段,那么可执行文件将不会运行,并且error 193会显示一个(格式错误的可执行文件):

在此处输入图像描述

我如何计算该SizeOfImage字段,如果设置错误,为什么可执行文件不会运行(例如,如果设置为 0x00003000 但不是 0x00004000 或 0x00002000,则可执行文件运行)?

4

1 回答 1

3

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

于 2016-09-02T01:49:57.287 回答