PE文件格式

PE(Portable Exec utable)是 Win32 平台下可执行文件遵守的数据格式。常见的可执行文件(如“*.exe”文件和“*.dll”文件)都是典型的 PE 文件。

 

PE 文件格式把可执行文件分成若干个数据节(section),不同的资源被存放在不同的节中。

一个典型的 PE 文件中包含的节如下。

  • .text 由编译器产生,存放着二进制的机器代码,也是我们反汇编和调试的对象。
  • .data 初始化的数据块,如宏定义、全局变量、静态变量等。
  • .idata 可执行文件所使用的动态链接库等外来函数与文件的信息。
  • .rsrc 存放程序的资源,如图标、菜单等。

除此以外,还可能出现的节包括“.reloc”、“.edata”、“.tls”、“.rdata”等。

 

如果可执行文件经过了“加壳”处理,PE 的节信息就会变得非常“古怪”。 在 Crack 和反病毒分析中需要经常处理这类古怪的 PE 文件。

 

虚拟内存

Windows 的内存可以被分为两个层面:物理内存和虚拟内存。其中,物理内存比较复杂,需要进入Windows 内核级别 ring0 才能看到。通常,在用户模式下,我们用调试器看到的内存 地址都是虚拟内存。

 

Windows 的内存管理机制在很大程度上与日常生活中银行所起的金融作用有一定的相似性,我们可以通过一个形象的比方来理解虚拟内存。

  •  进程相当于储户。
  •  内存管理器相当于银行。
  •  物理内存相当于钞票。
  •  虚拟内存相当于存款。
  •  进程可能拥有大片的内存,但使用的往往很少;储户拥有大笔的存款,但实际生活中 的开销并没有多少。
  •  进程不使用虚拟内存时,这些内存只是一些地址,是虚拟存在的,是一笔无形的数字财富。
  •  进程使用内存时,内存管理器会为这个虚拟地址映射实际的物理内存地址,虚拟内存 地址和最终被映射到的物理内存地址之间没有什么必然联系;储户需要用钱时,银行 才会兑换一定的现金给储户,但物理钞票的号码与储户心目中的数字存款之间可能并没有任何联系。
  •  操作系统的实际物理内存空间可以远远小于进程的虚拟内存空间之和,仍能正常调度; 银行中的现金准备可以远远小于所有储户的储蓄额总和,仍能正常运转,因为很少会出现所有储户同时要取出全部存款的现象;社会上实际流通的钞票也可以远远小于社会的财富总额。

 

PE文件与虚拟内存之间的映射

(1)文件偏移地址(File Offset) 数据在 PE 文件中的地址叫文件偏移地址,个人认为叫做文件地址更加准确。这是文件在磁盘上存放时相对于文件开头的偏移。

(2)装载基址(Image Base) PE 装入内存时的基地址。默认情况下,EXE 文件在内存中的基地址是 0x00400000,DLL文件是 0x10000000。这些位置可以通过修改编译选项更改。

(3)虚拟内存地址(Virtual Address,VA) PE 文件中的指令被装入内存后的地址。

(4)相对虚拟地址(Relative Virtual Address,RVA) 相对虚拟地址是内存地址相对于映射基址的偏移量。 虚拟内存地址、映射基址、相对虚拟内存地址三者之间有如下关系。

VA= Image Base+ RVA

 

由于操作系统在进行装载时“基本”上保持 PE 中的各种数据结构,所 以文件偏移地址和 RVA有很大的一致性。之所以说“基本”上一致是因为还有一些细微的差异。这些差异是由于文件数据的存放单 位与内存数据存放单位不同而造成的。

(1)PE 文件中的数据按照磁盘数据标准存放,以 0x200 字节为基本单位进行组织。因此 PE 数据节的大小永远是 0x200 的整数倍。

(2)当代码装入内存后,将按照内存数据标准存放,并以 0x1000 字节为基本单位进行组织。因此,内存中的节总是 0x1000 的整数倍。

那么文件偏移地址与虚拟内存地址之间的换算关系可以用下面的公式来计算。

文件偏移地址

= 虚拟内存地址(VA)−装载基址(Image Base)−节偏移

= RVA -节偏移