• PE文件详解1——PE文件头部解析


    参考书籍:《WindowsPE文件权威指南》
    MSDN中winnt.h是PE文件定义的最终决定者。
    EXE文件与DLL文件之间的区别完全是语义上的,二者PE结构完全相同。唯一区别用一个字段标示处这个文件是exe还是dll。许多DLL扩展,如OCX控件,控制面板等都是DLL,它们有一样的实体。

    64位的Windows只是对PE格式做了一些简单的修饰,新格式叫PE32+。没有新的结构加进去,其余的改变只是简单地将以前的32位字段扩展为64位字段。

    1.PE文件基本结构:


    PE文件的头分为DOS头、NT头、节头。注意,这是本人的分法。这样分法会更加合理,更易理解。因为这三个部分正好构成SizeOfHeaders所指的范围,所以将它们合为“头”。

    2.文件头

    2.1 DOS头

    用记事本打开任何一个镜像文件,其头2个字节必为字符串“MZ”,这是Mark Zbikowski的姓名缩写,他是最初的MS-DOS设计者之一。然后是一些在MS-DOS下的一些参数,这些参数是在MS-DOS下运行该程序时要用到的。在这些参数的末尾也就是文件的偏移0x3C(第60字节)处是是一个4字节的PE文件签名的偏移地址。该地址有一个专用名称叫做“E_lfanew”。这个签名是“PE00”(字母“P”和“E”后跟着两个空字节)。紧跟着E_lfanew的是一个MS-DOS程序。那是一个运行于MS-DOS下的合法应用程序。当可执行文件(一般指exe、com文件)运行于MS-DOS下时,这个程序显示“This program cannot be run in DOS mode(此程序不能在DOS模式下运行)”这条消息。用户也可以自己更改该程序,有些还原软件就是这么干的。同时,有些程序既能运行于DOS又能运行于Windows下就是这个原因。Notepad.exe整个DOS头大小为224个字节,大部分不能在DOS下运行的Win32文件都是这个值。MS-DOS程序是可有可无的,如果你想使文件大小尽可能的小可以省掉MS-DOS程序,同时把前面的参数都清0。

    2.1.1 DOS头结构体

    typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
        WORD   e_magic;                     // Magic number                             //EXE标志 "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;                  // Maximum extra paragraphs needed          //所需的最大附加段
        WORD   e_ss;                        // Initial (relative) SS value              //初始的SS值
        WORD   e_sp;                        // Initial SP value			    //初始的SP值	
        WORD   e_csum;                      // Checksum				    //补码校检值
        WORD   e_ip;                        // Initial IP value			    //初始的IP值
        WORD   e_cs;                        // Initial (relative) CS value		    //初始的CS值
        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)           //OEM标识符
        WORD   e_oeminfo;                   // OEM information; e_oemid specific        //OEM信息
        WORD   e_res2[10];                  // Reserved words                           //保留字
        LONG   e_lfanew;                    // File address of new exe header           //PE头相对于文件的偏移地址
      } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
    注意:DOS头后是一个整个DOS Stub字节块,其内容随使用的链接器的不同而不同,PE中并没有与之相关的结构

    2.2 NT头

    紧跟着PE文件签名之后,是NT头。NT头分为三个部分,1.PE文件标识2.PE文件头3.PE扩展头


    2.2.1 NT头结构体

    typedef struct _IMAGE_NT_HEADERS {
        DWORD Signature;								//PE文件标识 “PE”
        IMAGE_FILE_HEADER FileHeader;						//PE文件头
        IMAGE_OPTIONAL_HEADER32 OptionalHeader;					//PE扩展头
    } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

    (1)PE文件标准PE头(用于判断PE文件是exe还是dll,得到节的总量)

    typedef struct _IMAGE_FILE_HEADER {
        WORD    Machine;                         //运行平台 
        WORD    NumberOfSections;                //PE中节的数量
        DWORD   TimeDateStamp;           <span style="white-space:pre">	</span>     //文件创建日期和时间
        DWORD   PointerToSymbolTable;<span style="white-space:pre">	</span>     //指向符号表(用于调试)
        DWORD   NumberOfSymbols;	 <span style="white-space:pre">	</span>     //符号表中的符号数量(用于调试)
        WORD    SizeOfOptionalHeader;  <span style="white-space:pre">	</span>     //扩展头结构长度
        WORD    Characteristics;		     //文件属性
    } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

    (2)PE扩展头(虽然是扩展头,但更像真正的PE头,非常重要,包含各种属性)

    typedef struct _IMAGE_OPTIONAL_HEADER {
        //
        // Standard fields.
        //
    
        WORD    Magic;                               //魔幻字  0x10b 32位 0x20b 64位
        BYTE    MajorLinkerVersion;             <span style="white-space:pre">	</span> //链接器版本号
        BYTE    MinorLinkerVersion;		   <span style="white-space:pre">	</span> //链接器版本号
        DWORD   SizeOfCode;			  <span style="white-space:pre">	</span> //所有含代码节的总大小
        DWORD   SizeOfInitializedData;	   <span style="white-space:pre">	</span> //初始化的数据长度。
        DWORD   SizeOfUninitializedData; <span style="white-space:pre">		</span> //未初始化的数据长度。
        DWORD   AddressOfEntryPoint;     <span style="white-space:pre">		</span> //程序入口的RVA
        DWORD   BaseOfCode;		   <span style="white-space:pre">		</span> //代码段起始地址的RVA
        DWORD   BaseOfData;			    <span style="white-space:pre">	</span> //数据段起始地址的RVA
    
        //
        // NT additional fields.
        //
    
        DWORD   ImageBase;				 //映象(加载到内存中的PE文件)的基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址。
        DWORD   SectionAlignment;			 //内存中节对齐粒度,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0。
        DWORD   FileAlignment;			 //文件中节对齐粒度,SectionAlignment必须大于或等于FileAlignment。
        WORD    MajorOperatingSystemVersion;	 //所需操作系统的版本号
        WORD    MinorOperatingSystemVersion;	 //
        WORD    MajorImageVersion;			 //映象的版本号,这个是开发者自己指定的,由连接器填写。
        WORD    MinorImageVersion;			 //
        WORD    MajorSubsystemVersion;		 //所需子系统版本号
        WORD    MinorSubsystemVersion;		 //
        DWORD   Win32VersionValue;			 //保留,必须为0。
        DWORD   SizeOfImage;			 //映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。
        DWORD   SizeOfHeaders;			 //所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。
        DWORD   CheckSum;				 //映象文件的校验和。
        WORD    Subsystem;				 //运行该PE文件所需的子系统
        WORD    DllCharacteristics;			 //DLL的文件属性,只对DLL文件有效
        DWORD   SizeOfStackReserve;			 //运行时为每个线程栈保留内存的大小。
        DWORD   SizeOfStackCommit;			 //运行时为每个线程栈保留内存的大小。
        DWORD   SizeOfHeapReserve;                   //运行时为进程堆保留内存大小。
        DWORD   SizeOfHeapCommit;			 //运行时进程堆初始占用内存大小。
        DWORD   LoaderFlags;			 //保留,必须为0。
        DWORD   NumberOfRvaAndSizes;		 //数据目录的项数
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录,这是一个数组,定义了PE文件中所有出现的不同类型的数据目录信息
    } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

    2.3 数据目录项(节头)


    注:总的数据目录一共由16个相同的_IMAGE_DATA_DIRECTORY组成。

    2.3.1 结构体

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;								//数据的起始RVA			
        DWORD   Size;									//数据块的大小
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

    2.3.2 节表项的数据结构

    typedef struct _IMAGE_SECTION_HEADER {
        BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];            //8个字节的节名
        union {                                           //
                DWORD   PhysicalAddress;		      //节区大小
                DWORD   VirtualSize;
        } Misc;
        DWORD   VirtualAddress;			      //节区RVA地址
        DWORD   SizeOfRawData;			      //在文件中对齐后的地址
        DWORD   PointerToRawData;			      //在文件中的偏移
        DWORD   PointerToRelocations;		      //在OBJ文件中使用
        DWORD   PointerToLinenumbers;		      //行号表的位置(调试用)
        WORD    NumberOfRelocations;		      //OBJ文件中使用
        WORD    NumberOfLinenumbers;		      //行号表中行号的数量
        DWORD   Characteristics;			      //节的属性
    } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


  • 相关阅读:
    Elasticsearch-PHP 索引操作2
    Elasticsearch-PHP 索引操作
    Linux系统编程1_C标准函数库和系统调用
    Git命令_git commit
    Git命令_git log
    Linux27_配置samba
    计算机网络12_整理
    理解操作系统8——字符设备与块设备
    网站开发基础知识3_会话和cookie
    网站开发基础知识2_前后端分离
  • 原文地址:https://www.cnblogs.com/Toring/p/6628303.html
Copyright © 2020-2023  润新知