在PE文件头之前

理论

Windows的PE(Portable Executable)文件有两个头,一个是是Windows头,一个是DOS头。在文件的最开始会有一段DOS的EXE文件头,来说明这个程序不可以在DOS环境下运行。我们需要在DOS头+3Ch处,会有一个4字节的指针指向windows头。

根据+3Ch处的值,定位到Windows文件头可以看到"PE"两个字节,Windows头就从此处开始。这也就是Windows EXE文件经常被称为PE文件的原因。

实践

  1. 在xp环境下,用QuickView打开一个EXE文件,观察其头部。
    • 在00h处有两个字节4D 5A表示这是一个DOS头。
    • 在4Eh处,有一个字符串This program cannot be run in DOS mode. 表明这不是一个DOS文件
    • 在3C处,有一个四字节指针,其值为000000D0h,颜色标黄,指向windows头

  1. 查看文件地址D0处的值

    可以看到此处的两个字节为50 45即“PE”,表示这个EXE文件是一个PE文件,从这两个字节开始才是PE的文件头。

PE文件头

背景知识

要了解PE文件头的具体内容,我们需要对Windows的内存管理,文件存储有一定的了解。接下来做简要的说明,想要了解更详细的内容可以去学习操作系统的相关知识。

Windows通过分段以及分页两种机制管理内存和实现进程的隔离及保护。以下说明会涉及到一些寄存器和比较抽象的概念,不懂也没有关系。只需要知道结论,*一个进程的虚拟地址会经过一些转换成为真正的物理地址. *进程之间的虚拟地址可以相同,但相同的虚拟地址会转化成不同的物理地址。

  • 在Windows系统中,为了向下兼容,保留了分段(section)的的机制,但是CS,DS,SS这些段地址在GDT表中的值全部为0,所以经过分段后的逻辑地址(logical address)与线性地址(linear address)是完全一致的,在此处不用过多理会。
  • 对于分页(paging)机制,每一个进程都有一个属于他们自己的页目录表,每一个页目录表是不一样的,线性地址需要经过页表项的查询,转换成的真正的物理地址。所以对于不同进程而言,相同的线性地址会转化成为不同的物理地址。

有了分页的机制后,windows的exe在运行时统一被载入内存的基地址=400000h

在PE文件头中出现的内存地址都是以RVA(relative virtual address)相对虚拟地址的形式表示的,即

程序真正的虚拟地址 = 相对虚拟地址+载入内存的基地址=RVA+400000h

PE文件头的具体内容

有了上述的了解后,接下来我们来看PE文件头的具体内容。以下的描述涉及到的偏移地址均为16进制,且均相对于PE文件头。

以一个EXE文件的PE头为例,我们来说明,PE文件头一些重要的项的具体含义。

000000d0: 5045 0000 4c01 0300 2fe6 a25d 0000 0000  PE..L.../..]....
			  |
		          +------------------------------- +06 用两个字节来保存段的数量,此时为0003个段
000000e0: 0000 0000 e000 0f01 0b01 0600 0050 0000  .............P..
000000f0: 0050 0000 0000 0000 4510 0000 0010 0000  .P......E.......
				|
				+------------------------- +28 程序的32位入口地址,即当前EXE在运行时,																													第一条指令执行的相对虚拟地址
00000100: 0060 0000 0000 4000 0010 0000 0010 0000  .`....@.........
		      |		|	    |
		      |		|	    +------------- +3C 文件对齐,两个字节,此处为1000h
		      |		+------------------------- +38 内存对齐,两个字节,此处为1000h
		      |				
		      +----------------------------------- +34 程序载入内存的基地址,4个字节EXE一般上都为400000h																												
00000110: 0400 0000 0000 0000 0400 0000 0000 0000  ................
00000120: 00b0 0000 0010 0000 0000 0000 0300 0000  ................
	    |	      |
            |	      |
            |	      +---------------------------------- +54 EXE文件头的文件长度(经过文件对齐之后),载入内存后会进行内存对齐(包括DOS头和Windows头)
            |                                                                                          																				 					
            +-------------------------------------------- +50 EXE文件载入内存后的总长度=文件头+各个section载入内存后的总长度(经过内存对齐后)																	

  • 在PE+F8处定义了各个section的描述,每一个节的描述的大小为28h字节,在QV中查看具体的值如下:

  • PE+F8+0: 节名,8字节,以00作为结束标记
  • PE+F8+8:节的内存长度 4980h
  • PE+F8+C:节的内存偏移 1000h
  • PE+F8+10: 节的文件长度 5000h
  • PE+F8+14: 节的文件偏移 1000h
  • PE+F8+24: 节的属性,read,write,execute

关于PE文件头中的输出表、输入表、资源表、重定位表的描述比较复杂,会另写文档讲述。

输出表:To do...

输入表:Windows PE文件的输入表

重定位表:To do...

随笔是个人学习的总结,如有错误欢迎指出!