PE文件结构及在winnt.h中的定义

    PE就是Portable Executable(可移植的执行体)。它是 Win32环境自身所带的执行体文件格式,如:exe文件、DLL 、ocx文件均为PE格式。"portable executable"意味着此文件格式是跨win32平台的 : 即使Windows运行在非IntelCPU上,任何win32平台的PE装载器都能识别和使用该文件格式。当然,移植到不同的CPUPE执行体必然得有一些改变。所有 win32执行体 (除了VxD16位的Dll)都使用PE文件格式,包括NT的内核模式驱动程序(kernel mode drivers)。  

PE文件框架构成

DOS MZ header

DOS stub

PE header

Section table

Section 1

Section 2

Section ...

Section n

 

1 DOS Header

PE文件最开始是一个简单的 DOS MZ header,它是一个 IMAGE_DOS_HEADER 结构。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ Header 之后的 DOS Stub

 

2  DOS Stub

DOS Stub 是一个有效的 DOS 程序。当程序在DOS下运时,输出象 "This program cannot be run in DOS mode" 这样的提示。

 

3 PE Header

紧接着 DOS Stub 的是 PE Header。它是一个 IMAGE_NT_HEADERS 结构。其中包含了很多PE文件被载入内存时需要用到的重要域。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从 DOS MZ header 中找到 PE header 的起始偏移量。

 

4 Section Table

PE Header 接下来的数组结构 Section Table (节表)。如果PE文件里有5个节,那么此 Section Table 结构数组内就有5个成员,每个成员包含对应节的属性、文件偏移量、虚拟偏移量等。

5 Sections

PE文件的真正内容划分成块,称之为sections(节)。Sections 是以其起始位址来排列,而不是以其字母次序来排列。通过节表提供的信息,

节名 作用

.arch 最初的构建信息(Alpha Architecture Information)

.bss 未经初始化的数据

.CRT C运行期只读数据

.data 已经初始化的数据

.debug 调试信息

.didata 延迟输入文件名表

.edata 导出文件名表

.idata 导入文件名表

.pdata 异常信息(Exception Information)

.rdata 只读的初始化数据

.reloc 重定位表信息

.rsrc 资源

.text .exe.dll文件的可执行代码

.tls 线程的本地存储器

.xdata 异常处理表

 

所有常见的PE结构定义在winnt.h头文件中都有。我们一般关心以下几项:

  IMAGE_DOS_HEADER

     IMAGE_NT_HEADERS

  IMAGE_FILE_HEADER

  IMAGE_OPTIONAL_HEADER32

  IMAGE_SECTION_HEADER

  IMAGE_IMPORT_DESCRIPTOR

  IMAGE_EXPORT_DIRECTORY

  IMAGE_RESOURCE_DIRECTORY

 

typedef struct _IMAGE_DOS_HEADER { // DOS .EXE Header

       WORD e_magic;                 // "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;                 // 指向PEHeaderRVA

} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

 

typedef struct _IMAGE_NT_HEADERS {

       DWORD Signature;                      //PE文件头标志 :“PE/0/0”。在开始DOS header的偏移3CH处所指向的地址开始

       IMAGE_FILE_HEADER FileHeader;          //PE文件物理分布的信息

       IMAGE_OPTIONAL_HEADER32 OptionalHeader;     //PE文件逻辑分布的信息

}IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

 

typedef struct _IMAGE_FILE_HEADER {

       WORD      Machine;                          //该文件运行所需要的CPU,对于Intel平台是14Ch

       WORD      NumberOfSections;             //文件的节数目

       DWORD     TimeDateStamp;                 //文件创建日期和时间

       DWORD     PointerToSymbolTable;           //用于调试

       DWORD     NumberOfSymbols;            //符号表中符号个数

       WORD      SizeOfOptionalHeader;             //OptionalHeader 结构大小

       WORD      Characteristics;                    //文件信息标记,区分文件是exe还是dll

}IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 

typedef struct _IMAGE_OPTIONAL_HEADER {

       WORD      Magic;                             //标志字(总是010bh)

       BYTE      MajorLinkerVersion;               //连接器版本号

       BYTE      MinorLinkerVersion;               //

       DWORD     SizeOfCode;                    //代码段大小

       DWORD     SizeOfInitializedData;      //已初始化数据块大小

       DWORD     SizeOfUninitializedData;          //未初始化数据块大小

       DWORD     AddressOfEntryPoint;             //PE装载器准备运行的PE文件的第一个指令的RVA,也就是OEP

       DWORD     BaseOfCode;                   //代码段起始RVA

       DWORD     BaseOfData;                    //数据段起始RVA

       DWORD     ImageBase;                     //PE文件的装载地址

       DWORD     SectionAlignment;               //块对齐

       DWORD     FileAlignment;                     //文件块对齐

       WORD      MajorOperatingSystemVersion;     //所需操作系统版本号

       WORD      MinorOperatingSystemVersion;     //

       WORD      MajorImageVersion;             //用户自定义版本号

       WORD      MinorImageVersion;             //

       WORD      MajorSubsystemVersion;          //win32子系统版本。若PE文件是专门为Win32设计的

       WORD      MinorSubsystemVersion;         //该子系统版本必定是4.0否则对话框不会有3维立体感

       DWORD     Win32VersionValue;             //保留

       DWORD     SizeOfImage;                   //内存中整个PE映像体的尺寸

       DWORD     SizeOfHeaders;                   //所有头+节表的大小

       DWORD     CheckSum;                            //此程序的一个CRC 校验和

       WORD      Subsystem;                     //NT用来识别PE文件属于哪个子系统

       WORD      DllCharacteristics;                //一组标志位,用来指出dll的初始化函数(例如 DllMain)在什么环境下被调用

       DWORD     SizeOfStackReserve;          // 线程初始堆栈的保留大小

       DWORD     SizeOfStackCommit;          // 一开始就被指定给执行线程初始堆栈的内存数量

       DWORD     SizeOfHeapReserve;            // 保留给最初的进程堆(process heap)的虚拟内存数量

       DWORD     SizeOfHeapCommit;            // 一开始就被指定给进程堆(process heap)的内存数量

       DWORD     LoaderFlags;                    // Debug

       DWORD     NumberOfRvaAndSizes;         // DataDirectory(下一个域)数组的成员结构个数

       IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

       //IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如引入地址表等

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

 

typedef struct _IMAGE_DATA_DIRECTORY {

       DWORD     VirtualAddress;            //表的RVA地址

       DWORD     Size;                     //大小

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

 

 

typedef struct _IMAGE_SECTION_HEADER {

       BYTE      Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如“.text

       union {

          DWORD     PhysicalAddress;             //物理地址            

          DWORD     VirtualSize;                     //真实长度

       } Misc;

       DWORD     VirtualAddress;                     //RVA

       DWORD     SizeOfRawData;                 //物理长度

       DWORD     PointerToRawData;             //节基于文件的偏移量

       DWORD     PointerToRelocations;             //重定位的偏移

       DWORD     PointerToLinenumbers;           //行号表的偏移

       WORD      NumberOfRelocations;            //重定位项数目

       WORD      NumberOfLinenumbers;          //行号表的数目

       DWORD     Characteristics;                   //节属性

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

 

RVA 及其相关概念:

RAV(Relative Virual Address)相对虚拟地址,RVA是虚拟空间中到参考点的一段距离。RVA就是类似文件偏移量的东西。当然它是相对虚拟空间里的一个地址,而不是文件头部。举例说明,如果PE文件装入虚拟地址(VA)空间的400000h处,且进程从虚址401000h开始执行,我们可以说进程执行起始地址在RVA 1000h。每个RVA都是相对于模块的起始VA的。虛址(VA)0x401000h - 基址(BA)0x400000h = RVA 0x1464h。基址(Base Address)用来描述被映射到内存中的exe或者dll的起始位置。

 

PE文件格式使用RVA是为了减少PE装载器的负担,因为每个模块都有可能被重载到任何虚拟地址空间,如果让PE装载器修正每个重定位项,这肯定是个梦魇。相反,如果所有重定位项都使用RVA,那么PE装载器就不必操心那些东西了: 它只要将整个模块重定位到新的起始VA。这就象相对路径和绝对路径的概念: RVA类似相对路径,VA就象绝对路径。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值