Python:向 PE 文件添加分区

逆向工程 聚乙烯 Python 包装工
2021-06-28 13:19:21

我正在使用 Python 和 pefile 库处理 PE 二进制文件。它适用于从二进制文件中读取信息并重写某些字节。如果我想在文件中添加一个部分,那么我不知道如何去做。如果有人可以向我提供此主题的任何先前作品或有关此方面的帮助,我将不胜感激。

3个回答

这不是特定于语言的概念。您需要修改(或打算)引用的值:

!IMAGE_FILE_HEADER: - NumberOfSections

!IMAGE_OPTIONAL_HEADER: - SizeOfImage - SizeOfHeaders(可选,取决于您正在修改的部分。与 .text、.data 和 .idata 相关,但 afaik 未使用) - SizeOfCode、SizeOfInitializedData 或 SizeOfUninitializedData - BaseOfCode、BaseOfData

然后你必须构造一个新的 IMAGE_SECTION_HEADER,一些值依赖于图像。- VirtualAddress 和 VirtualSize 必须与 !IMAGE_OPTIONAL_HEADER.SectionAlignment 对齐 - PointerToRawData 和 SizeOfRawData 必须与 !IMAGE_OPTIONAL_HEADER.FileAlignment 对齐 - PointerToRelocations 有点棘手。如果!IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]末尾没有足够的空间,可以通过找到它所包含的section的大小,减去::.Size和(::.VirtualAddress - Reloc Section Virtual地址),您必须重新定位表。之后,您只需要调整大小并追加即可。

例子

附加数据部分: - 检查标题中是否有足够的空间用于另一部分 - 添加

(IMAGE_SECTION_HEADER){ 
.VirtualSize = align_up(%SIZE%, !IMAGE_OPTIONAL_HEADER.SectionAlignment,
.SizeOfRawData = align_up(%SIZE%, !IMAGE_OPTIONAL_HEADER.FileAlignment),
.VirtualAddress = find_max(!IMAGE_SECTION_HEADER[].VirtualAddress + !IMAGE_SECTION_HEADER[].VirtualSize),
.PointerToRawData = find_max(!IMAGE_SECTION_HEADER[].PointerToRawData + !IMAGE_SECTION_HEADER[].SizeOfRawData),
.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
}

然后调整文件大小,将您的数据复制到 .PointeToRawData

添加新的 PE 部分是我在大量修改 PE 文件时采用的主要方法。现有二进制文件中存在位置和 RVA 以及特定于 VA 的依赖项。更改这些内容是具有挑战性的,尤其是在基重定位表由于任何原因丢失或损坏的情况下。

让我们来看看我在Windows PE Tools for PHP 中采用的一般方法(它是 PHP,但对于 Python 的类似实现/方法应该是可能的):

https://github.com/cubiclesoft/php-winpefile/blob/master/support/win_pe_file.php#L2222

CreateNewPESection()函数首先调用PrepareForNewPESection(). PrepareForNewPESection()检查标题中是否有足够的空间用于新部分。如果有足够的空间,那么它就会返回。请注意,标头中可能存在各种奇怪的内容,例如绑定导入、调试目录数据、重定位等。 PrepareForNewPESection()如果在标头空间中遇到此类数据,则会通过减少该值来计算实际可用空间量。

如果没有足够的空间,PrepareForNewPESection()将通过最小化 DOS 存根和擦除标题中不需要的信息来尝试腾出空间来调整标题的大小并将数据向上移动到 PE 部分对齐。一个新的部分只需要 40 个字节。调整标头大小是一项可选且复杂的练习,只是为了获得几个字节的存储空间。

假设有足够的空间,CreateNewPESection()然后通过遍历这些部分并计算该部分的最大 RVA 值来计算下一个可用的 RVA。请注意,部分中的 RVA 大小实际上可能小于原始数据大小。在这些情况下,Windows 加载程序似乎假定 RVA 大小等于部分对齐的原始数据大小。我认为那些碰巧可以工作的损坏的 PE 文件。然后将新部分文件对齐(通常是无操作),并将该部分的新数据(也是文件对齐)放置在文件末尾。

添加PE部分后,不要忘记更新各种值:PE头中的部分数量以及代码大小,初始化数据大小,未初始化数据大小,最重要的是PE中的图像大小可选标头(当然还有 PE 校验和,当一切都说完后)。

除了添加另一个部分,附加到最后一个部分也可能是一个值得考虑的选项。首先,查看该部分的标志。如果标志与您要查找的内容匹配,并且最后一部分也是最后一个 RVA 并且位于物理文件的末尾,则该部分可能是重用的候选,只需调整现有部分的大小而不是添加新部分。这样做有助于减少新部分标题中空间不足的可能性。