PE文件结构及其加载机制(一)
一、PE文件结构
PE即Portable Executable,是win32环境自身所带的执行体文件格式,其部分特性继承自Unix的COFF(Common Object File Format)文件格式。PE表示该文件格式是跨win32平台的,即使Windows运行在非Intel的CPU上,任何Win32平台的PE装载器也能识别和使用该文件格式的文件。
所有Win32执行体(除了VxD和16位的DLL)都使用PE文件格式,如EXE文件、DLL文件等,包括NT的内核模式驱动程序(Kernel Mode Driver)。
PE文件至少包含两个段,即数据段和代码段。Windows NT 的应用程序有9个预定义的段,分别为 .text 、.bss 、.rdata 、.data 、.pdata 和.debug 段,这些段并不是都是必须的,当然,也可以根据需要定义更多的段(比如一些加壳程序)。
在应用程序中最常出现的段有以下6种:
.执行代码段,通常 .text (Microsoft)或 CODE(Borland)命名;
.数据段,通常以 .data 、.rdata 或 .bss(Microsoft)、DATA(Borland)命名;
.资源段,通常以 .rsrc命名;
.导出表,通常以 .edata命名;
.导入表,通常以 .idata命名;
.调试信息段,通常以 .debug命名;
PE文件的结构在磁盘和内存中是基本一样的,但在装入内存中时又不是完全复制。Windows装载器在装载的时候仅仅建立好虚拟地址和PE文件之间的映射关系,只有真正执行到某个内存页中的指令或访问某一页中的数据时,这个页才会被从磁盘提交到物理内存。但因为装载可执行文件时,有些数据在装入前会被预先处理(如需要重定位的代码),装入以后,数据之间的相对位置也可能发生改变。因此,一个节的偏移和大小在装入内存前后可能是完全不同的。
..
PE的基本结构就是这样了。
下面开始各个部分学习。
==================================================
(1)IMAGE_DOS_HEADER和Dos Stub
其实IMAGE_DOS_HEADER和Dos Stub没有什么重要的,只是IMAGE_DOS_HEADER中的第十九个成员指向IMAGE_NT_HEADERS的位置。
typedef struct IMAGE_DOS_HEADER { WORD e_magic; WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10]; DWORD e_lfanew; //指向IMAGE_NT_HEADERS的所在 }IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
下面我们用一个具体的PE文件来看看。
这是64个字节(64Byte),下面是一些分析。
首先,只要是PE文件,那么开始两个字节就一定是4D 5A。
然后看最后四个字节,是E8000000,代表了地址是000000E8h,我们往下找找。
通过搜索,可以直接到达这个位置,一会我们再来看这个位置。
也就是说,00000040h到000000E8h之间的数据都是Dos Stub(称为dos残余程序)。
-----------------------------------------------------------------------------
(2)PE文件头
现在我们来看看IMAGE_NT_HEADERS的情况,看看e_lfanew指向的这里是什么含义,看看000000E8h(这个数值是不固定的,不同的PE程序的值可能不同,我们只要找到这个位置,读取它的值即可找到PE文件的头所在)指向的这里又是什么。
typedef struct IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; }IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;
首先我们看到的,这是一个结构体,而且结构体中还有结构体。
我们先看看它的总大小和在c32中的情况
【1】其中红色的部分就是DWORD Signature,表示字符“PE\0\0”,十六进制为00004550h,这个值也是不会变化的。
【2】绿色线部分表示IMAGE_FILE_HEADER FileHeader,我们具体来看看这个结构体代表了什么含义。
typedef struct _IMAGE_FILE_HEADER { WORD Machine; //运行平台 WORD NumberOfSections; //块(section)数目 DWORD TimeDateStamp; //时间日期标记 DWORD PointerToSymbolTable; //COFF符号指针,这是程序调试信息 DWORD NumberOfSymbols; //符号数 WORD SizeOfOptionalHeader; //可选部首长度,是IMAGE_OPTIONAL_HEADER的长度 WORD Characteristics; //文件属性 }
第一个表示这个程序运行需要的平台,来看看我刚才这个程序的值
这里表示运行的平台是Intel的CPU,下面是一个列表代表各种对应的平台的值:
第二个表示块的数目
0004当然就是4个节了,一会我们可以看看是哪四个节
第三个表示时间,我们先看看
也就是4F91318Fh,用计算器算一下就是1334915471,这个值应该是秒,我们换算一下,结果大约是42年
这个是程序的创建日期,减去42年,大约就是1970年,这个日期就是从1970到文件最后修改时间之间的秒数?
这个我可不知道,有待研究。
第六个SizeOfOptionalHeader表示之后OptionalHeader的大小,我们先来看看
00E0就是十进制的224,也就是说OptionalHeader的大小是224字节。
第七个值Characteristics表示文件属性,它的每一个bit都代表了某种含义。
Bit 0 :置1表示文件中没有重定向信息。每个段都有它们自己的重定向信息。 这个标志在可执行文件中没有使用,在可执行文件中是用一个叫做基址重定向目录表来表示重定向信息的,这将在下面介绍。 Bit 1 :置1表示该文件是可执行文件(也就是说不是一个目标文件或库文件)。 Bit 2 :置1表示没有行数信息;在可执行文件中没有使用。 Bit 3 :置1表示没有局部符号信息;在可执行文件中没有使用。 Bit 4 : Bit 7 Bit 8 :表示希望机器为32位机。这个值永远为1。 Bit 9 :表示没有调试信息,在可执行文件中没有使用。 Bit 10:置1表示该程序不能运行于可移动介质中(如软驱或CD-ROM)。在这 种情况下,OS必须把文件拷贝到交换文件中执行。 Bit 11:置1表示程序不能在网上运行。在这种情况下,OS必须把文件拷贝到交换文件中执行。 Bit 12:置1表示文件是一个系统文件例如驱动程序。在可执行文件中没有使用。 Bit 13:置1表示文件是一个动态链接库(DLL)。 Bit 14:表示文件被设计成不能运行于多处理器系统中。 Bit 15:表示文件的字节顺序如果不是机器所期望的,那么在读出之前要进行 交换。在可执行文件中它们是不可信的(操作系统期望按正确的字节顺序执行程序)。
010Fh就是0000000100001111
具体的我们来看一张图:
【3】下面是OptionalHeader,占224个字节
我们看看 它的结构是怎样的
typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
好了,下面的内容将在下一篇文章中学习。