我编写了一个可移植可执行(PE)库,该库还提供查找覆盖的起始偏移量(将附加数据添加到未映射到内存的PE中)。到目前为止:

public long getOverlayOffset() throws IOException {
        if (offset == null) {
            SectionTable table = data.getSectionTable();
            offset = 0L;
            for (SectionTableEntry section : table.getSectionEntries()) {
                long pointerToRaw = section.get(POINTER_TO_RAW_DATA);
                long sizeOfRaw = section.get(SIZE_OF_RAW_DATA);
                long virtSize = section.get(VIRTUAL_SIZE);
                //see https://code.google.com/p/corkami/wiki/PE#section_table: "if bigger than virtual size, then virtual size is taken. "
                //and: "a section can have a null VirtualSize: in this case, only the SizeOfRawData is taken into consideration. "
                if(virtSize != 0 && sizeOfRaw > virtSize) { 
                    sizeOfRaw = virtSize;
                }
                long endPoint = pointerToRaw + sizeOfRaw;
                if (offset < endPoint) {
                    offset = endPoint;
                }
            }
        }
        if(offset > file.length()) {
            offset = file.length();
        }
        return offset;
    }


我使用corkami作为源,以了解计算叠加偏移的一些可能性。我不仅希望它坚固而且准确。
我错过了什么吗?我还需要考虑什么?

注意:这里有一个类似的问题:如何提取便携式可执行文件的附加数据?到目前为止,可靠的算法。据我了解,使用工具就足以解决这个问题。

评论

您是否关心代码签名(如果有)?这是在其中一个目录中指出的,但实际上是像覆盖图一样附加的。

@ 0xC0000022L好的问题。如果不在某节中,则将其定义为叠加。

#1 楼

您似乎缺少了一些极端情况,例如未对齐的指针(应向下舍入)和大小(应向上舍入)。不管标头中的值如何,都可以减小到512的倍数,但是通过使用文件对齐和4kb的组合来将读取大小四舍五入。不管标头中的值如何,虚拟大小总是四舍五入为4kb的倍数。

数字签名必须是叠加的。出于安全原因,这是Windows现在强制执行的检查。如果没有覆盖,则文件将不会加载。

您需要类似的内容(并且filealign来自PE标头): />然后,您将“ alignedpointerToRaw”作为起始位置,并将“ readsize”作为该节中的字节数。对它们求和以找到本节的结尾。您需要对所有部分执行此计算(因为物理数据在文件中可能不是顺序的)。最大的和是图像的结尾。除此之外的任何东西都可以叠加。

评论


关于alignedPointerToRaw:规范指出,它应该是fileAlignment的倍数。 fileAlignment的默认值为512,但是假设它未设置为默认值。该部分从pointerToRaw和〜0x1ff开始是否仍然正确?

– Karsten Hahn
2014年5月14日7:00

关于fileAlignment的另一个问题:我是否应该将fileAlignment改正为:“该值应为512到64 K(含)之间的2的幂。” (pecoff规格)?非常感谢你的帮助。

– Karsten Hahn
2014年5月14日7:23



无论标头中的值如何,对齐PointerToRaw为512。真。对于fileAlignment的可能值,它是2的幂,最大为64kb,但包括1(即2 ^^ 0)。

–彼得·弗里
2014年5月14日15:40

如果不是,我应该将文件对齐方式向上/向下取整为2的幂吗?

– Karsten Hahn
2014年5月15日7:35



如果文件不是2的幂,则文件将不会加载。您无需担心这种情况。

–彼得·弗里
2014年5月15日15:38

#2 楼

为了完整起见:这是我根据Peter Ferrie的建议编写的代码



/**
 * Calculates the beginning of the overlay
 * 
 * @return the file offset to the beginning of overlay
 */
public long getOverlayOffset() {
    SectionTable table = data.getSectionTable();
    OptionalHeader opt = data.getOptionalHeader();
    offset = 0L;
    List<SectionHeader> headers = table.getSectionHeaders();
    if(headers.size() == 0) {
        offset = file.length(); //offset for sectionless PE's
    }
    for (SectionTableEntry section : table.getSectionEntries()) {
        long alignedPointerToRaw = section.get(POINTER_TO_RAW_DATA)
                & ~0x1ff;
        long endPoint = getReadSize(section) + alignedPointerToRaw;
        if (offset < endPoint) {
            offset = endPoint;
        }
    }
    if (offset > file.length()) {
        offset = file.length();
    }
    return offset;
}

/**
 * Determines the the number of bytes that is read for the section.
 * 
 * @param section
 * @return section bytes that are read
 */
private long getReadSize(SectionTableEntry section) {
    long pointerToRaw = section.get(POINTER_TO_RAW_DATA);
    long virtSize = section.get(VIRTUAL_SIZE);
    long sizeOfRaw = section.get(SIZE_OF_RAW_DATA);
    long fileAlign = data.getOptionalHeader().get(
            WindowsEntryKey.FILE_ALIGNMENT);
    long alignedPointerToRaw = section.get(POINTER_TO_RAW_DATA) & ~0x1ff;
    long readSize = alignedUp(pointerToRaw + sizeOfRaw, fileAlign)
            - alignedPointerToRaw;
    readSize = Math.min(readSize,
            alignedUp(section.get(SIZE_OF_RAW_DATA), 0x1000));
    if (virtSize != 0) {
        readSize = Math.min(readSize,
                alignedUp(section.get(VIRTUAL_SIZE), 0x1000));
    }
    return readSize;
}

/**
 * Returns the value rounded up to a multiple of alignTo.
 * 
 * @param value
 * @param alignTo
 * @return value rounded up to a multiple of alignTo
 */
private long alignedUp(long value, long alignTo) {
    if (value % alignTo != 0) {
        value = (value + alignTo - 1) & ~(alignTo - 1);
    }
    return value;
}


评论


常数0x1000是SectionAlignment的值吗?根据MSDN,这是默认设置-但由于提供了一个值,因此您可能仍要使用它。

–杂件
2014年5月14日14:31

@Jongware,始终为0x1000。尽管文档中有说明,但它和512(当前)在Windows中(当前)是硬编码的(请注意,这都是未记录的,因此理论上可能会改变一天)。标头中的节对齐用于内存分配,而不是文件内容。

–彼得·弗里
2014年5月14日15:42



@Veitch,看起来不错。

–彼得·弗里
2014年5月14日15:52

#3 楼

您是否考虑过使用Microsoft的代码来做到这一点?反转加载程序刚好足以找到加载图像后要查找的指针,阻止其执行除解析PE之外的任何实际代码。 ntdll或任何特定版本中所需指针的偏移量,但是您将获得超准确的解析器。

评论


解析是在内核模式下完成的,而不是在用户模式下完成的,您仍然缺乏实际的算法。不建议盲目执行可能会更改的代码(即使格式不变)。

–彼得·弗里
2014年5月23日15:45

我要求一种算法,因为我写了一个PE库。所以这不是一个选择。

– Karsten Hahn
2014年5月23日20:16

那么参考实现显然将无济于事。

–offbyseveral
2014年5月23日在21:49