PE文件:(1)基础知识

基本概念

  PE文件的全称是"Portable Executable File Format",也就是可移植的执行体,目前是Windows平台上的主流可执行文件格式。
  PE文件使用的是一个平面地址空间,所有的代码和数据都合并在了一起,组成了一个完整的文件结构。文件内容被分成了不同的部分区段,区段中包含文件基本信息、数据、代码,各个区段按页大小对齐,每个区段都有自己的属性,如是否可读、可写和可执行。

PE文件结构

  PE文件的整体结构如下:主要分为头部和区段(也称节表)两个部分,头部又包括Dos头、NT头(PE签名、PE文件头、PE可选头)、区段表,区段包括代码段、数据段等。
  接下来,我们就分别介绍各个结构的内容。
在这里插入图片描述

Dos头

  Dos头的结构内容如下:

typedef struct _IMAGE_DOS_HEADER {     
    WORD   e_magic;                     //PE文件标志 MZ
    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];                  
    LONG   e_lfanew;                    //NT头的偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

  在Dos头中,我们只需要关注两个成员,即第一成员e_magic和最后一成员e_lfanew。e_magic是PE文件的前两个字节,也是PE文件的标志,值为4D 5A,即MZ。e_lfanew成员是NT头结构相对于Dos头的偏移值,即文件中的位置。
  使用WinHex查看一个PE文件的结构,可以看到,文件起始两个字节的内容为MZ,Dos头的最后一成员e_lfanew的值为00000118h,即NT头所在的位置为0x118h处。
在这里插入图片描述
  查看文件的0x118位置处,可以查看到NT头起始的PE标志,这就是NT头开始的位置。
在这里插入图片描述

NT头

  NT头在Dos头后面,主要包含PE标志、文件头和可选头三个部分,其中文件头和可选头非常重要,它们包含着PE文件的大部分重要信息。

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;							//PE
    IMAGE_FILE_HEADER FileHeader;				//文件头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;		//可选头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

(1)PE标志

  正如上面我们看到的那样,在Dos头偏移e_lfanew大小的位置处,就是NT头,前四个字节大小就是50 45 00 00,也就是PE。这个值同样可以作为我们检查PE文件合法性的标志。
在这里插入图片描述

(2)文件头

  文件头的结构如下:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;				//运行平台
    WORD    NumberOfSections;		//区段数量
    DWORD   TimeDateStamp;			
    DWORD   PointerToSymbolTable;	
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;	//可选头大小
    WORD    Characteristics;		//文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

  ①Machine:目标文件运行的CPU类型,PE文件可以在多种类型的机器上使用,不同类型机器的指令对应的机器码不同。这里Machine的值可以作为我们判断32位PE或64位PE的标准。
  ②NumberOfSections:区段的数量,区段中一般包含.data,.text,.idata,.tls以及自定义段。
  ③SizeOfOptionalHeader:文件头后面的可选头的大小。
  ④Characteristics:文件属性信息,一般可以通过该值判断PE文件的类型(exe、Dll、sys)。

(3)可选头

  可选头的结构如下:

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;						 //PE32或PE64
    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_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

  ①Magic:通常用来判断PE文件的类型,PE32为0x10B,PE64为0x20B。
  ②SizeOfCode:代码段大小。
  ③AddressOfEntry:程序的入口点。Dll文件的入口点为DllMain(),一般在进程创建和退出或线程的创建和销毁时才会调用;进程的入口点并不直接指向main()或wmain()函数,而是指向运行时的库代码,再由库代码中调用main和wmain函数。
  ④ImageBase:PE文件在被加载到内存时,预计要加载到的内存基址。文件中的所有偏移地址都是以ImageBase为基础进行偏移的。
  ⑤SectionAlignment:PE文件加载到内存中后,各个区段的对齐粒度,这个值一般为0x1000h。
  ⑥FileAlignment:PE文件未加载时,文件中区段的对齐粒度,一般为0x200h。
  ⑦DataDirectory:数据目录,这是一个数组,里面保存着16个IMAGE_DATA_DIRECTORY结构体,每个结构体用来维护一个包含重要信息的表,成员包含一个表的起始地址和表的大小。因为数据目录中有16个成员,也就维护了16张包含重要信息的表。每个IMAGE_DATA_DIRECTORY的结构如下所示:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;		//起始地址
    DWORD   Size;				//大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

区段表

  前面我们知道,PE文件的数据和代码等都是存储在区段中,那么如何获得关于这些区段的信息呢,那么就需要用到我们的区段表了。每个区段都有自己的区段名,区段起始地址,区段大小,区段属性等信息,这些信息都保存在_IMAGE_SECTION_HERADER结构中,一般区段的数量都是由文件头中的NumberOfSections进行指定,由多少个区段就有多少个_IMAGE_SECTION_HERADER结构体。下面是区段表的结构:

typedef struct _IMAGE_SECTION_HEADER
  {
    BYTE Name[IMAGE_SIZEOF_SHORT_NAME];	//区段名
    union{
	DWORD PhysicalAddress;
	DWORD VirtualSize;
    }
    Misc;
    DWORD VirtualAddress;			//区段的内存偏移地址
    DWORD SizeOfRawData;			//区段的大小
    DWORD PointerToRawData;			//区段的文件偏移地址
    DWORD PointerToRelocations;
    DWORD PointerToLinenumbers;
    WORD NumberOfRelocations;
    WORD NumberOfLinenumbers;
    DWORD Characteristics;			//区段的属性
  }

总结

  以上就是PE文件头部的基本信息了,主要包括Dos头,NT头和区段表等结构。下面是我整理的PE文件头部结构图,仅供参考。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值