可执行文件(executable file)指的是可以由操作系统进行加载执行的文件。在不同的操作系统环境下,可执行程序的呈现方式不一样。
Windows操作系统下,可执行文件格式PE(Portable Executable)可以是“.exe文件”、“.sys文件”、“.com文件”等类型文件。
Linux操作系统下,可执行文件格式为ELF即Executable and Linkable Format。这篇文章主要用Windows的PE文件进行说明。
我们自己判断或在自动化扫描过程中如何才确认文件是否为PE文件呢?
如果只是通过后缀名去判断的话,任何文件只要改个名字就是了,这样一来错误率就比较高,这个时候PE文件的指纹就能帮助我们识别文件是否为PE文件了。
那么如何识别一个文件是否为PE文件呢?
这里我们用Windows自带播放器wmplayer.exe来举例。
我们用二进制文本编辑工具进行查看wmplayer.exe。
首先它的前两个字节是MZ,然后查看3C位置的数值,目前是E0。
然后找到E0指向的位置,正好是PE。
这样一来,我们基本可以判断文件的格式为PE了。至于为什么是从3C的位置指向E0,然后确认是PE呢?那就要了解PE文件的结构了。
3.1 PE文件整体结构
接下来我们看看PE文件的结构。PE文件主要是四部分组成:DOS头、PE文件头、节表、节数据。
上面的定义我们可以参考Windows Kits中的winnt.h的定义。
此处定义了dos头的结构体,我们研究PE文件,其实就是在研究这些结构体。
3.2 DOS头
DOS部分存在是历史问题,windows是从DOS环境来的,是为了适配16位的DOS系统的。DOS部分分为DOS MZ头和DOS块。
从DOS头的结构体计算DOS头长度,其中有两个数组长度为10和4,总共正好64个字节。我们目前只关注e_magic和e_lfanew,其中e_magic为MZ标识,e_lfanew是指向下个节的地址,由于DOS块的大小是不一定的,DOS块是链接器插入自己的数据用的。我们可以通过e_lfanew来查看PE头的开始位置,就是上面我们查看PE时用3C的位置查看PE头的原因。
这样我们就可以知道整个DOS部分的结构了。
3.3 PE文件头
PE文件头分为三部分,PE标识、标准PE头、扩展PE头。
3.3.1 PE标识
PE标识就是四个字节的数据,这块是为了校验文件的格式,是不能修改的。
3.3.2 标准PE头
标准PE头是20个字节的长度,分别是:
其中,Machine是表示此程序运行在什么机器上,也就是判断文件是32位还是64位的。014c代表的是32位。64位的的话是8664。
NumberOfSections表示节的数量,此处的值为0006,代表的是有6个节。
SizeOfOptionalHeader表示扩展PE头的大小,大小为E0也就是224个字节,这个值不是一定的,是可以改变的。
Characteristics表示文件属性,0102代表的是只在32位上运行的可执行文件。
我们关心以下几个成员的含义。
ImageBase:内存的镜像地址。每个程序在内存中都有4GB的内存,程序在内存中从那块开始展开,是通过这个值来决定的。
AddressOfEntryPoint:程序入口。此处是一个相对ImageBase的偏移量。
SectionAlignment:内存对齐。
FileAlignment:文件对齐。
SizeOfImage:内存中整个PE文件映射的大小,此处一定是内存对齐的整数倍。
SizeOfHeaders:所有头加节表按照文件对齐后的大小。头在文件和内存中是没有变化的。
IMAGE_NUMBEROF_DIRECTORY_ENTRIES:所有的导入表、导出表等表的信息都在这里面,感兴趣的话可以自己去了解一下。这些表是学写PE的关键。
3.4 节表
每个节表的结构体如下,一个节表是40个字节。
第一个节的信息
IMAGE_SIZEOF_SHORT_NAME:当前节的名字,8个字节。
Misc:这个联合体表示在没有对齐前的真实大小。
VirtualAddress:在内存中的相对ImageBase的偏移地址。
SizeOfRawData:节在文件对齐后的大小。
了解PE文件格式,对于日常工作有很大的帮助。对于研究病毒与反病毒、加壳和脱壳的人来说,这块是必须要了解的。对于一般的程序的头信息,我们也没必要一个字节一个字节去找,可以通过工具直接获取。但了解了整个PE格式,我们才能了解工具所表达的参数的意义。
END
原文始发于微信公众号(联想全球安全实验室):PE文件格式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论