如何为Windows PE 32位可执行文件中的代码留出空间

问题描述 投票:5回答:1

因此,我想在minesweeper.exe(典型的Windows XP minesweeper游戏,链接:Minesweeper)中为我的代码洞穴腾出空间。因此,我通过CFF Explorer修改了文件的PE标头,以增加.text部分的大小。

Segs

我曾尝试将.text段的原始大小增加1000h(新大小为3B58),但是Windows无法找到入口点,因此游戏无法启动。然后,我尝试增加.rsrc部分的大小,添加一个新部分,增加图像大小,但是这些尝试均未成功,Windows表示“这不是x32可执行文件”。

所以这是一个问题:如何为我的代码留出空间?我不想搜索编译器留下的空白空间,我想为我的代码准备好干净的1000h字节。很好的教程以及如何在不破坏游戏的情况下做到这一点的详细解释将是非常棒的! (是的,我实际上是在扫雷)

windows assembly x86 portable-executable
1个回答
12
投票

您不能在不使以下部分无效的情况下增加部分的大小(通常是因为这会使那些部分中的偏移量和地址无效)。这仍然有可能,但是它极易出错,如果您有一个简单的解决方案,就不值得为此烦恼。

通常,您只需要在PE的末尾部分添加一个代码段即可。通常在代码部分(代码洞穴)的末尾有一些空间,因此您可以在其中放置JMP(或一些代码存根)以重定向到新部分。您还可以添加其他新节来获取数据或新资源,或添加任何所需的内容。


注意:我使用的是两种工具:CFF Explorer作为PE浏览器;十六进制编辑器。

此文件非常特殊,因此添加新部分比平时要难一些。

开始吧!

下面是IMAGE_SECTION_HEADER数组的十六进制视图:

Section headers

通常,有一定的空间可以添加新的节,但是在这种情况下,就没有了...。最后一节的标题后面紧跟着一些内容。

根据内容判断,这可能是一个绑定的导入目录,已在CFF资源管理器中确认(绑定目录的偏移量为0x248:]

Bound import directory

今天,绑定导入目录已无用,尤其是对于ASLR,因此我们可以将整个目录归零(如上一个屏幕快照所示,其大小为0xA8字节:]

Zeroed bound import directory

您也可以将数据目录中的绑定导入目录RVA归零,尽管并非严格要求:

enter image description here

现在是时候添加新部分了。

添加新部分

Minesweeper默认附带3个部分,因此将部分数从3增加到4:

Increment number of sections

转到部分标题并添加一个新部分(您可以直接在CFF资源管理器中进行操作;我将其命名为.foobar,请注意,部分名称最多为8个字符,并且不需要以NULL字节结尾):New section

您需要选择两个数字:

  • 新部分的原始大小(我选择了0x400);

    必须

    FileAlignment的倍数(在这种情况下为0x200)。
  • 新部分的虚拟大小(我选择了0x1000);它

    必须

  • SectionAlignement的倍数(此二进制文件为0x1000)。现在我们需要计算另外两个成员Virtual AddressRaw Address

虚拟地址

以第一部分和第二部分为例。

第一部分从虚拟地址0x1000开始,虚拟大小为0x3A56。下一部分虚拟地址

必须

SectionAlignement(0x1000)上对齐,因此计算是(在此处使用python):
>>> def round_up_multiple_of(number, multiple): num = number + (multiple - 1) return num - (num % multiple) >>> hex(round_up_multiple_of(0x1000 + 0x3a56, 0x1000)) '0x5000'
哪个给出正确的0x5000(.data节从虚拟地址0x5000开始)。

现在,上一节应该从哪里开始?

。rsrc节从0x6000开始,大小为0x19160:

>>> hex(round_up_multiple_of(0x6000 + 0x19160, 0x1000)) '0x20000'

因此,它必须从虚拟地址0x20000开始。将该数字放入Virtual Address

原始地址

(通常不需要这样做,因为所有节都已经对齐,最后一节必须从文件末尾开始,但是我们会这样做)。

提醒一下,原始地址是文件中的地址(而不是内存中的地址。

让我们从一个例子开始(第一和第二部分):

第一部分原始地址为0x400,原始大小为0x3c00。 FileAlignement为0x200,因此:

>>> hex(round_up_multiple_of(0x400 + 0x3c00, 0x200)) '0x4000'

第二部分应从文件(其Raw address)的0x4000开始,是正确的。

因此,对于我们的新部分,计算为:

    。rsrc部分从文件的0x4200开始
  • 。rsrc文件的节大小为0x19200
  • FileAligment是0x200
  • 计算如下:

>>> hex(round_up_multiple_of(0x4200 + 0x19200, 0x200)) '0x1d400'

我们的最后一部分从文件的原始地址0x1d400开始,该文件已通过十六进制编辑器确认:

Last section location

最终步骤

需要执行最后一步,计算Optional标头中的SizeOfImage字段。根据PE规范,该字段为:

作为图像的图像的大小(以字节为单位),包括所有标题已加载到内存中。它必须是SectionAlignment的倍数。

因此,计算可以简化为:最后部分的VirtualAddress + VirtualSize,对齐到SectionAlignment(0x1000):

>>> hex(round_up_multiple_of(0x20000 + 0x1000, 0x1000)) '0x21000'

New size of image

现在,将所有修改保存在CFF资源管理器中并退出。

为新部分添加空间

最后一步是为最后一节添加所需的字节。当我选择Raw size为0x400时,我使用十六进制编辑器在Raw Address(0x1d400)处插入了0x400字节。

保存文件。如果按照所有步骤操作,它必须按原样运行(在Win 10上测试),并且可以启动已修改的可执行文件,而不会出现任何错误。

如果0x400不够,请尝试使用不同的原始尺寸的新部分。

现在您有了一个新的空白部分,其余的由您决定修改代码:)

© www.soinside.com 2019 - 2024. All rights reserved.