PE文件结构详解

来源:互联网 发布:dm软件 编辑:程序博客网 时间:2024/05/18 03:27
PE文件结构详解:1.PE文件时windows下可执行文件的总称,常见的包括OCX,SYS,DLL,EXE等。注意:一个文件是否是PE文件和后缀名无关。2.PE结构:|----------------||   DOS头  ||----------------||  |     |||PE签名  || NT头  |PE文件头|||PE可选头||----------------||节表 ||----------------||.rsrc资源节 ||.text代码节 ||.data数据节  ||----------------|详解:DOS头是用来兼容MS-DOS操作系统的,目的是 1.当这个文件在MS-DOS上运行时提示一段文字;2.指明NT头在文件中的位置NT头包含windows PE文件的主要信息,其中包括一个‘PE’字样的签名,PE文件头(IMAGE_FILE_HEADER)和PE可选头(IMAGE_OPTIONAL_HEADER32)节表:是PE文件后续节的描述,windows根据节表的描述加载每个节。节:每个节实际上是一个容器,可以包含代码、数据等等,每个节可以有独立的内存权限,比如代码节默认有读/执行权限,节的名字和数量可以自己定义,未必是上图中的三个。3.PE文件被加载到内存以后其占用的虚拟地址空间要比在硬盘上占用的空间大一些,这是因为各个节在硬盘上是连续的,而在内存中是按页对齐的注意:因为存在这种对齐,所以在PE结构内部,表示某个位置的地址采用了两种方式:1.物理地址:针对在硬盘上存储文件中的地址,表示距离文件头的偏移;2.虚拟地址(RVA):针对加载到内存以后映象中的地址,表示相对内存映象头的偏移3.VA是进程虚拟内存的绝对地址,RVA指的是从某个基准位置开始的相对地址。计算公式如下:RVA+ImageBase=VA //ImageBase基准地址4.DOS头结构:typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header      WORD   e_magic;         //一个WORD类型,值是一个常数0x5A4D,用文本编辑器查看该值位‘MZ’,可执行文件必须都是'MZ'开头    WORD   e_cblp;                      // Bytes on last page of file      WORD   e_cp;                        // Pages in file      WORD   e_crlc;                      // Relocations      WORD   e_cparhdr;                   // Size of header in paragraphs      WORD   e_minalloc;                  // Minimum extra paragraphs needed      WORD   e_maxalloc;                  // Maximum extra paragraphs needed      WORD   e_ss;                        // Initial (relative) SS value      WORD   e_sp;                        // Initial SP value      WORD   e_csum;                      // Checksum      WORD   e_ip;                        // Initial IP value      WORD   e_cs;                        // Initial (relative) CS value      WORD   e_lfarlc;                    // File address of relocation table      WORD   e_ovno;                      // Overlay number      WORD   e_res[4];                    // Reserved words      WORD   e_oemid;                     // OEM identifier (for e_oeminfo)      WORD   e_oeminfo;                   // OEM information; e_oemid specific      WORD   e_res2[10];                  // Reserved words      LONG   e_lfanew;        //为32位可执行文件扩展的域,用来表示DOS头之后的NT头相对文件起始地址的偏移  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;     5.NT头结构: typedef struct _IMAGE_NT_HEADERS {      DWORD Signature;  //类似于DOS头中的e_magic,其高16位是0,低16是0x4550,用字符表示是'PE‘。    IMAGE_FILE_HEADER FileHeader;  //是PE文件头,C语言的定义是这样的:    IMAGE_OPTIONAL_HEADER32 OptionalHeader;  } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;PE文件头:(定义了PE文件的一些基本信息和属性,这些属性会在PE加载器加载时用到)typedef struct _IMAGE_FILE_HEADER {  WORD    Machine;  //该文件的运行平台,是x86、x64还是I64等等WORD    NumberOfSections;   //该PE文件中有多少个节,也就是节表中的项数。DWORD   TimeDateStamp;  //PE文件的创建时间,一般有连接器填写。DWORD   PointerToSymbolTable;  //COFF文件符号表在文件中的偏移DWORD   NumberOfSymbols;  //符号表的数量WORD    SizeOfOptionalHeader;  //紧随其后的可选头的大小。WORD    Characteristics;  //可执行文件的属性,} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;  PE可选头:typedef struct _IMAGE_OPTIONAL_HEADER {  WORD    Magic;  //表示可选头的类型。0x10b 32位PE可选头  0x20b 64位PE可选头  BYTE    MajorLinkerVersion;  //链接器的版本号BYTE    MinorLinkerVersion; DWORD   SizeOfCode;   //代码段的长度,如果有多个代码段,则是代码段长度的总和。DWORD   SizeOfInitializedData;  //初始化的数据长度。DWORD   SizeOfUninitializedData;  //未初始化的数据长度DWORD   AddressOfEntryPoint;  //程序入口的RVA,对于exe这个地址可以理解为WinMain的RVADWORD   BaseOfCode;  //代码段起始地址的RVADWORD   BaseOfData;  //数据段起始地址的RVA。DWORD   ImageBase;   //映象基地址,这个基地址是建议,对于DLL来说,如果无法加载,系统会自动为其选择地址。DWORD   SectionAlignment;  //节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐 比如0x1000DWORD   FileAlignment;  //节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignmentWORD    MajorOperatingSystemVersion;  //所需操作系统的版本号WORD    MinorOperatingSystemVersion;  WORD    MajorImageVersion;  //映象的版本号WORD    MinorImageVersion;  WORD    MajorSubsystemVersion;  //所需子系统版本号WORD    MinorSubsystemVersion;  DWORD   Win32VersionValue;  //保留,必须为0DWORD   SizeOfImage;  //映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小DWORD   SizeOfHeaders;  //所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的DWORD   CheckSum;  //映象文件的校验和WORD    Subsystem;  //运行该PE文件所需的子系统WORD    DllCharacteristics;  //DLL的文件属性,只对DLL文件有效DWORD   SizeOfStackReserve;  //运行时为每个线程栈保留内存的大小DWORD   SizeOfStackCommit;  //运行时每个线程栈初始占用内存大小DWORD   SizeOfHeapReserve;  //运行时为进程堆保留内存大小DWORD   SizeOfHeapCommit;   //运行时进程堆初始占用内存大小DWORD   LoaderFlags;  //保留,必须为0DWORD   NumberOfRvaAndSizes;  //数据目录的项数,即下面这个数组的项数IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];  //数据目录,这是一个数组} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;  数据目录:(数组中的每一项对应一个特定的数据结构,包括导入表,导出表等等,具体哪一项表示什么内容请查看资料)typedef struct _IMAGE_DATA_DIRECTORY {  DWORD   VirtualAddress; // 是一个RVADWORD   Size;  //是一个大小} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  注意:要从这个数组中获取具体某一项的值可以通过函数获取:PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, //Base:模块基地址。也就是数组地址BOOLEAN MappedAsImage, //MappedAsImage:是否映射为映象。USHORT Directory, //Directory:数据目录项的索引。有定义好的具体的宏PULONG Size//对应数据目录项的大小);//比如Directory=0;则表示导出表大小,如果一个模块导出了函数,那么这个函数会被记录在导出表中,这样通过GetProcAddress函数就能动态获取到函数的地址//导出的时候,可以按照函数名导出(显示导出函数名字中有一些符号比如??@@),也可以按照序号导出,#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory  #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory  #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory  #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory  #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory  #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table  #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory  #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data  #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP  #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory  #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory  #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers  #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table  #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors  #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor   节区结构:#define IMAGE_SIZEOF_SHORT_NAME  8typedef struct _IMAGE_SECTION_HEADER {BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];//节区名称,如“.text”  不足8位则用0填充union {DWORD   PhysicalAddress;//物理地址DWORD   VirtualSize;//实际的节区大小} Misc;DWORD   VirtualAddress;//节区被装载到内存中的RVA地址DWORD   SizeOfRawData;// 在PE文件中对齐后的大小DWORD   PointerToRawData;// 在整个PE文件中的偏移量DWORD   PointerToRelocations;//节区在PE文件中的偏移DWORD   PointerToLinenumbers; // 行号表的偏移(供调试使用)WORD    NumberOfRelocations;// 在PE文件中使用,重定位项数目WORD    NumberOfLinenumbers;// 行号表中行号的数目DWORD   Characteristics;//节区的属性,如可读、可写、可执行等} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;