为什么段虚拟地址需要连续?

我目前正在做一些工作,需要我从PE格式的可执行文件中删除部分。 首先,我刚刚删除了IMAGE_SECTION_HEADER ,更改了IMAGE_FILE_HEADER中的 NumberOfSections字段,重新计算了IMAGE_OPTIONAL_HEADER中的 SizeOfImage / SizeOfHeaders,并将以下部分的原始地址移动了已删除部分的原始大小。 但是,Windows拒绝加载错误消息“XXX不是有效的Win32应用程序”的文件。 我一直在挣扎一会儿,但也试图通过删除部分的虚拟大小来移动以下部分的虚拟地址,一切工作正常。

什么是虚拟地址需要连续的原因,不能有任何差距? 我试图通过正式的体育文件阅读,但没有成功。 我一直认为,只要SizeOfImage具有正确的值,那么每个部分的地址是什么都没有关系。

大多数应用程序不需要在部分之间有很大的差距 – 那些可以使用单独的DLL或通过VirtualAlloc动态分配内存。

更新:经过更多的测试后,我发现部分之间的差距必须是对齐下一部分以满足SectionAlignment所需的。 因此,将两个4096字节的部分分隔开0x20000字节需要将整个exe的SectionAlignment字段设置为0x20000。 (例如0x12000的间隔是不可能的。)这个128kB的间隔不会出现在进程的内存映射中,并且不会消耗内存,但是如果您尝试在间隙内分配内存,则VirtualAlloc仍然会以ERROR_INVALID_ADDRESS失败。

这是一个GCC / MinGW的最小测试用例,它演示了Win32 exe的部分必须是连续的

测试用例:

 .global _main .section .text _main: push $1 call _Sleep@4 xor %eax, %eax ret .section .bss .lcomm buf, NUMBYTES 

这产生一个有效的可执行文件

gcc -m32 -Wl,--image-base=0x00400000 -Wl,-Ttext=0x00401000 -Wl,-Tbss=0x00402000 -DNUMBYTES=0xfe000 -Wl,--section-start=.idata=0x00500000 -s -nostartfiles -o contiguous.exe testcase.S

这会产生无效的可执行文件(gap.exe不是有效的Win32应用程序):

gcc -m32 -Wl,--image-base=0x00400000 -Wl,-Ttext=0x00401000 -Wl,-Tbss=0x00402000 -DNUMBYTES=0x200 -Wl,--section-start=.idata=0x00500000 -s -nostartfiles -o gap.exe testcase.S

与十六进制编辑器和objdump相比,两个文件之间只有16个字节的差异。 改变的是:

  1. 时间戳(4字节更改)。
  2. 校验和(4字节更改)。
  3. .bss部分的大小(8字节更改)。 在contiguous.exe中,.bss部分的大小是0xfe000字节,而在gap.exe中,.bss部分的大小是0x200字节,它与.idata部分之间创建的大小为0xfc000个字节。