前述
在 NT 头结束后,紧接着就是区块表,区块表包含每个块在映象中的信息,分别指向不同的区块实体。
区块表
区块表是一个 IMAGE_SECTION_HEADER 结构数组,这个结构包含区块的信息,比如位置、长度、属性等,区块的数目是由 NT 头中的文件头里的 NumberOfSections 给出。以下为 IMAGE_SECTION_HEADER 结构:
在上述图中,有两个字段比较重要,分别为 VirtualAddress、PointerToRawData,这两个字段用于将相对虚拟地址或虚拟地址转换为文件偏移地址,以下为 RVA 转 FOA 函数:
1 | 1. // RVA 转 FOA |
计算公式为:FOA = VA - ImageBase - (所在区段的 RVA - 所在区段的 FOA) 或 FOA = RVA - 所在区段的 RVA + 所在区段的 FOA。
在上述代码中,有一个为 IMAGE_FIRST_SECTION,我们来看下它的定义,如下:
其实 IMAGE_FIRST_SECTION 为一个宏,它主要由三部分相加组成,作用是获取到第一个区段的首地址,参数为 NT 头。你可以把这个首地址理解成数组名,数组的首地址。在获取到了地址后,下面的 for 循环遍历所有的区段表找到符合要求的区段。这三部分内容具体如下:
- IMAGE_NT_HEADERS 的起始地址
- IMAGE_OPTIONAL_HEADER32 (PE 扩展头)在 IMAGE_NT_HEADERS 中的偏移
- IMAGE_OPTIONAL_HEADER32 的大小
其中后两个加起来的大小恰好就是 IMAGE_NT_HEADERS 的大小,再跟第一个相加就得到区段表的地址了。看到这你可以会问,为什么不直接加上 IMAGE_NT_HEADERS 的大小呢?因为 IMAGE_OPTIONAL_HEADER32 大小不固定,32 位下该值为 0x00E0H,64 位下该值为 0x00F0H,并且用户还可以自定义其大小。
额外说明
扩展头大小是由文件头中 SizeOfOptionalHeader 字段给出,FIELD_OFFSET 这个是给出 OptionalHeader 在 IMAGE_NT_HEADERS 结构中的偏移,如下: