节表:其实是一个IMAGE_SECTION_HEADERS数组。单个节表的数据大小为40
IMAGE_NT_HEADERS.FileHeader.NumberOfSections指出。
IMAGE_SECTION_HEADERS结构
IMAGE_SECTION_HEADER STRUCT
Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?) ;8个字节的节区名称
union Misc
PhysicalAddress dd ?
VirtualSize dd ? ;节区的尺寸
ends
VirtualAddress dd ? ;节区的RVA地址
SizeOfRawData dd ? ;在文件中对齐后的尺寸
PointerToRawData dd ? ;在文件中的偏移
PointerToRelocations dd ? ;在OBJ文件中使用
PointerToLinenumbers dd ? ;行号表的位置(供调试用)
NumberOfRelocations dw ? ;在OBJ文件中使用
NumberOfLinenumbers dw ? ;行号表中行号的数量
Characteristics dd ? ;节的属性
IMAGE_SECTION_HEADER ENDS
这里可以得出区块的大小:
Field | Meanings |
---|---|
Name1 | 事实上本域的名称是"name",只是"name"已被MASM用作关键字,所以我们只能用"Name1"代替。这儿的节名长不超过8字节。记住节名仅仅是个标记而已,我们选择任何名字甚至空着也行,注意这里不用null结束。命名不是一个ASCIIZ字符串,所以不用null结尾。 |
VirtualAddress | 本节的RVA(相对虚拟地址)。PE装载器将节映射至内存时会读取本值,因此如果域值是1000h,而PE文件装在地址400000h处,那么本节就被载到401000h。 |
SizeOfRawData | 经过文件对齐处理后节尺寸,PE装载器提取本域值了解需映射入内存的节字节数。(译者注: 假设一个文件的文件对齐尺寸是0x200,如果前面的 VirtualSize域指示本节长度是0x388字节,则本域值为0x400,表示本节是0x400字节长)。 |
PointerToRawData | 这是节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。 |
Characteristics | 包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。 |
现在我们已知晓 IMAGE_SECTION_HEADER结构,再来模拟一下 PE装载器的工作吧:
- 读取 IMAGE_FILE_HEADER的 NumberOfSections域,知道文件的节数目。
- SizeOfHeaders域值作为节表的文件偏移量,并以此定位节表。(节表的偏移量=所有头的总和-节数*节表长度=SizeOfHeaders的值-(NumberOfSections*单个节表的长度40))
- 遍历整个结构数组检查各成员值。
- 对于每个结构,我们读取PointerToRawData域值并定位到该文件偏移量。然后再读取SizeOfRawData域值来决定映射内存的字节数。将VirtualAddress域值加上ImageBase域值等于节起始的虚拟地址。然后就准备把节映射进内存,并根据Characteristics域值设置属性。
- 遍历整个数组,直至所有节都已处理完毕。