提取PE重叠的可靠算法

逆向工程 聚乙烯
2021-06-17 08:10:26

我编写了一个便携式可执行 (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作为来源来了解计算叠加偏移的一些几率。我不仅希望它健壮,而且要准确。我错过了什么?我还需要考虑什么?

注意:这里有一个类似的问题:How can one extract the appended data of a Portable Executable? 但到目前为止,它还没有涵盖可靠的算法。据我了解,在这个问题上使用工具就足够了。

3个回答

您似乎缺少一些极端情况,例如未对齐的指针(应向下舍入)和大小(应向上舍入)。

然而,即使舍入也有极端情况——物理指针应该向下舍入到 512 的倍数,而不管头中的值如何,但读取大小是通过使用文件对齐和 4kb 的组合向上舍入的。无论标题中的值如何,虚拟大小始终向上舍入为 4kb 的倍数。

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

你需要这样的东西(filealign 来自 PE 标头):

        long pointerToRaw = section.get(POINTER_TO_RAW_DATA);
        long alignedpointerToRaw = pointerToRaw & ~0x1ff;
        long sizeOfRaw = section.get(SIZE_OF_RAW_DATA);
        long readsize = ((pointerToRaw + sizeOfRaw) + filealign - 1) & ~(filealign - 1)) - alignedpointerToRaw;
        readsize = min(readsize, (sizeOfRaw + 0xfff) & ~0xfff);
        long virtsize = section.get(VIRTUAL_SIZE);

        if (virtsize)
        {
            readsize = min(readsize, (virtsize + 0xfff) & ~0xfff);
        }

然后将“alignedpointerToRaw”作为起始位置,将“readsize”作为该部分中的字节数。将这些相加以找到该部分的结尾。您需要对所有部分执行此计算(因为文件中的物理数据可能不是连续的)。最大的总和是图像的结尾。除此之外的任何东西都是叠加的。

为了完整起见:这是我根据 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;
}

你考虑过使用微软的代码来做吗?反转加载器以在加载图像后找到您要查找的指针,阻止它执行除解析 PE 之外的任何实际代码。您在 ntdll 或任何地方的所需指针的偏移量将是特定于版本的,但您将获得一个超级准确的解析器。