PE文件结构

こ雲淡風輕ζ 提交于 2019-12-26 11:22:36

PE,即移植的执行体。在Windows平台下,所有的可执行文件(包括EXE文件、DLL文件、SYS文件、COM文件)均使用PE文件结构。使用PE文件结构的可执行文件也成为PE文件。Windows系统下的可执行文件中包含着各种数据,包括代码、数据、资源等。虽然windows系统下的可执行文件包含如此众多的类型数据,但其存放都是有序的、结构化的、完全依赖于PE文件结构对各种数据的管理。

1.MZ头部是真正的DOS头部,其开始的两个字节为"MZ",该部分用于程序在DOS系统下的加载,它的结构被定义为IMAGE_DOS_HEADER,DOS头是为了该可执行程序可以兼容DOS系统。通常情况下,Win32的PE程序不能在DOS下运行,因此保留了这样一个简单的DOS程序用于提醒:不能运行于DOS程序下。
(1)struct IMAGE_DOS_HEADER{
        WORD e_magic            //这两个字节保存"MZ"
        ...........
        ...........                        //中间的成员不常用省略
        LONG e_lfanew          //这是最后一个成员  保存PE头部的位置
2.PE头保存着Windows系统加载可执行文件的重要信息。PE头部有IMAGE_NT_HEADERS(包含PE标识符,文件头IMAGE_FILE_HEADER、可选头IMAGE_OPTIONAL_HEADER。)定义。PE头部在PE文件中的位置不是固定不变的,PE头部的位置由DOS头部的某个字段给出。
(1)IMAGE_NT_HEADERS包含PE标识符,文件头IMAGE_FILE_HEADER、可选头IMAGE_OPTIONAL_HEADER。
        struct IMAGE_HEADER32{
        DWORD Signatyre;
        IMAGE_FILE_HEADER;
        IMAGE_OPTIONAL_HEADER
        }
    其中的DWORD型就是PE标识符,该值非常重要,如果要简单地判断一个文件是否是一个PE文件,实现就要判断DOS头部的开始字节是否是“MZ”。如果是“MZ”头部,则通过DOS头部找到PE头部,接着判断PE头部的前四个字节是否为“PE\0\0”,如果是的话就是一个有效的PE文件。

(2)IMAGE_FILE_HEADER的起始位置取决于PE头部的起始位置,除了IMAGE_DOS_HEADER的位置除外,其他头部的位置都依赖于PE头部的起始位置IMAGE_FILE_HEADER结构包含PE文件的一些基础信息。
struct IMAGE_FILE_HEADER{
        WORD Machine;                          //0x014c  支持Intel类型的CPU
        WORD NUmberOfSections;        //PE文件的节区有几个  举例是3个
        DWORD TimeDataStamp            //文件时何时被创建的
        DWORD PointerToSymbols        
        DWORD NumberOfSymbols       //这两个字段几乎不被使用
        WORD SizeofOptionHeader       //指定IMAGE_OPTIONAL_HEADER的大小,大小时可变的
        WORD Charateristics                 //指定文件的类型
}
Charateristics:
        0x0001:文件中不存在重定位信息
        0x0002:文件可执行
        0x0004:行号信息已从文件中删除
        0x0008:符号信息已经从文件中删除
        0x2000:DLL文件
        0x1000:系统文件
        0x0100:目标平台为32位
加入其值为:0x010F,则0x010F=0x0001+0x0002+0x0004+0x0008+0x0100,即该文件不存在定位信息、可执行、行号和符号信息已经从文件中删除、目标平台为32位
(3)IMAGE_OPTIONAL_HEADER:虽然称作可选头,其实是必须存在的头。可选头紧挨着文件头(MAGE_FILE_HEADER),大小由文件头中的参数SizeofOptionHeader给出。不能直接sizeof(IMAGE_OPTIONAL_HEADER)

 

所以可选头的起始位置=文件头结束位置+1;
所以可选头的结束位置=可选头的起始位置+SizeofOptionHeader-1;

可选头是对文件头的一个补充,文件头主要描述文件的相关信息,可选头主要管理PE文件被操作系统装载时所需要的信息。成员非常多。

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;                    //文件被加载在内存的首选地址。DLL默认值 0x10000000. EXE默认值  0x00400000. 
  DWORD   SectionAlignment;            //节表被装入内存的对齐值 0X1000  即4kb
  DWORD   FileAlignment;                //节表在磁盘文件中的对齐值  有可能0x1000(加载速度快)  或者 0x200(节省空间)
  WORD    MajorOperatingSystemVersion;
  WORD    MinorOperatingSystemVersion;
  WORD    MajorImageVersion;
  WORD    MinorImageVersion;
  WORD    MajorSubsystemVersion;
  WORD    MinorSubsystemVersion;
  DWORD   Win32VersionValue;
  DWORD   SizeOfImage;                  //可执行文件 装入内存后的总大小
  DWORD   SizeOfHeaders;              //整个PE头部的大小  DOS头 PE头 节表的总大小
  DWORD   CheckSum;
  WORD    Subsystem;
  WORD    DllCharacteristics;
  DWORD   SizeOfStackReserve;
  DWORD   SizeOfStackCommit;
  DWORD   SizeOfHeapReserve;
  DWORD   SizeOfHeapCommit;
  DWORD   LoaderFlags;
  DWORD   NumberOfRvaAndSizes;                          //正下边的数组大小为16                                                                          
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];     //数据目录   导入表、导出表、资源、重定位等数据的信息在此
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;           

3.节表:程序的组织按照属性的不同被保存在不同的节中,在PE头部之后就是一个数据结构的字表。描述字表的结构体是IMAGE_SECTION_HEADER,如果PE文件中由N个节,那么节表就是由N个IMAGE_SECTION_HEADER组成的数组。节表中存储了各个节的属性、文件位置、内存位置等相关信息。
(1)IMAGE_SECTION_HEADER:节表中的每一个IMAGE_SECTION_HEADER都存放着可执行文件被映射到内存中所在的位置信息,节的个数由IMAGE_FILE_HEADER给出。
struct IMAGE_SECTION_HEADER{
        BYTE Name[IMAGE_SIZEOF_SHORT_NAME];      //用来保存节表名称 节名通常以"."开头,例如第一节总是:".txt"
        union {
                DWORD PhySicalAdress;
                DWORD VirtualSize;               //实际的节表项大小
        }Misc;
        DWORD VirtualAddress;                     //该节表项载入内存后的相对虚拟地址,这个地址是按照内存对齐的(0x1000,4KB)
        DWORD SizeofRawData;                      //该节表项在磁盘中的大小、通常是对齐后的
        DWORD PointerToRawData;                    //该节表项在磁盘中的偏移地址
        ..................
        DWORD Characterristics;
Characterristics成员和IMAGE_FILE_HEADER中的用法是一样的:
    0x00000020: 该节区含有代码
    0x10000000: 该节区可共享
    0x20000000: 该节区为可执行
    0x40000000: 该节区可读
    0x80000000: 该节区可写

 

将一个exe文件用C32Asm以16进制模式打开后:
    .txt的位置是(节表起始处):0x00000210
    节表结束的位置在 B 处 :  0X00000327
所以整个节表的大小:0X00000327-0x00000210=0x00000117
而一个IMAGE_SECTION_HEADER的大小是40字节:转换为16进制是0x00000028
                           0x00000117/0x00000028=6;
所以共有6个节表,由图看分别是:.txt .rdata .data .idata .rsrc .reloc

4.节表数据:PE文件的真正程序部分就保存在节数据部分。在PE结构中,有几个节表就有几个节表数据,根据节表的属性、地址等信息。程序的数据就分布在节表指定的位置中。
 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!