Winhex在PE文件格式剖析中的教学应用
2014-12-25姚罡
姚 罡
(桂林电子科技大学,广西 桂林541004)
0 引言
PE(Portable Execute)文件被称为可移植的执行体,是微软制定的一种文件标准,常见的EXE、DLL、OCX、SYS、COM都是PE文件,在Windows操作系统中举足轻重。在加密与解密、软件汉化、逆向工程、反病毒等安全领域都涉及到PE文件的应用。
但PE文件格式定义复杂,难以理解,导致入门较难,学生很难深入去研究。通过多媒体教学,课堂上直接利用Winhex打开任意EXE文件或DLL文件,从原始的十六进制找到我们感兴趣的值,可以明了地分析出Windows操作系统如何载入EXE文件及DLL文件的工作机制,将抽象的格式定义以直观、互动的方式展示给学生,提高学生的学习兴趣。
本文介绍了如何应用Winhex理解PE文件的教学过程,以研究输入表和输入地址表为例,并对两者的运行机制进行分析,使得学生理解Windows操作系统如何载入PE文件,以及可执行文件如何调用API函数。
1 Winhex软件简介
winhex是一款以十六进制编辑器为核心的数据处理高级工具,可以直接打开硬盘上的文件和内存,并以十六进制形式显示数据,可以实现数据恢复、低级数据处理等强大功能,可分析RAW格式原始数据镜像文件中的完整目录结构。Winhex可用于分析静态PE文件(硬盘上)和动态PE文件(调入内存后),并通过静、动态PE文件的比较,重点讨论两个非常重要的数据结构:输入表和输入地址表,从而掌握Windows操作系统中PE文件的工作机制。
2 PE文件格式分析
通过理解PE文件格式中的字段,可以逐步理解PE文件的结构及其原理。PE文件格式如图1所示。所有的PE文件都是从MS-DOS头开始,该头部后面的是PE头,其位置可从MS-DOS头中的一个e_lfanew字段的值得到,PE头是由PE Signature、IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER构成,在IMAGE_NT_HEADERS头部后面是节表(Section Table),节表是一个数组,数组中的每一个元素用来描述后继每一节的信息,如节名、节大小、节偏移、节属性等。在节表后面是具体的节,如代码节(.text)、数据节(.data)等,PE文件中非常重要的输入表、输出表等就存在上述节中。
图1 PE文件格式
2.1 判断合法PE文件
利用Winhex打开一个可执行文件thunder.exe(迅雷安装程序),通过对字段值的分析,即DOS头的“MZ”和PE头的“PE”这两个标志可以初步判断当前程序是否是目标是PE文件。也就是通过IMAGE_DOS_HEADER结构来识别一个合法的DOS头,可以看到文件起始为0X4D5AH(“MZ”),接着通过该结构的e_lfanew(偏移3CH,32bits)的值是0x00000118H(注意字节序)即为PE头开始的偏移,定位到该偏移,其值为PE文件开始的标志0X00004550H(“PE ”),即可初步确定该文件是一个合法的PE文件。
2.2 输入表(Import Table)和输入地址表(IAT,Import Address Table)
数据目录表(Data Directory)中有两项:IMAGE_DIRECTORY_ENTRY_IMPORT(输入表,索引为1)和IMAGE_DIRECTORY_ENTRY_IAT(输入地址表,索引为12)。
PE文件用到来自其他EXE或DLL的函数称为输入,输入表中保存的是函数名和其驻留的DLL名等动态链接所需要的信息。当Windows PE装载器载入该PE文件时,检查输入表并将相关的DLL/EXE映射到其地址空间,定位要用到的由这些EXE或DLL提供的函数,从而可以使用这些函数的地址(这些地址保存在输入地址表),即可调用这些的函数。
PE文件有两种状态,静态(尚未运行)和动态(调入内存运行)。在这两种状态下,输入地址表的内容是不相同的,代表的含义也不同,只有充分理解含义的变化才能理解输入的概念,初步掌握PE文件的精髓。
输入表是一个IMAGE_IMPORT_DESCRIPTOR(IID)数据结构的数组,数组长度不定,但它最后是以一个全为NULL(0)的IID作为结束的标志。每个结构对应着一个用到的DLL及其提供的函数的信息,也就是说,如果用到5个DLL,那么就有5个对应的数据结构。这个结构总共20个字节(5个DWORD),在本文中我们讨论最重要的三个:OriginalFirstThunk、Name、FirstThunk。
打开静态的thunder.exe,PE文件头相对偏移0x80H指明了输入表的RVA(相对虚拟地址)为0X000B8E4CH,即输入表位于.rdata节,由于打开的是静态PE文件,所以减去偏移C00H,得到物理地址0X000B824CH,该偏移为输入表结构数组开始的地方。
图2 Winhex查看输入表
上图阴影部分即为一个IMAGE_IMPORT_DESCRIPTOR数据结构,也就是涉及到的DLL的相关信息,可以找到我们将讨论的值,首先讨论Name,其作用是指向用到的DLL的名字,在图2中,可以看到Name值为0X000B9682H,同前所述,物理地址为0X000B8A82H,图3的阴影部分我们可以看到描述的DLL文件为WININET.dll。
图3 Winhex查看DLL名称
那么该DLL提供的API函数和函数的入口地址保存在哪儿呢?在图2中,我们可以查到OriginalFirstThunk值为0X000B9590H(物理地址为0X000B8990H),FirstThunk值为0X0009F5DCH(物理地址为0X0009E9DCH)。可以看到这2个偏移地址存储的值都为0X00B9672H(物理地址为0X000B8A72H),也就是说静态情况下,OriginalFirstThunk和FirstThun都指向相同的地方,可看到指向WININET.dll提供的API函数当中的一个(下图阴影部分),该函数名字是InternetOpenw。
图4 Winhex查看API函数名字
的确如此,OriginalFirstThunk指向INT表,用以指向WININET.dll提供的API函数名字。在静态时,FirstThunk同样指向INT表,上述操作过程验证了这点,但当动态情况下,也就是PE装载器将PE文件载入到内存时,将会改变FirstThunk指向的数组的内容,将所存储的API函数名字的地址变换为相应API函数的入口地址,即IAT表,为PE文件的执行做好了准备。
为了说明这点,可以通过Winhex打开RAM中运行的thunder.exe(动态)来研究FirstThunk发生的变化。此时,我们看到FirstThunk值为0X0009F5DCH没有变化,但该偏移处的值已变为0X75CA629EH(图5中阴影部分)。
图5 Winhex查看IAT-API函数的地址
需说明的是,由于载入基址为0X01160000H,所以上图中偏移为0X011FF5DCH(即0X0009F5DCH与0X01160000H相加),进一步验证该值是否为API函数InternetOpenw的地址,我们打开WININET.dll,发现其基址为0X75C80000H,而其提供的API函数InternetOpenw的相对虚拟地址为0X0002629EH,将两者相加恰好就是刚才我们看到的0X75CA629EH,也就是说,FirstThunk指向的内容已经发生变化,所存储的API函数名字的地址变换为相应API函数的入口地址。
3 小结
在利用Winhex对PE文件的输入表和输入地址表的分析过程中,我们应注意以下内容,由于操作系统按照一定的规则将PE文件装载到内存中,装入后的整个文件头内容不会发生变化,但PE文件的某一部分如节的内容会按照字段中的对齐方式在内存中对齐,分析静态PE文件我们用到物理地址,因而我们要进行RVA到物理地址的转换。
其次,通过分析,我们得知,PE运行前,PE装载器首先搜索OriginalFirstThunk,并通过DLL文件的输出表找到相应的API函数的地址替代FirstThunk指向的数组,也就是输入地址表。那么在程序加载完成之后,API函数的调用与运行就仅仅跟输入地址表有关系,不会再涉及到输入表的内容了。
[1]戚利.Windows PE权威指南[M].北京:机械工业出版社,2011.
[2]段刚.加密与解密[M].3版.北京:电子工业出版社,2008.
[3]看雪学院.软件加密技术内幕[M].北京:电子工业出版社,2004.