2024年1月2日发(作者:)

大眼睛书吧整理编辑

32位Windows系统PE文件格式漏洞简要分析

可移植的执行体(Portable Executable,PE)文件格式是微软制定的一种文件标准,它是从普遍运用于UNIX 操作系统的COFF发展而来,是目前Windows平台上的主流可执行文件的文件格式,应用于所有的32位Windows。可执行文件的格式是操作系统本身执行机制的反映。基于PE文件格式的研究层出不穷,及这方面的漏洞分析不容忽视。

一、PE文件结构

PE文件主要由DOS部首,PE文件头,节表,节和辅助结构等5部分组成,如图所示。数据结构通常定义在WINNT. H。

二、基于PE文件格式的应用

很多进一步的应用开发都是建立在此原理之上,比如在加密解密领域(在PE文件的冗余空间中插入加密解密程序的代码),信息隐藏领域(主流的方法是利用PE文件中存在的冗余空间和冗余字段进行信息嵌入,该方法不会改变PE文件长度和结构。也可利用静态分配字符串存储空间进行信息嵌入的方法,但只适用于未编译的PE文件源代码中进行信息隐藏)和文件病毒领域。

2.1 向PE文件中添加可执行代码

大眼睛书吧整理编辑

PE文件的数据结构在磁盘和内存中是一样的,装载一个可执行文件到内存中,它不是作为单一内存映射文件被载入内存的,Windows加载器遍历PE文件并决定文件的哪一部分被映射。依据PE文件的存储结构及其加载过程,得出两种典型的向PE文件中添加代码的方法。

2.1.1 插入方式

PE文件在磁盘中,节与节之间必须遵循文件对齐原则,以利于PE文件快速定位,而程序的代码或数据可能没有占满该节,造成冗余空间,可乘机插入代码,实现思想如下:由DOS头结构字段e_lfanew定位PE 头结构位置;读取PE 头,得到节个数字段NumberOfSections;依据节个数分配节表缓冲区读取所有节表到缓冲区;遍历每个节,空白区的文件偏移等于(PointerToRawData+ VirtualSize);空白区大小为该节实际占用磁盘空间减去该节实际大小(SizeOfRawData-VirtualSize);修改节表结构让缓冲区中每个节表的字段VirtualSize等于字段SizeOfRawData;将缓冲区数据写入。

2.1.2 附加方式

对于绝大多数编译器生成的PE 文件,在最后一个节表结构和第一个节数据区之间存在一个空白区,如图1 所示的UNUSED 区。往往足以插入一个新的节表结构(每个节表结构大小是28HB),实现思想如下:计算NumberOfSections*0x28

+e_lfanew + sizeof(IMAGE_NT_HEADER),得到要添加新节头的起始地址;根据PointerToRawData=(最后一节的PointerToRawData+最后一节的SizeOfRawData)和SizeOfRawData/VirtualSize=((要添加的代码长度/FileAlignment+1)*FileAlignment)赋值给节头结构中的对应变量。修改NumberOfSections,SizeOfImage等字段。在这里一定要注意,依照SectionAlignment对齐VirtualSize和VirtualAddress同样依照FileAlignmentSizeOfRawData和PointerToRawData;根据不同需要设置属性字段等。

2.2 文件病毒领域

以插入方式添加的病毒代码,如CIH 病毒。对于存在空白空间的节,可以通过修改节表结构,让VirtualSize等于SizeOfRawData,然后在空白空间中插入随机数据欺骗病毒,使它无法以这种方式感染PE 文件。

以附加方式添加的病毒代码,如劳拉病毒。修改PE 文件时在最后一个节表

大眼睛书吧整理编辑

和第一个节数据区的空白区插入了一个新的节表结构,用以描述在文件尾部附加一个新的节存放病毒代码。由于PE 头文件、节表结构是相连的,如果将这两个结构往下移动到与第一个节数据区相连,病毒就无法插入。PE头文件和节表结构在文件中的位置是由DOS 头结构中的字段e_lfanew决定,修改该值并把两个结构向下移动到连接第一个节就可以防范病毒插入节表结构,有效禁止了添加新节。

最新的研究是建立自免疫防病毒系统:在文件中某个不固定的空白位置存储文件的重要数据,如果非覆盖型文件病毒修改文件的任何一个字节,程序启动时会计算当前的CRC32 校验值并和原来存储的CRC32 值相比较,若相等,则跳转到原来程序入口继续运行,否则从自身释放出一个修复程序,并关闭当前程序由修复程序对文件进行恢复操作,完成修复后调用被修复文件然后删除自己,删除方法是是先生成一个批处理文件,然后用函数ShellExecute调用批处理文件后退出进程,在批处理文件中循环删除主文件,直到主文件的进程退出,批处理程序才能删除主文件,然后让批处理文件删除自身。

三、漏洞

3.1 应用程序接口(API)函数地址获取

PE 文件在编译和连接成功后,会有一个IAT。当需要执行API 的时候,会先在IAT 中得到API 的地址,然后调用它。

当往PE 文件中插入可执行代码时,情况不同,可执行代码是在PE 文件编译好之后才插入,它本身没有导入表,为了在插入代码中调用API 函数,必须获取API 函数的线性地址[4]。(某些情况下可以搜索宿主的导入表获得GetProcAddress和GetModuleHandleA函数,然后通过它们返回系统的DLL,进

而获得其中的API 函数,但在宿主没有使用GetProcAddree函数的时候,这种方法行不通。)而要得到API 的地址,必须用到Loadlibrary和GetProcAddress函数,这两个函数在 中。如图2,CreatProcess函数在完成装载应用程序后,会将一个返回地址压入堆栈并转而执行应用程序,这个保存在堆栈顶的数据就在 中的返回地址。因此只要从返回地址按照页对齐的边界一页页地往低地址搜索,就必然可以找到 文件头地址,搜索它的导出表,并从导出表的信息中查找到Loadlibrary和GetProcAddress函数的地址,利用这两个函数得到想要的API 函数。(对于给定的API 函数,也可以直接在

大眼睛书吧整理编辑

的导出表中搜索其地址。)

3.2 Win32结构下基于PE 文件格式的几个漏洞

上面的研究表明:由于节的对齐粒度固定,造成节与节之间可能存在虚拟自由空间,这些空间成为嵌入代码的隐藏地和更具规模的移动代码的发射台,从而引起安全危机。具体漏洞如下所示:

1 )模糊的将DLL可执行体链接到进程,尤其是GetProcAddress和LoadLibrary这两个API 函数,在方便了普通程序开发者链接到他们的DLL 之外有更深远的影响。这两个API 函数使得挖掘代码,嵌入代码,及其他高度移动代码的行为成为可能。

2)完整的PE 头信息,包括导入导出库信息,留在进程的内存中方便了GetProcAddress函数和Kernel32 模块基础获得信息,埋下隐患。

3)在发布可执行文件的准备时期,对运行在操作系统(和/或者,防病毒程序)的可执行文件的整体性缺乏检测机制,或者很薄弱的不充分的检测机制,这使得修改可执行文件和运行新的可移动代码相对容易,很多组件都没能检测到对可行性文件的修改。