一、PE的基本概念

PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。
    认识PE文件不是作为单一内存映射文件被装入内存是很重要的。Windows加载器(又称PE加载器)遍历PE文件并决定文件的哪一部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址中。PE文件的结构在磁盘和内存中是基本一样的,但在装入内存中时又不是完全复制。Windows加载器会决定加载哪些部分,哪些部分不需要加载。而且由于磁盘对齐与内存对齐的不一致,加载到内存的PE文件与磁盘上的PE文件各个部分的分布都会有差异。

二、PE结构分析

图1:PE文件的框架结构
PE文件至少包含两个段,即数据段和代码段。Windows NT 的应用程序有9个预定义的段,分别为 .text 、.bss 、.rdata 、.data 、.pdata 和.debug 段,这些段并不是都是必须的,当然,也可以根据需要定义更多的段(比如一些加壳程序)。
在应用程序中最常出现的段有以下6种:
.执行代码段,通常  .text (Microsoft)或 CODE(Borland)命名;
.数据段,通常以 .data 、.rdata 或 .bss(Microsoft)、DATA(Borland)命名;
.资源段,通常以 .rsrc命名;
.导出表,通常以 .edata命名;
.导入表,通常以 .idata命名;
.调试信息段,通常以 .debug命名;

1、DOS头结构

所有的PE文件都是以一个64字节的DOS头开始。这个DOS头只是为了兼容早期的DOS操作系统。DOS头的结构如下:

typedef struct IMAGE_DOS_HEADER{WORD e_magic;            //DOS头的标识,为4Dh和5Ah。分别为字母MZWORD 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];DWORD e_lfanew;             //指向IMAGE_NT_HEADERS的所在
}IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

DOS头后跟一个DOS Stub数据,是链接器链接执行文件的时候加入的部分数据,一般是“This program must be run under Microsoft Windows”。这个可以通过修改链接器的设置来修改成自己定义的数据。

2、PE头文件

紧跟着DOS stub的时PE头文件(PE Header)。PE Header是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,其中包含许多PE装载器用到的重要字段。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从IMAGE_DOS_HEADER结构中的e_lfanew字段里找到PE Header的起始偏移量,加上基址得到PE文件头的指针。
PNTHeader = ImageBase + dosHeader->e_lfanew
PE头的数据结构被定义为IMAGE_NT_HEADERS。包含三部分,其结构如下:

typedef struct IMAGE_NT_HEADERS{DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
}IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS; 

Signature字段:PE头的标识。双字结构。为50h, 45h, 00h, 00h. 即“PE\0\0”。
FileHeader字段:IMAGE_FILE_HEADER(映像头文件)结构包含了文件的物理层信息及文件属性。共20字节的数据,其结构如下:

typedef struct _IMAGE_FILE_HEADER {WORD    Machine;                  //运行平台WORD    NumberOfSections;         //文件的区块数目DWORD   TimeDateStamp;             //文件创建日期和时间DWORD   PointerToSymbolTable;        //指向符号表(用于调试)DWORD   NumberOfSymbols;         //符号表中符号个数(用于调试)WORD    SizeOfOptionalHeader;     //IMAGE_OPTIONAL_HEADER32结构大小WORD    Characteristics;           //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

OptionalHeader字段:IMAGE_OPTIONAL_HEADER(可选映像头)是一个可选的机构,实际上IMAGE_FILE_HEADER结构不足以定义PE文件属性,因此可选映像头中定义了更多的数据。总共224个字节,最后128个字节为数据目录(Data Directory),其结构如下:

typedef struct _IMAGE_OPTIONAL_HEADER {WORD    Magic;                            //标志字BYTE    MajorLinkerVersion;                //链接器主版本号BYTE    MinorLinkerVersion;                //链接器次版本号DWORD   SizeOfCode;                        //所有含有代码表的总大小DWORD   SizeOfInitializedData;         //所有初始化数据表总大小DWORD   SizeOfUninitializedData;       //所有未初始化数据表总大小DWORD   AddressOfEntryPoint;          //程序执行入口RVADWORD   BaseOfCode;                      //代码表其实RVADWORD   BaseOfData;                       //数据表其实RVADWORD   ImageBase;                        //程序默认装入基地址DWORD   SectionAlignment;                //内存中表的对齐值DWORD   FileAlignment;                    //文件中表的对齐值WORD    MajorOperatingSystemVersion;  //操作系统主版本号WORD    MinorOperatingSystemVersion;  //操作系统次版本号WORD    MajorImageVersion;                //用户自定义主版本号WORD    MinorImageVersion;               //用户自定义次版本号WORD    MajorSubsystemVersion;           //所需要子系统主版本号WORD    MinorSubsystemVersion;          //所需要子系统次版本号DWORD   Win32VersionValue;              //保留,通常设置为0DWORD   SizeOfImage;                  //映像装入内存后的总大小DWORD   SizeOfHeaders;                 //DOS头、PE头、区块表总大小DWORD   CheckSum;                      //映像校验和WORD    Subsystem;                       //文件子系统WORD    DllCharacteristics;              //显示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;

DataDirectory是OptionalHeader的最后128个字节,也是IMAGE_NT_HEADERS的最后一部分数据。它由16个IMAGE_DATA_DIRECTORY结构组成的数组构成,指向输出表、输入表、资源块等数据。IMAGE_DATA_DIRECTORY的结构如下:

typedef struct _IMAGE_DATA_DIRECTORY {DWORD   VirtualAddress;            //数据块的起始RVADWORD   Size;                    //数据块的长度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

数据表成员结构如下:

#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_COPYRIGHT       7   // (X86 usage)
#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

用LordPE查看EXE文件的数据目录表:

3、区块表

3.1 区块结构

在PE文件头与原始数据之间存在一个区块表(Section Table),它是一个IMAGE_SECTION_HEADER结构数组,区块表包含每个块在映像中的信息(如位置、长度、属性),分别指向不同的区块实体。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束,所以节表中总的IMAGE_SECTION_HEADER结构数量等于节的数量加一。另外,节表中 IMAGE_SECTION_HEADER 结构的总数总是由PE文件头 IMAGE_NT_HEADERS->FileHeader.NumberOfSections 字段来指定的。

IMAGE_SECTION_HEADER结构定义如下:

typedef struct _IMAGE_SECTION_HEADER {Name                       //8个字节的块名union                      {DWORD PhysicalAddress;DWORD VirtualSize;} Misc;                     //区块尺寸</span>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;

(1)Name:这是一个8位的ASCII(不是Unicode内码),用来定义块名,多数块名以,开始(如.Text),这个实际上不是必需的,注意如果块名超过了8个字节,则没有最后面的终止标志NULL字节,带有$的区块的名字会从编译器里将带有$的相同名字的区块被按字母顺序合并。
(2) VirtualSize:指出实际的,被使用的区块大小,是区块在没有对齐处理前的实际大小.如果VirtualSize > SizeOfRawData,那么SizeOfRawData是可执行文件初始化数据的大小(SizeOfRawData – VirtualSize)的字节用0来填充。这个字段在OBJ文件中被设为0。
(3)VirtualAddress:该块时装载到内存中的RVA,注意这个地址是按内存页对齐的,她总是SectionAlignment的整数倍,在工具中第一个块默认RVA为1000,在OBJ中为0。
(4)SizeofRawData:该块在磁盘中所占的大小,在可执行文件中,该字段包括经过FileAlignment调整后块的长度。例如FileAlignment的大小为200h,如果VirtualSize中的块长度为19Ah个字节,这一块保存的长度为200h个字节。
(5) PointerToRawData:该块是在磁盘文件中的偏移,程序编译或汇编后生成原始数据,这个字段用于给出原始数据块在文件的偏移,如果程序自装载PE或COFF文件(而不是由OS装载),这种情况,必须完全使用线性映像方法装入文件,需要在该块处找到块的数据。
(6) PointerToRelocations 在PE中无意义
(7) PointerToLinenumbers 行号表在文件中的偏移值,文件调试的信息
(8) NumberOfRelocations 在PE中无意义
(9) NumberOfLinenumbers 该块在行号表中的行号数目
(10) Characteristics 块属性,(如代码/数据/可读/可写)的标志,这个值可通过链接器的/SECTION选项设置.下面是比较重要的标志:

通常,区块中的数据在逻辑上是关联的。PE 文件一般至少都会有两个区块:一个是代码块,另一个是数据块。每一个区块都需要有一个截然不同的名字,这个名字主要是用来表达区块的用途。例如有一个区块叫.rdata,表明他是一个只读区块。注意:区块在映像中是按起始地址(RVA)来排列的,而不是按字母表顺序。另外,使用区块名字只是人们为了认识和编程的方便,而对操作系统来说这些是无关紧要的。微软给这些区块取了个有特色的名字,但这不是必须的。当编程从PE 文件中读取需要的内容时,如输入表、输出表,不能以区块名字作为参考,正确的方法是按照数据目录表中的字段来进行定位。
区块名称以及意义:

每个区块的名称都是唯一的,不能有同名的两个区块。但事实上节的名称不代表任何含义,他的存在仅仅是为了正规统一编程的时候方便程序员查看方便而设置的一个标记而已。所以将包含代码的区块命名为“.Data” 或者说将包含数据的区块命名为“.Code” 都是合法的。当我们要从PE 文件中读取需要的区块时候,不能以区块的名称作为定位的标准和依据,正确的方法是按照 IMAGE_OPTIONAL_HEADER32 结构中的数据目录字段结合进行定位。
在Visual C++中,用#pragma来声明,告诉编译器插入数据到一个区块内:
#pragma data_seg("MY_DATA")
链接器的一个有趣特征就是能够合并区块。如果两个区块有相似、一致性的属性,那么它们在链接的时候能被合并成一个单一的区块。这取决于是否开启编译器的 /merge 开关。下面的链接器选项将.rdata与.text区块合并为一个.text区块:
/MERGE : .rdata = .text
注意:当合并区块时,因为这没有什么硬性规定。例如,把.rdata合并到.text里不会有什么问题,但是不应该将.rsrc、.reloc或者.pdata合并到其它的区块里。

3.2 区块的对齐

区块大小是要对齐的,有两种对齐值,一种用于磁盘文件内,另一种用于内存中。PE文件头指出了这两个值,他们可以不同。PE 文件头里边的FileAligment 定义了磁盘区块的对齐值。每一个区块从对齐值的倍数的偏移位置开始存放。而区块的实际代码或数据的大小不一定刚好是这么多,所以在多余的地方一般以00h 来填充,这就是区块间的间隙。例如,在PE文件中,一个典型的对齐值是200h ,这样,每个区块都将从200h
 的倍数的文件偏移位置开始,假设第一个区块在400h 处,长度为90h,那么从文件400h 到490h 为这一区块的内容,而由于文件的对齐值是200h,所以为了使这一区块的长度为FileAlignment 的整数倍,490h 到 600h 这一个区间都会被00h 填充,这段空间称为区块间隙,下一个区块的开始地址为600h 。
    PE 文件头里边的SectionAligment 定义了内存中区块的对齐值。PE 文件被映射到内存中时,区块总是至少从一个页边界开始。一般在X86 系列的CPU 中,页是按4KB(1000h)来排列的;在IA-64 上,是按8KB(2000h)来排列的。所以在X86 系统中,PE文件区块的内存对齐值一般等于 1000h,每个区块按1000h 的倍数在内存中存放。

3.3 文件偏移与RVA

由于一些PE文件为减少体积,磁盘对齐值不是一个内存页 1000h,而是 200h,当这类文件被映射到内存后,同一数据相对于文件头的偏移量在内存中和磁盘文件中是不同的,这样就存在着文件偏移地址与虚拟地址的转换问题。

由上图可以看出,文件被映射到内存,DOS文件头,PE文件头,区块表的偏移位置和大小都没有发生改变。而各区块映射到内存后,起偏移位置发生了改变。
转换需要前面提到的一个公式:设:ΔK为相对虚拟地址RVA与文件偏移地址File Offset的差值
VA = ImageBase + RVA
File Offset = RVA - ΔK
File Offset = VA - ImageBase - ΔK

原文:https://blog.csdn.net/shitdbg/article/details/49734495

PE文件解析-文件头与整体介绍相关推荐

  1. 15.windbg-dds、dps、dqs、PE文件解析

    以下默认windbg加载calc程序 d*s dds.dps和dqs命令显示给定范围内存的内容,它们是把内存区域转储出来,并把内存中每个元素都视为一个符号对其进行解析,dds是四字节视为一个符号,dq ...

  2. java 文件头_常用文件的文件头(附JAVA测试类)

    1. MIDI (mid),文件头:4D546864 2. JPEG (jpg),文件头:FFD8FF 3. PNG (png),文件头:89504E47 4. GIF (gif),文件头:47494 ...

  3. Shapefile文件读取-文件头

    1 介绍 在Shapefile文件格式介绍一文中我们介绍了shapefile文件的结构组成,本文主要介绍如何读取shapefile文件头部分,使用的语言是c++. 2 文件头结构 Shapefile文 ...

  4. 各类文件的文件头标志[转]

    各类文件的文件头标志 参见  http://www.garykessler.net/library/file_sigs.html 扩展名 文件头标识(HEX) 文件描述 123 00 00 1A 00 ...

  5. html的文件头标志,各类文件的文件头标志.docx

    各类文件的文件头标志.docx 还剩 12页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保! 内容要点: 扩展名 文件头标识( ...

  6. html的文件头标志,各类文件的文件头标志.doc

    各类文件的文件头标志 1.从Ultra-edit-32中提取出来的 JPEG (jpg),文件头:FFD8FF PNG (png),文件头:89504E47 GIF (gif),文件头TIFF (ti ...

  7. Apache 文件解析漏洞SSRF漏洞原理介绍及代码

    Apache 文件解析漏洞 SSRF漏洞原理介绍及代码 1. Apache 环境简介 2. Apache 解析漏洞介绍 3. 解析漏洞利用演示 4.利用场景介绍 1. Apache 环境简介 ​ Ap ...

  8. 编写PE文件解析器(三)

    下面有几个表网上资料比较少,因为几乎用不到,我查文档写写吧,这篇写得比较久很抱歉. 7.IMAGE_DIRECTORY_ENTRY_EXCEPTION[异常处理表] CPU特定的并且基于表的异常处理. ...

  9. 图解VC++版PE文件解析器源码分析

    该源码下载自 http://download.csdn.net/download/witch_soya/4979587 1 Understand 分析的图表 2 PE结构解析的主要代码简要分析 首先看 ...

最新文章

  1. kwargs.pop是什么意思
  2. MySQL show processlist说明
  3. 英伟达再发边缘AI计算设备:仅信用卡大小,性能比TX2强15倍
  4. java split 逗号_咦,Java拆分个字符串都这么讲究
  5. 关于target=标签
  6. 在Vue中遇到的各种坑 及性能提升
  7. php动态网页转换成html,怎么把动态的php文件转换成静态的html文件,html文件是php文件…...
  8. 2021算法竞赛入门班第七节课【图论】练习题
  9. ElementUI中el-radio-group使用v-model绑定是属性为String字符串类型时不回显数据
  10. Openshift 4.4 静态 IP 离线安装系列:初始安装
  11. mysql语句编码_使用SQL语句操作MYSQL字符编码
  12. 信息学奥赛一本通(1249:Lake Counting)
  13. instanceof 和 构造函数
  14. Jmeter系列之参数化
  15. 华为交换机初始化和配置SSH和TELNET远程登录方法
  16. Python爬虫之头条采集免费方法
  17. 光滑曲线_光滑流形(4)
  18. 微信每日早安推送,快来给你女友做爱心提醒吧,自定义推送名称,企业号通知非订阅号测试号,后台python,精简无第三方网站注册、无第三方接口,无基础快速上不了手
  19. Django自动化测试平台项目案例
  20. Python解析SWAN气象雷达数据--(解析、生成ASCII、Image、netCDF)

热门文章

  1. 企查查、天眼查、启信宝API怎么批量操作调用,API接口应用场景。
  2. 文件解析 (JSON解析)
  3. 湖南师大教科院民办幼儿园园长规范办园培训班在湖南智慧教育装备展示体验中心参观学习
  4. CSS3与页面布局——Box Model、边距折叠、内联与块标签、CSSReset
  5. ORB_SLAM2代码阅读及总结使用(二)
  6. [聊聊] (解读)5模13频、5模17频、全网通、双4G,到底是什么?
  7. 5行代码,你也可以给心仪的小姐制作漫画头像
  8. 深度了解 JavaScript 中 三目运算符
  9. 大家都是怎么看待STO的?
  10. Windows10系统最强大版本是哪个