PE格式自检

Self inspection of the PE format

可移植可执行文件即使在数据附加到它们之后仍然有效。据我所知,这是由于可执行代码的偏移量+大小性质所致。

我有一个类似于 Winzip 的 Zip2Exe 的可执行打包应用程序。到目前为止,我一直在将压缩的有效负载添加到可执行存根的末尾,后跟一个表示有效负载字节大小的 8 字节值。

我想做的是让可执行存根计算自己的大小,并在没有任何大小信息的情况下将剩余字节假定为有效负载。这纯粹是出于好奇,我真正想知道的是 PE 是否可以计算自己的大小并检测是否添加了任何内容。

当然,我知道如果内容被注入或其他方式更改,它无法验证它自己的内容。

为了澄清这个问题,我想要 .NET 中的一个函数来检查 Assembly.GetExecutingAssembly().Location 并计算出附加负载的偏移量可能是多少。如果此偏移量与文件大小相同,则不会有有效载荷。

请注意,我对 C# 不是很满意,所以这是一个笼统的回答。

由于 Assembly.GetExecutingAssembly().Location 似乎是 return 图像路径,因此您有一个 "flat" 文件路径,即磁盘上的图像而不是内存中的图像。解析比在内存中更容易,而且您已经知道它是一个有效的 PE 文件,因此您可以跳过所有有效性检查。

由于您将使用 C# 浏览图像文件,因此您可能需要选中“Exploring pe file headers using managed code" and the various structures on Pinvoke.net.

查找图像文件末尾是否有负载:

  • 打开文件:)
  • 获取文件大小
  • 文件以 IMAGE_DOS_HEADER (pinvoke) 开头
    • 关注 e_lfanew 成员找到 IMAGE_NT_HEADERS
  • IMAGE_NT_HEADERS(pinvoke ; msdn)有两种:IMAGE_NT_HEADERS32(PE32:32位PE)和IMAGE_NT_HEADERS64(PE32+:64位聚乙烯)
    • 在IMAGE_NT_HEADERS中,SignatureFileHeader(IMAGE_FILE_HEADER结构)字段是相同的,只有OptionalHeader会改变。
    • 检查 IMAGE_FILE_HEADER (pinvoke ; msdn) 中的 Machine 以查看您的类型:0x014c (Intel 386) = PE32 ; 0x8664 = PE32+
  • 从IMAGE_FILE_HEADER,获取NumberOfSections字段。
  • 完全跳过(跳过)IMAGE_NT_HEADERS 结构:无论其类型是什么,它的大小都是常数。
  • 您将落在 IMAGE_SECTION_HEADER (pinvoke ; msdn) 的数组中。此数组中的条目数是 NumberOfSections 字段(来自 IMAGE_FILE_HEADER)。
  • 找到IMAGE_SECTION_HEADER数组中所有PointerToRawData字段的最大/最高值(这通常是数组中的最后一个条目,但您应该检查数组中的所有条目以确保).
    • 您现在有了最后一节开头的文件偏移量。
  • PointerToRawData最高的IMAGE_SECTION_HEADER中得到SizeOfRawData字段。
    • 你现在有最后一节的大小
  • 同时添加 PointerToRawDataSizeOfRawData 字段:如果结果小于文件大小,您就会知道 PE 文件末尾有有效负载。

简单(视觉)示例:

上图代表calc.exe中的所有IMAGE_SECTION_HEADER。如您所见,PointerToRawData(标记为 "Raw address') is 0xE3A00, and the corresponding section size (SizeOfRawData, labeled "原始大小”)的最大值为 0x600。

  • 0xE3A00 + 0x600 = 0xe4000 = 933888

calc.exe的文件大小相同(933888字节):

因此,文件末尾没有负载。如果文件大小大于 933888(上述计算的结果),则文件末尾会有一个负载。