1.1、PE中有关的基本概念
1.1.1、地址:
PE中涉及的地址有四类,分别是
1>虚拟内存地址(VA)
2>相对虚拟内存地址(RVA)
3>文件偏移地址(FOA)
4>特殊地址
1.1.2、指针
PE数据结构中某个字段储存的值是一个地址,这个字段就是指针
1.1.3、数据目录
为了使PE文件包含的各种数据可扩展,提供稳定的兼容性,所以引入了数据目录
PE中有一个数据结构称为数据目录,其中记录了所有可能的数据类型。
这些类型中,目前已定义的有15中,导出表,导入表,资源表,异常表,属性证书表,重定位表,调试数据,Architecture,Global Ptr,线程局部储存,加载配置表,绑定导入表,IAT,EAT,延迟导入表,和CLR运行时头部
1.1.4、节
为了程序中各种不同数据的相互独立、安全,Windows操作系统在加载可执行程序时,会为这些具有不同属性的数据分别分配标记有不同属性的页面(相同属性的数据可能会被放到同一个页面中),以确保程序运行时的安全。正式基于这个原因,PE中才出现了所谓的节的概念。
节就是存放不同类型数据(代码、数据、常量、资源等)的地方,不同的节具有不同的访问权限。节是PE文件中存代码或数据的基本单元。增加节的数目会增加文件开销,但连接器在连接代码时会有更大的选择余地。一个接中的所有原始数据必须被加载到连续的内存空间中。
从操作系统加载角度来看,节是相同属性的组合。与数据目录不同的事,尽管数据类型不同,分别数据不同的数据目录,但是由于访问属性相同,便被归类到同一个节中。无论这个节最终占用多少个页面,所有页面均会被赋予相同的页面属性(只写,只写,可写,可读,可执行,不可执行等)
1.1.5、对齐
PE中规定了三类对齐:数据在内存中的对齐、数据在文件中的对齐、资源文件中资源数据的对齐
1>内存对齐
由于Windows操作系统对内存属性的设计以页为单位,所以通常节在内存中的对齐单位必须至少是一个页的大小。对32位的WindowsXP来说,此值是4KB(1000h),而对于64位System来说,此值位8KB(2000h)
2>文件对齐
相对于内存对齐,文件对齐没有那么严格,为了提高磁盘利用率,通常,定义的节在文件中的对齐单位要远小于内存对齐的单位;通常以一个物理扇区的大小作为对齐粒度的值,即512byte,HEX表示为200h。(所以数据段、代码段等起始位置都是200h的倍数)
出于节约资源的考虑,操作系统允许节在内存和文件中的对齐尺寸不一致,这就直接造成了PE在文件中和在内存中的大小也会不一致。通常PE在内存中的尺寸要比在文件中的尺寸大。用户可以自己定义这些对齐的值
[注意:如果内存对齐被定义为小鱼操作系统页的大小,则文件对齐和内存对齐的值必须一致!
3>资源数据对齐
资源文件中,资源字节码部分一般要求以汉字(4个byte)方式对齐
1.1.6、Unicode字符串
汇编语言中,Unicode字符串被定义为一个结构体
typedef struct _UNICODE_STRING{
USHORT Length;
USHORT MAximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
1.2、PE文件结构
1.2.1、16位系统下的PE结构
在16位系统下,PE结构可以大致划分为两部分:DOS头和冗余数据
16位系统下,PE的四部分内容被重新组合成两部分——可以在16位系统下运行的DOS头和冗余数据。把Windows下的PE文件存储到DOS系统并运行,他就是DOS系统下的一个EXE文件。
DOS头分为两部分,DOS MZ头和DOS Stud(即执行字节码)。大部分情况下,这些指令实现功能都非常简单,不会涉及重定位信息。在往后的PE头和PE数据可以看做是16位系统下的可执行文件的冗余数据。
1>DOS MZ头
从m_res字段开始包括以下的字段在16位系统上是没有定义的。由于其开始的标志字为“MZ”(Mark Zbikowski,DOS系统的开发者之一),所以称它位“DOS MZ头”(总共64字节)
2>DOS Stub
由于DOS Stub的大小不固定,因此DOS头的大小也是不固定的。DOS Stub部分是该程序在DOS系统下运行的指令字节码。
40-4d处执行以下指令
1.2.2、32位系统下的PE结构
在16位系统中,PE头和PE数据部分被当成是冗余数据;在32位系统中,刚好相反,即DOS头成为冗余数据。在32位系统中尽管DOS头为冗余数据,但也不能把这部分数据从PE结构中除去。因为DOS MZ头中有一个字段非常重要,即IMAGE_DOS_HEADER.elfanew没有它操作系统就定位不到标准的PE头部,可执行程序也就会被操作系统认为是非法的PE映像。
1>定位标准PE头
PE_start = DOS MZ基地址 + IMAGE_DOS_HEADER.e_lfanew。可以看到该位置是以ASCII字符"PE"开头的
2>PE文件结构
在32位系统中,最重要的部分就是PE头和PE数据区
32位系统下的PE被划分为五个部分,DOS MZ头、DOS Stub、PE头、节表、节内容;
DOS MZ头:64个byte
PE头:456个byte(准确的说是不确定的,该结构的实际大小是由字段IMAGE_FILE_HEADER.SizeOfOptionalHeader来确定)
节表:每个节的表述信息是个固定值,共40个byte,节表是由不确定数量的节描述信息组成的,其大小等于节的数量×40,节的数量由IMAGE_FILE_HEADER.NumberOfsections来定义
DOS Stub、节内容:都是大小不确定的
节表是PE中所有节的目录
1.2.3、程序员眼中的PE结构
在程序员眼中,PE文件格式是由许多数据结构组成的
一个标准PE文件一般由四大部分组成
DOS头(IMAGE_DOS_HEADER) PE头(IMAGE_NT_HEADERS) 节表(多个IMAGE_SECTION_HEADER结构) 节内容其中,PE头的结构最为复杂。简单说,PE头包含:
4个字节的标识符号(Signature)
20个字节的基本头信息(IMAGE_FILE_HEADER)
216个字节的扩展头信息(IMAGE_OPTIONAL_HEADER32)
PE文件头部 = DOS头 + PE头 + 节表
PE文件身体 = 节内容
3、PE文件头部解析
1.3.1、DOS MZ头和IMAGE_DOS_HEADER
DOS MZ头的下面是DOS Stub,整个DOS Stub是一个字节快,没有对应的数据结构(注释后的偏移是基于IMAGE_DOS_HEADER头的)
1.3.2、PE头标识Signature
DOS Stub头后紧跟PE头标识符Signature,该标识位于指针IMAGE_DOS_HEADER.e_lfanew指向的位置。内容固定,对应于ASCII码的字符电脑串"PE\0\0"
1.3.3、标准PE头IMAGE_FILE_HEADER
标准PE头紧跟IMAGE_FILE_HEADER(标准PE头),IMAGE_FILE_HEADER = IMAGE_DOS_HEADER.lfanew + 4
1.3.4、扩展PE头IMAGE_OPTIONAL_HEADER32
1.3.5、PE头IMAGE_NT_HEADERS
1.3.6、数据目录项IMAGE_DATA_DIRECTORY电脑
IMAGE_OPTIONAL_HEADER32结构的最后一个字段为DataDirectory,改字段定义了PE文件中出现的所有不同类型的数据的目录信息
总共有16个这样的结构连续排列在一起组成数据目录,数据目录存放了各种类型数据的起始地址和尺寸
1.3.7、节表项IMAGE_SECTION_HEADER
PE头IMAGE_NT_HEADERS后紧跟着节表。
节表由多个节表项(IMAGE_SECTION_HEADER)组成,每个节表项记录了PE中与某个特定的节有关的信息,如节的属性、节的大小、在文件和内存中的起始位置等。
电脑
节表后面就是节的内容。截至节表,PE文件头就结束了。
4、数据结构字段详解
1.4.1、PE头IMAGE_NT_HEADER的字段
OffsetSizeValueInformationIMAGE_NT_HRADER.Signature+0000hDWORD00004550hPE文件标识符,如果修改会导致PE无法加载IMAGE_NT_HRADER.FileHeader+0004hsizeof(IMAGE_FILE_HEADER)IMAGE_FILE_HEADER由于PE扩展自通用COFF规范,所以,改字段在官方文档中称为标准COFF头IMAGE_NT_HRADER.OptionalHeader+0018hsizeof(IMAGE_OPTIONAL_HEADER32)IMAGE_OPTIONAL_HEADER32Windows加载PE文件的大部分特性均在这个结构里呈现。分为两部分,前10个字段属于COFF,用来加载和运行一个可执行文件;后21个字段则通过连接器追加的
1.4.2 标准PE头IMAGE_FILE_HEADER的字段
电脑