你的位置:首页 > 操作系统

[操作系统]PE文件结构详解


  1、定位标准PE头

  DOS Stub长度不固定,所以DOS头不是一个固定大小的数据结构。DOS头位于PE的起始位置,通过DOS头去定位后面标准PE头的位置就是通过字段e_lfanew。

  e_lfanew字段的值是一个相对偏移量,绝对定位时需要加上DOS MZ头的基地址。

  也就是PE头的绝对位置是:

  PE_start = DOS MZ 基地址+IMAGE_DOS_HEADER.e_lfanew

  2、PE文件结构

  在32位系统下,最重要的部分是PE头和PE数据区。

  32位系统下的PE文件被划分为:DOS MZ头、DOS Stub、PE头、节表和节内容。

  节表是PE中所有节的目录,每个目录的字节码就是节内容,节内容按照目录里的指针指向的地址,分别将节的字节码在文件空间中排列起来,组成一个完整的PE文件,PE文件

头等于DOS头加PE头。

  3、PE文件头部解析 

  DOS MZ头IMAGE_DOS_HEADER:

  其中最重要的成员是e_magic成员和e_lfanew成员。

  DOS MZ头下面的是DOS Stub。整个DOS Stub是一个字节块,其内容随着链接时使用的链接器不同而不同,PE中并没有与之对应的相关结构。

  PE头标识Signature:

  在DOS Stub后的是PE 头标识Signature,PE头部信息中有一个四字节的标识,该标识位于指针IMAGE_DOS_HEADER.elfanew指向的位置。其内容固定,对应于ASCII码

  的字符串“PE\0\0”。

  标准PE头IMAGE_FILE_HEADER:

  标准的PE头IMAGE_FILE_HEADER在PE头部标识后面,即位于elfanew值+4的位置。从此位置开始的20个字节为数据结构标准PE头IMAGE_FILE_HEADER的内容。(该结

构在微软官方文档中被称为标准通用对象文件格式)

  该部分记录了PE文件的全局属性,包括PE文件运行的平台,PE文件类型(EXE Or DLL?),文件中存在的节的总数等信息。

  该部分用途:判断文件类型,得到PE文件中节的总量,当成节区信息进行遍历操作时的循环次数。

    扩展PE头IMAGE_OPTIONAL_HEADER32

 存储文件执行时的入口地址、文件被操作系统装入内存之后的默认基地址,节在磁盘和内存中的对其单位等信息。   

  PE头IAMGE_NT_HEADER:

  该部分是广义上的PE头,在标准PE文件中其大小为456字节

  IMAGE_NT_HEADER是上述三部分的总和,及Signature、IMAGE_FILE_HEADER和IMAGE_OPTIONAL_HEADER。

  该结构的详细定义如下:

  数据目录项IMAGE_DATA_DIRECTORY

  IMAGE_OPTIONAL_HEADER结构的最后一个字段DataDirectory字段定义了PE文件中出现所有不同类型的数据的目录信息。

  如导入表、导出表、资源和重定位表等。在内存中,这些数据被操作系统以页为单位组织起来,并赋以不同的访问属性。在文件中,这些数据也同样被组织起来,按照不同类别

分别存放在文件的指定位置。

  该结构就是用来描述不同类别的数据在文件(和内存)中的位置及大小。

  该数据目录中定义的数据类型一直是种,PE中用数据目录项IMAGE_DATA_DIRECTORY的数据结构定义每种数据结构。

  结构定义如下:

  总得数据目录一共由16个相同的IMAGE_DATA_DIRECTORY结构连续排列在一起组成。

  节表项IMAGE_SECTION_HEADER:

  节表位于IMAGE_NT_HEADER之后,由多个节表项(IMAGE_SECTION_HEADER)组成,每个节表项记录了PE中与某个特定的节有关的信息,如节的属性,节的大小,在

文件和内存中起始位置等。

  节表中的节的数量由字段IMAGE_FILE_HEADER中的NumberOfSection成员定义。

  节表的数据结构定义如下:

  4、PE头IMAGE_NT_HEADER的字段

  1.IMAGE_NT_HEADER.Signature

  +0000h,双字。PE文件标识,被定义为00004550h。

  如果更改其中任何一个字节,操作系统就会无法把该文件识别为正确的PE文件。由于文件的DOS头部分没有被破坏,程序仍然可以在DOS环境下运行。

  2.IMAGE_NT_HEADER.FileHeader:

  +0004h,结构。该结构指向IMAGE_FILE_HEADER。

  3.IMAGE_NT_HEADER.OptionalHeader:

  +0018h,结构。这个指向IMAGE_OPTIONAL_HEADER32。它是操作系统映像文件所有独有的头部信息。

  5、标准PE头IMAGE_FILE_HEADER的字段

  4.IMAGE_FILE_HEADER.Machine:

  +0004h,单字。指定给PE文件运行的平台。

  5.IMAGE_FILE_HEADER.NumberOfSections:

  +0006h,单字。文件中存在的节的总数。

  在XP系统中,可以有0个节,但是数值不能小于1,也不能超过96,如果将该值设为0,操作系统在装载时会提示不是有效的Win32程序。

  如果想在PE中增加或删除节,必须变更此处的值。

  这个值不能比实际内存中存在的节多,也不能少,否则在装载的时候回出现错误。

  6.IMAGE_FILE_HEADER.TimeDateStamp:

  +0008h,双字。编译器创建此文件的时间戳。低32位存储的值是自1970年1月1日00:00开始到创建时间为止的总秒数。

  这个值可以随便修改,对程序的运行没有影响。

  7.IMAGE_FILE_HEADER.PointerToSymbolTable:

  +000Ch,双字。COFF符号表的文件偏移。

  如果不存在COFF符号表,此值为0.对于映像文件,这个值为0。

  8.IMAGE_FILE_HEADER.NumberOfSymbols:

  +0010h,双字。符号表中元素的数目。

  因为字符串表紧跟符号表,所以可以根据这个值定位字符串表。

  在映像文件中,这个值为0,主要用于调试。

  9.IAMGE_FILE_HEADER.SizeOfOptionalHeader:

  +0014h,单字。指定结构IMAGE_OPTIONAL_HEADER32的长度。

  默认情况下,这个值是00e0h,如果是64位的PE文件,该结构默认大小为00F0h。

  10.IMAGE_FILE_HEADER.Characteristics:
  +0016h,单字。文件属性标志字段,它的不同数据位定义了不同的文件属性。这是一个很重要的字段,不同的定义将影响系统对文件的载入方式。
  对于普通的可执行PE文件来说,这个字段的值一般是010fh,对于DLL文件来说,这个字段的值是210ch。
  第1位为1时,表明此映像文件是合法的,可以运行。如果未设置此标志,表明出现链接器错误。
  第10位为1时,如果此映像文件在可移动存储介质上,加载器将完全加载它并把它复制到内存交换文件中。
  第11位为1时,如果此映像文件在网络上,那么加载器也将完全加载它并把它复制到内存交换文件中。
  当第13位为1时,表明此映像文件为动态链接库。这样的文件总被认为是可执行文件。
  可执行文件的标志位被设置为010fh,即第0、1、2、3、8位分别被设置为1,标识该文件为可执行文件,不含重定位信息,不含符号信息和行为信息,文件只在32位平台运
行。
  6、扩展PE头IAMGE_OPTIONAL_HEADER32的字段
  11.IMAGE_OPTIONAL_HEADER32.Magic:
  +0018h,单字。魔术字,说明文件的类型,如果为010BH,则表示该文件为PE32;
  如果为0107h,则表示为ROM映像;如果为020BH,则表示文件为PE32+,即64位下的PE文件。
    12.IMAGE_OPTIONGAL_HEADER32.MajorLinkerVersion
 13.IMAGE_OPTIONAL_HEADER32.MinorLinkerVersion:
        +001ah,单字。这两个字段都是字节型,指定链接的版本号,对执行没有任何影响。
    14.IAMGE_OPTIONAL_HEADER32.SizeOfCode:
        +001ch,双字。所有代码节的总和(以字节计算),该大小是基于文件对齐后的大小,而非内存对齐后的大小。
    15.IMAGE_OPTIONAL_HEADER32.SizeOfInitializedData:
        +0020h,双字。所有包含已经初始化的数据的节的总大小。
    16.IMAGE_OPTIONAL_HEADER32.SizeOfUninitializedData:
        +0024h,双字。所有包含未初始化数据的节的总大小。
        数据未被初始化,在文件中不占用空间,但被加载到内存之后,PE加载程序会为这些数据分配适当大小的虚拟地址空间。
    17.IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint:
        +0028h,双字。该字段值是一个RVA,记录了启动代码距离该PE加载后的起始位置有多少个字节。
        如果一个可执行文件中附加了一段自己的代码,并想让这段代码首先被执行,一般要修改这里的值,使其指向自己代码的位置。
        对于一般程序映像,它就是启动地址。
        对于设备驱动文件来说,它是初始化函数的地址。入口点对于DLL是可选的,如果不存在入口点,这个字段值必须设置为0。
    18.IMAGE_OPTIONAL_HEADER32.BaseOfCode:
        +002Ch,双字。代码节的起始RVA,表示映像被加载进内存时代码节的开头相对于映像基地址的偏移地址。一般情况下,代码节紧跟在PE头部后面,节的名称通常为“.text”。
    19.IMAGE_OPTIONAL_HEADER32.BaseOfData:
        +0030h,双字。数据节的起始RVA,表示映像被加载进内存时数据节的开头相对于映像的基地址的偏移地址。一般情况下,数据节位于文件末尾,节的名称通常为“.data”。
    20.IMAGE_OPTIONAL_HEADER32.ImageBase:
        +0034h,双字。该字段指出了PE映像的优先装入地址。就是在AddressOfEntryPoint中的程序被加载到内存之后的RVA。
        链接器在产生可执行文件时,是对应这个地址生成机器码。
        如果操作系统也是按照这个地址加载机器码到内存中,那么指令中许多定位信息就不需要修改了,这样运行速度会快一些。
        对于EXE文件,每个文件使用的都是独立的虚拟地址孔家。所以,装入的地址通常不会被其他模块占据。EXE文件总能按照这个地址装入,这就意味着装入后的EXE文件不需要进行重定位了。
        在链接的时候,可以使用参数“.base”来指定优先装入的地址,如果不指定,链接器默认装入EXE的地址是0X00400000。相对于DLL文件, 默认优先装入的地址是0X1000000。集成用到多个DLL文件,装入地址可能会发生冲突,PE加载器会调整其中的值。
        可以自己定义这个值,但是取值不能超出边界,取得值必须在进程空间中,该值必须是64K的整数倍。
    21.IMAGE_OPTIONAL_HEADER32.SectionAlignment:
        +0038h,双字。内存中节的对齐粒度。该字段指定了节被装入内存后的对齐单位。
        SectionAlignment的值必须大于等于FileAlignment的值。
    22.IMAGE_OPTIONAL_HEADER32.FileAlignment:
        +003ch,双字。文件中节的对齐粒度。文件中节对齐是为了提高文件从磁盘加载的效率。
        Windows XP用来组织硬盘的所有文件系统都是基于簇(分配单元)的,每个簇包含几个物理扇区。扇区是磁盘物理存取的最小单位。簇越大,磁盘存储信息的容量就越大,但存取所花费的事件越长。
        通常情况下,Windows会选择使用152字节的簇大小来格式化分区,最大可达到4KB。
    23.IMAGE_OPTIONAL_HEADER32.MajorOperatingSystemVersion:
    24.IMAGE_OPTIONAL_HEADER32.MinorOperatingSystemVersion:
        +0040h。上述两个字段都为单字,共计为双字。标识操作系统的版本号,分主版本号和次版本号。
    25.IMAGE_OPTIONAL_HEADER32.MajorImageVersion:
    26.IMAGE_OPTIONAL_HEADER32.MinorImageVersion:
        +0044h,双字。本PE文件映像的版本号。
    27.IMAGE_OPTIONAL_HEADER32.MajorSubsystemVersion
    28.IMAGE_OPTIONAL_HEADER32.MinorSubsystemVersion
        +0048h,双字。运行所需要的子系统的版本号。
    29.IMAGE_OPTIONAL_HEADER32.Win32VersionValue:
        +004ch,双字。子系统版本的值,暂时保留未用,必须设置为0。
    30.IMAGE_OPTIONAL_HEADER32.SizeOfImage:
        +0050h,双字。内存中整个PE文件的映射尺寸。
        必须保证它的值是SectionAlignment的整数倍。
    31.IMAGE_OPTIONAL_HEADER32.SizeOfHeaders:
        +0054h,双字。所有头+节表按照对齐粒度对齐后的大小。
    32.IMAGE_OPTIONAL_HEADER32.Checksum
        +0058h,双字。检验和,大多数PE文件中,这个值为0,但是在一些内核模式的驱动程序和系统DLL中,该值必须是正确的。
    33.IMAGE_OPTIONAL_HEADER32.Subsystem
        +005ch,单字。指定使用界面的子系统。取值如下:
    34.IMAGE_OPTIONAL_HEADER32.DllCharateristics
        +005eh,单字。DLL文件属性。是一个标志,不是针对DLL文件的,而是针对所有的PE文件。
    35.IMAGE_OPTIONAL_HEADER32.SizeOfStackReserve:
        +0060h,双字。初始化时保留栈的大小。该字段表示为初始线程的栈二保留的虚拟内存数量。该字段默认值为0x100000(1MB)。
    36.IMAGE_OPTIONAL_HEADER32.SizeOfStackCommit:
        +0064h,双字。初始化时实际提交的栈的大小。
        保证初始线程的栈实际占用的内存空间的大小,它是被系统提交的。
    37.IMAGE_OPTIONAL_HEADER32.SizeOfHeapReserve:
        +0068h,双字。初始化保留的堆的大小。用来保留初始进程堆使用的虚拟内存,堆的句柄可以通过GetProcessHeap函数获得。每一个进程至 少会有一个默认的进程堆,该堆在启动进程的时候被创建,在进程的生命周期中永远不会被删除。默认值为1MB。
    38.IMAGE_OPTIONAL_HEADER32.SizeOfHeapCommit:
        +006ch,双字。初始化时还涉及提交的堆大小。在进程初始化时设定的堆所占用的内存空间,默认值为1页。
    39.IMAGE_OPTIONAL_HEADER32.LoaderFlags:
        +0070h,双字。加载标志。
    40.IMAGE_OPTIONAL_HEADER32.NumberOfRvaAndSzie:
        +0074h,双字。定义数据目录结构的数量,一般为00000010h,即16个。
        该字段由SizeOfOptionalHeaders决定,实际应用中可取2~16.
    41.IMAGE_OPTIONAL_HEADER32.DataDirectory:
        +0078h,结构。
        由16个IMAGE_DATA_DIRECTORY结构线性排列而成。定义PE文件中16中不同类别的数据所在的位置和大小。
7、数据目录项IMAGE_DATA_DIRECTORY的字段
    42.IMAGE_DATA_DIRECTORY.VirtualAddress:
        +0000h,双字。该字段记录了特定数据类型的起始RVA。针对不同的数据结构,该字段包含的数据含义不同。
    43.IMAGE_DATA_DIRECTORY.isize:
        +0004h,双字。该字段记录了特定类型的数据块的长度。
8、节表项IMAGE_SECTION_HEADER
    44.IAMGE_SECTION_HEADER.Name1
        +0000h,8字节。通常是以“\0“结尾的ASCII码字符串”来标识节的名称。内容可自行定义。
    45.IMAGE_SECTION_HEADER.Misc:
        +0008h,双字。
    46.IMAGE_SECTION_HEADER.VirtualAddress
        +000ch,双字。节区的RVA地址。
    47.IMAGE_SECTION_HEADER.SizeOfRawData
        +0010h,双字。节在文件对齐后的尺寸。
    48.IMAGE_SECTION_HEADER.PointerToRawData:
        +0014h,双字。节区起始数据在文件中的偏移。
    49.IMAGE_SECTION_HEADER.PointerToRelocations:
        +0018h,双字。在“obj”文件中使用,指向重定位表的指针。
    50.IMAGE_SECTION_HEADER.PointerToLinenumbers
        +001ch,双字。行号表的位置。
    51.IMAGE_SECTION_HEADER.NumberOfRelocations
        +0020h,单字。重定位表的个数。(在OBJ文件中使用)
    52.IMAGE_SECTION_HEADER.NumberOfLinenumbers
        +0022h,单字。行号表中行号的数量。
    53.IMAGE_SECTION_HEADER.Characteristics:
        +0024h,双字。节的属性。
        该字段属于节的属性标志字段,其中不同的数据位表示了不同的属性。具体定义如下: