1_基本概念

1 基本概念

这些东西不能不知道

exe 和 dll 文件之间的区别完全是语义上的, 因为它们使用完全相同的 PE 格式, 而唯一的区别就是用一个字段标识出这个文件是 exe 还是 dll.

64 位 windows 知识对 PE 格式做了一些简单的修饰, 新的格式叫作 PE32+. 它并没有任何新的结构加进来, 只是简单地将 32 位字段扩展为 64 位.

PE 格式定义的主要地方位于我们的头文件 winnt.h, 这个头文件中几乎能找到关于 PE 文件的所有定义.

PE 文件框架结构图自行搜索.

PE 的基本概念

PE 文件使用的是一个平面地址空间, 所有代码和数据都被合并在一起, 组成一个很大的结构.

文件的内容被分割为不同的区块, 块中包含代码或者数据. 各个区块按页边界来对齐, 区块没有大小限制, 是一个连续的结构.

此外, 每个块有自己在内存中的一套属性, 比如说这个区块是否包含代码, 是否只读或者可读/写等.

PE 文件不是作为单一内存映射文件被装入内存的.

Windows 加载器 (又称为 PE 装载器) 遍历 PE 文件并决定文件的哪一部分被映射, 这种映射方式是将文件较高的偏移位置映射到较高的内存地址中.

PE 结构映射到内存中, 头依旧是头, 屁股依旧是屁股.

当磁盘文件一旦被装入内存中, 磁盘上的数据结构布局和内存中的数据结构布局是一致的. 只是有些没有必要的东西没有被映射上去而已.

这样, 如果你在磁盘的数据结构中找到一些内容, 那么这些内容都能在内存映射文件中找到.

但是数据之间的相对位置有可能改变, 其某项的偏移地址可能区别于原始的偏移位置. 但是不管怎样, 所有表现出来的信息都允许从磁盘文件偏移到内存偏移的转换.

图 2.1 PE 文件结构

基地址

Image Base 基地址是整个内存中 PE 结构的头地址

当 PE 被加载器加载到内存中的时候, 内存中的 PE 文件我们称之为一个模块 (Module)

我们映射文件的起始地址被称为这个模块的句柄 (handle)

一般情况下, 对于我们的 Windows NT, 我们获取了模块的句柄就是相当于获取了基地址, 有了这个基地址就相当于获取了这整个 PE 结构的「头」, 把它的「头发」给牵制住了, 你想让它往哪走就往哪走, 也就是说我们控制了这个模块.

这个基地址可以用函数 GetModuleHandle 获取, 这个函数你可以在 MSDN 查到

相对虚拟地址

RVA 相对虚拟地址 = 被映射到内存中的总的地址 (虚拟地址 VA) - 基地址

文件偏移地址

文件偏移地址是指数据在 PE 文件中的地址,是文件在磁盘上存放时相对于文件开头的偏移。文件偏移地址从 pe 文件的第一个字节开始计数,起始值为 0。用十六进制工具(如WINHEX)打开文件所显示的地址就是文件偏移地址.