深入理解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个值都按照内存对齐的最大值进行映射才对。

posted @ 2023-07-07 18:56  一日学一日功  阅读(494)  评论(0编辑  收藏  举报