3_IMAGE_OPTIONAL_HEADER32

3 IMAGE_OPTIONAL_HEADER32

IMAGE_OPTIONAL_HEADER32

AddressOfEntryPoint 字段

指出文件被执行时的入口地址, 这是一个 RVA 地址. 如果在一个可执行文件上附加了一段代码并想让这段代码首先被执行, 那么只需要将这个入口地址指向附加的代码就可以了.

ImageBase 字段

指出文件的优先装入地址, 也就是说当文件被执行时, 如果可能的话, windows 优先将文件装入到由 ImageBase 字段指定的地址中, 只有指定的地址已经被其它模块所使用的时候, 文件才被装到其他地址中.

链接器产生可执行文件的时候对应这个地址生成机器码, 所以当文件被装入这个地址时不需要进行重定位操作, 装入的速度最快, 如果文件被装载到其他地址的话, 将不得不进行重定位操作, 这样就要慢一点.

对于 exe 文件来说, 由于每个文件总是使用独立的虚拟地址空间, 优先装入地址不可能被其他模块占据, 所以 exe 总是能够按照这个地址装入, 这也意味着 exe 文件不再需要重定位信息.

如果使我们自己弄的病毒, 我们必须自己寻找这个对齐的偏移差, 然后进行重定位.

对于 dll 文件来说, 由于多个 dll 文件全部使用宿主 exe 文件的地址空间, 不能保证优先装入地址没有被其他 dll 使用, 所以 dll 文件中必须包含重定位信息以防万一. 因此, 在前面介绍的 IMAGE_FILE_HEADER 结构的 Characteristics 字段中, dll 文件对应的 IMAGE_FILE_RELOCS_STRIPPED 位总是为 0, 而 exe 文件的这个标志总是为 1.

在链接的时候, 可以通过对 link.exe 指定 /base:address 选项来自定义优先装入地址, 如果不指定这个选项, 一般 exe 文件默认优先装入地址为 00400000H, 而 dll 文件默认优先装入地址为 10000000H.

SectionAlignment 字段和 FileAlignment 字段

SectionAlignment 字段指定了节 (Section) 被装入内存后的对齐单位. 也就是说, 每个节 (Section) 被装入的地址必定是本字段指定数值的整数倍.

而 FileAlignment 字段指定了节 (Section) 存储在磁盘文件中时的对齐单位.

Subsystem 字段

没啥要讲的, 自己去搜吧

DataDirectory 字段

一个数组, 有 16 个元素, 每个元素都是一个结构. 每个结构都很简单, 只存放了两个内容.

IMAGE_DATA_DIRECTORY struct{
    VitualAddress DWORD ?;  // 存放数据的起始 RVA
    isize DWORD ?;  // 存放数据块的长度
}

这个字段可以说是最重要的字段之一, 它由 16 个相同的 IMAGE_DATA_DIRECTORY 结构组成, 显然 PE 文件中的数据是按照装入内存后的页属性归类而被放在不同的节中的, 但是这些处于各个节中的数据按照用途可以被分为导出表, 导入表, 资源, 重定位表等数据块, 这 16 个 IMAGE_DATA_DIRECTORY 结构就是用来定义多种不同用途的数据块的. IMAGE_DATA_DIRECTORY 结构的定义很简单, 它仅仅指出了某种数据块的位置和长度.

索引 索引值在 windows.inc 中的预定义值 对应的数据块
0 IMAGE_DIRECTORY_ENTRY_EXPORT 导出表
1 IMAGE_DIRECTORY_ENTRY_IMPORT 导入表
2 IMAGE_DIRECTORY_ENTRY_RESOURCE 资源
... ... ...
5 IMAGE_DIRECTORY_ENTRY_BASERELOC 重定位表
... ... ...

在 PE 文件中寻找特定的数据时就是从这些 IMAGE_DATA_DIRECTORY 结构开始的, 比如要存取资源, 那么必须从第 3 个 IMAGE_DATA_DIRECTORY 结构 (索引为 2) 中得到资源数据块的大小和文职; 同理, 如果要查看 PE 文件导入了哪些 dll 文件的哪些 API 函数, 那就必须首先从第 2 个 IMAGE_DATA_DIRECTORY 结构得到导入表的位置和大小.