【问题标题】:PE: Adding code at the end of .txt sectionPE:在 .txt 部分的末尾添加代码
【发布时间】:2017-07-05 13:23:03
【问题描述】:

据我了解,PE 文件中的 Virtual Size 显示了它在加载期间为部分分配了多少空间,而 Raw Size 显示了该部分在磁盘上的大小。

我遇到了这个可执行文件,它执行了以下操作:

它从原始数据大小 (offset 0x10) 中减去虚拟大小 (offset 0x8) 并确保有一些空间(例如 100 字节)。在文本节标题的偏移量0x14 处,它在文件中找到了该节本身的偏移量。它添加了虚拟大小,找到了文件中该部分的结束位置。它复制了一些 shellcode(最终跳转到可执行文件的原始入口点以确保原始可执行文件运行)到二进制文件文本部分的末尾。

现在我有点困惑,如果虚拟大小显示将分配给可执行文件的确切空间,是否不会在.txt 部分的末尾添加代码覆盖可执行文件的其他一些数据并使其崩溃?谢谢。

【问题讨论】:

    标签: c++ c windows exe portable-executable


    【解决方案1】:

    这是一个很好的问题,它说明了关于 Windows 加载程序如何计算内存大小的一个非常重要的观点(或者您可能会说怪异)。

    PE/COFF 规范确实将 VirtualSize 描述为“加载到内存时部分的总大小”。如果您认为 total 是组成该部分的 REAL 数据的总量,这在技术上是正确的,但它不是 Windows 为该部分分配的内存总量。您会发现 VirtualSize 通常小于 Windows 为内存中的部分分配的数量,因为 VirtualSize 必须向上舍入到最接近的内存对齐值(在 PE 映像中设置)。

    换句话说,VirtualSize 表示未四舍五入的部分大小,而 SizeOfRawData 是图像文件中数据的大小,但四舍五入到最接近的文件对齐填充值。这就是为什么 VirtualSize 可以更好地表示数据在内存或磁盘上的真实“原始”大小。 PE/COFF 规范没有做出这种区分。为什么一个在图像文件中是四舍五入的,而不是另一个可能有其古老的向后兼容性的根源。

    这正是您的 shellcode 使用 VirtualSize 来查找数据的“真实”结尾的原因,即使它驻留在图像文件中。毫不奇怪,您可以通过将 VirtualSize 向上舍入到文件对齐值来计算 SizeOfRawData,至少在格式良好的 PE 文件中是这样。

    shellcode 只是使用 VirtualSize 来查找 REAL 代码的结尾。在那里和 SizeOfRawData 字节之间,只是未使用的填充零,使其成为添加新代码而不影响文件大小或破坏 PE 文件中的寻址偏移量的主要位置。

    总而言之,Windows 加载程序本质上采用 VirtualSize 值并将其向上舍入到内存对齐值以获得内存分配的实际大小(甚至可能向上舍入到最接近的 4k 最小内存页)。然后最多将 SizeOfRawData 字节从文件复制到内存部分的开头。如果小于内存段的大小,则余数为零。

    【讨论】:

    • VirtualSize represents the unrounded size of the section 这绝对是错误的。 VirtualSize - 这是为部分保留的内存量。 SizeOfRawData - 这是磁盘上有多少字节。例如部分 (.bss) 有未初始化的数据 - 在这种情况下 SizeOfRawData 将为 0 - 所以 VirtualSize > SizeOfRawData。从另一个尺寸,因为SizeOfRawData四舍五入到FileAligment(通常512字节)可以和VirtualSize < SizeOfRawData
    • 问题不在于 SizeOfRawData=0; 的未初始化数据部分。即使在那种情况下,VirtualSize 仍然是“内存中”部分的未舍入大小,也没有什么可以从磁盘复制。在几乎任何其他情况下,我所做的声明都是真正的实现细节。我同意它令人困惑并且看起来与规范相反,但该声明反映了 MS 链接器(以及复制其行为的链接器)是如何实现的。这也包括 GNU 和 Borland 链接器。另请注意,链接器可以舍入 VirtualSize 并且仍然有一个工作可执行文件。
    • 作为遗留行为的旁注,Borland TLINK 4.x(从 90 年代初)交换了 VirtualSize 和 SizeOfRawData
    【解决方案2】:

    嗯,这个黑客代码似乎是为了隐藏部分之间的恶意代码。

    回答您的问题,您是正确的 VirtualSize 是内存中真正分配的空间,而 RawSize 是磁盘上用于保存部分数据的空间。 您错过的是(来自 MS PECOFF 规范):

    VirtualSize:加载到内存中的部分的总大小。如果此值大于SizeOfRawData,则该部分是零填充的。该字段仅对可执行图像有效,对于目标文件应设置为零。

    这意味着如果SizeOfRawData-VirtualSize 的结果是肯定的,我们在磁盘上有一些可用空间实际上是用0 填充的

    如果该空间足以容纳恶意代码,然后在磁盘上添加文本部分的开头VirtualSize,您可以获得可用于复制代码的 0 填充区域的开头。

    剩下的就是故事了……

    【讨论】:

    • 谢谢您的回复:) 但是即使添加了代码,执行时也只会加载.txt 部分的VirtualSize 对吗?
    • 很难说是否对尺寸进行了任何控制。考虑到该部分本身将扩展到多个架构页面(PECOFF 规范),一些操作系统版本的加载器可以简单地复制整个块。 但无论如何,经过如此大的修改以更改 VirtualSize 值也没什么大不了的......
    • 所以你说VirtualSize 没什么大不了的,加载器会复制整个块,这意味着它将实际的.txt 部分扩展到架构页面。这是否意味着将被加载的.txt 部分大小将始终为RawDataSize?对不起,如果我误解了什么。
    • 我没这么说。我说过,在复制文件中的数据后,还要修改偏移量 0x8 处的 virtualSize 字段,添加冷复制数据的大小,以确保它会被加载。 但在这一点上,我真的认为我们已经谈论了太多非标准编码的内容,不应该讨论,因为这对不良做法很有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-15
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 2022-10-08
    相关资源
    最近更新 更多