深入理解PE结构之节表Section(VirtualSize和SizeofRawData)
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //8个字节,可以修改 union { DWORD PhysicalAddress; DWORD VirtualSize; //说明信息,可以修改,但是不能乱改,修改范围得根据文件复制的大小,按照内存对齐大小改,比如:如果从文件复制的大小为0x1500,那么VirtualSize大小可以随便修改为1--0x2000的值,超过这个范围,SizeofImage、下一个节区内存的开头位置等其他属性都要改 } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
VirtualSize修改问题:
按照VirtualSize上边的修改范围:0x200映射到内存为0x1000,因此VirtualSize可随便修改为1--0x1000,我这里修改为0x0FFF,照样运行。
DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers;
这几个字段是为别的平台准备的。
节表记忆的方法:先是节表的名字
然后文件在内存的属性:内存的起始偏移位置和大小
文件在磁盘中的属性:文件的起始偏移位置和大小
节区的属性:0xC0 00 00 40, 主要是看C这个位置,C这个位置的记忆用WRES(写、读、执行、共享权限),如果想把节区改为拥有所有的权限,那么根据WRES,那么把C变成F就行了,就成0xF0 00 00 40.
节表的理解:
从文件中0x200(PointerToRawData)的位置,按照0x200(SizeofRawData)大小复制到内存0x1000(VirtualAddress)位置,复制之后的有效数据是0x26(VirtualSize)。
而SizeofRawData是对齐后的大小,VirtualSize不是对齐的大小。
因此可以看出来:VirtualSize 正常情况下是小于SizeofRawData的。
有时候VirtualSize大于SizeOfRawData,有时候VirtualSize小于SizeOfRawData。
正常情况下是 VirtualSize 小于 SizeOfRawData
不正常情况:VirtualSize 大于 SizeOfRawData
那么这2个值的大小关系如何理解呢?
MSDN的解释如下:
VirtualSize:当文件加载到内存后这个节的大小,如果VirtualSize比SizeOfRawData大,那么SizeOfRawData小尺寸变成大尺寸的填充内容,是用0填充。只有exe镜像文件这个值才是有效的,对于obj文件这个值为0.
SizeOfRawData:在obj文件中,指的是这个节的大小。对于在磁盘中的镜像文件指的是已初始化的数据。对于exe镜像文件,这个值必须是文件对齐的整数倍。如果SizeOfRawData小于 VirtualSize,那么这个节对齐的时候,需要用0填充。
由于SizeOfRawData是表示该节文件对齐后的大小,而VirtualSize是当文件加载到内存后的大小,这个大小不是对齐后的大小,因此就会出现SizeOfRawData比VirtualSize大这种情况。当一个节中只包含未初始化的数据时候,这个值应该为0。
VirtualSize < SizeOfRawData的情况,为正常情况;因为VirtualSize是说明字段,表示有效数据,SizeOfRawData是文件对齐后的大小。
VirtualSize > SizeOfRawData的情况,为不正常情况,不正常情况如何理解?这种情况叫变形的未初始化区。
先看一下一种特殊的:
(1)无文件映射的节
文件中PointerToRawData、SizeOfRawData都为0,但是内存映射是存在的?
这个区表示只占内存空间,没有文件映射,就从内存0x3000的位置,大小为0x1000(内存对齐的空间),这个时候,这个区在内存中就相当于一个未初始化数据的全局变量区。
未初始化的数据不用在文件中记录初值,只描述未初始化的数据在内存中什么地方开始,占多大的空间。这就是未初始化的数据区,对应高级语言中未初始化数据的全局变量区.data区,以及汇编语言中的.data?区
对于这个区,os先检测文件映射,发现文件映射为0,那么就以内存映射大小为参考,以VirtualSize内存对齐后的大小,为其准备空间,空间位置在VirtualAddress。
例子:使用vs生成代码的debug版本程序就有一个.txtbss的无文件映射的节
我们使用od观察内存。 .textbss会映射到内存0x00401000的位置,大小为10000.
(2)无文件映射的节的变种
VirtualSize > SizeOfRawData 就是这种变形的未初始化区,某些链接器会使用这种。
例如:SizeOfRawData = 200,VirtualSize为0x2000
如果一个程序中定义了未初始化数据区,并且节表中每一个节都是VirtualSize < SizeOfRawData,但是有那么1个节表中的VirtualSize远远大于SizeOfRawData,
例如:有200个字节是有映射的,0x2000 - 0x200 = 0x1E00的空间是没有文件映射的,0x1E00这多出来的空间就被链接器就不用专门定义1个节来存储未初始化的全局变量,就把这个多余的0x1E00作为未初始化全局变量的空间使用。这样就形成了第一种没有文件映射,又不是第一种特殊情况的,一种变形的未初始化区。
即没有做未初始化区,每个节都有文件映射,但是有一个节映射到内存远大于文件实际占用的大小,这就是无文件映射的变种。
如果在程序中在1个节中发现 VirtualSize > SizeOfRawData,很有可能就是把 未初始化数据区和已初始化数据区合并在1个节中。已初始化的数据映射到内存,VirtualSize-SizeOfRawData剩下的空间作为未初始化数据的空间使用。
使用c语言编写一个最简单的程序
#include "stdafx.h" //int mm[10000]; int _tmain(int argc, _TCHAR* argv[]) { printf("HelloWorld!\n"); return 0; }
定义一个含有全局变量的数组,和一个没有初始化的数组,经过比较,发现
1、.data节(全局变量区)中,SizeOfRawData为0x0200 < VirtualSize为0x9FCC,即为变形的全局变量区
2、定义全局变量的大小发生变化,内存映射尺寸值就会发生变化。
上边说的所有的可以理解为下边2句话:
1、这种变形也是一种增加节大小的一种办法。
2、VirtualSize只是一个说明字段,通常情况下没用,那么在什么情况下才有用。
1) 当节中的 SizeOfRawData为0的时候,这个时候映射到内存就要使用VirtualSize按照内存大小进行映射。
2)当VirtualSize用内存对齐求的值 远大于 SizeOfRawData也求内存对齐的值,表示节含有未初始化的数据,这个时候映射到内存要用VirtualSize的值进行映射。 当然这个时候如果要编程实现从文件映射到到内存中,max(Bufferalign(SizeOfRawData),Bufferalign(VirtualSize)),即这2个值都按照内存对齐的最大值进行映射才对。