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_FILE_HEADER,获取
NumberOfSections
字段。
- 完全跳过(跳过)IMAGE_NT_HEADERS 结构:无论其类型是什么,它的大小都是常数。
- 您将落在
IMAGE_SECTION_HEADER
(pinvoke ; msdn) 的数组中。此数组中的条目数是 NumberOfSections
字段(来自 IMAGE_FILE_HEADER)。
- 找到
IMAGE_SECTION_HEADER
数组中所有PointerToRawData
字段的最大/最高值(这通常是数组中的最后一个条目,但您应该检查数组中的所有条目以确保).
- 您现在有了最后一节开头的文件偏移量。
- 从
PointerToRawData
最高的IMAGE_SECTION_HEADER
中得到SizeOfRawData
字段。
- 你现在有最后一节的大小
- 同时添加
PointerToRawData
和 SizeOfRawData
字段:如果结果小于文件大小,您就会知道 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(上述计算的结果),则文件末尾会有一个负载。
可移植可执行文件即使在数据附加到它们之后仍然有效。据我所知,这是由于可执行代码的偏移量+大小性质所致。
我有一个类似于 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_FILE_HEADER,获取
NumberOfSections
字段。 - 完全跳过(跳过)IMAGE_NT_HEADERS 结构:无论其类型是什么,它的大小都是常数。
- 您将落在
IMAGE_SECTION_HEADER
(pinvoke ; msdn) 的数组中。此数组中的条目数是NumberOfSections
字段(来自 IMAGE_FILE_HEADER)。 - 找到
IMAGE_SECTION_HEADER
数组中所有PointerToRawData
字段的最大/最高值(这通常是数组中的最后一个条目,但您应该检查数组中的所有条目以确保).- 您现在有了最后一节开头的文件偏移量。
- 从
PointerToRawData
最高的IMAGE_SECTION_HEADER
中得到SizeOfRawData
字段。- 你现在有最后一节的大小
- 同时添加
PointerToRawData
和SizeOfRawData
字段:如果结果小于文件大小,您就会知道 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(上述计算的结果),则文件末尾会有一个负载。