1基础知识介绍
运行时压缩器是软件逆向工程中常见课题;了解运行时压缩,需要掌握基本的PE文件格式,操作系统的基本知识等。
理解本文内容最好有PE文件基础知识,对PE文件加载器有一定了解。本文会穿插介绍PE文件基础知识但重点不在于此。
下图是PE文件结构部分及在磁盘和加载到内存中的对比,其文件的总体结构未发生变化,但是文件在磁盘和内存中的相对偏移发生了变化。
运行时压缩器是针对可执行文件而言,可执行文件内部含有解压缩代码,文件在运行瞬间于内存中解压缩后执行。
普通压缩与运行时压缩比较
项目 | 运行时压缩 | 普通压缩 |
对象 | PE文件(exe,dll,sys) | 所有文件 |
压缩结果 | PE文件(exe,dll,sys) | 压缩文件(rar,zip) |
解压方式 | 内部自解压程序 | 专门解压程序 |
可否执行 | 本身可执行 | 本身不可执行 |
特点 | 无需专门解压程序可直接执行 | 对所有文件压缩 |
可执行文件的压缩器称为“运行时压缩器”,它是PE文件的专用压缩器。
PE压缩器使用的目的主要集中于两个方面:
缩减PE文件的大小
隐藏PE文件内部代码与资源
PE压缩器种类:
单纯用于压缩普通PE文件,比如UPX,ASPack等
对源文件(PE文件)进行较大变形、严重破坏PE头、意图不纯的压缩器,例如用于恶意程序(virtus,Trojan,worm等),比如UPack,PESpin等。
UPack的制作者对PE头有深刻认识,UPack以对PE头的独特变形技法而闻名。UPack在刚出现时,许多PE实用程序无法正常运行。导致许多恶意代码制作者使用UPack压缩恶意代码发布。
本文以UPack 0.39版本压缩的notepad.exe为例,详细介绍UPack对PE文件的压缩过程,尤其对PE头部及代码重置的关键技术做了详细介绍。
UPack对notepad.exe做运行时压缩,如下图所示
由上图可知notepad.exe程序有原来的66560个字节变成了44908个字节,文件大小有了变化,同时由图中输出信息可知文件的输入表,资源信息等也发生了变化。
被UPack压缩后的文件,文件头部和文件内容发生了变化,对导致许多安全软件认为压缩文件有可能包含病毒特征,会直接报毒,下图是利用QQ传输原始notepad文件和压缩notepad文件时,QQ自检会认为被UPack压缩后的文件包含病毒特征。
下图是利用IE浏览器下载压缩文件和正常文件,IE插件给出的提示信息。
下图是利用多扫描引擎对压缩文件进行检测,国内很多安全厂商的病毒引擎都认为被UPack压缩的文件含有病毒特征。
由于UPack对PE文件头部做了特殊处理,这样就会使一些PE实用程序无法正常运行,PEView使用工具加载压缩notepad文件和原始notepad文件时,对文件识别也表现不同;由于UPack对notepad头部做了变形,导致PEView在对压缩notepad解析时找不到正常的文件结构而加载失败。
经过UPack压缩后的文件,其头部和节部分的内容都发生了变化,貌似已经不再符合PE文件格式要求。
典型的PE文件头,其中数据按照IMAGE_DOS_HEADER、DOS Stub、IMAGE_NT_HEADERS、IMAGE_SECTION_HEADER顺序排列。
单独二进制文件对比发现,文件的大小和文件内容都发生了变化,压缩后的文件表面上看已经不在符合PE文件规范。如下图所示,更直观的对比,文件结构的很多字段都发生了变化,对于一般的实用工具或是调试工具,很难识别出压缩后文件的文件结构。
UPack以头部压缩著称,将PE文件头部变化的面目全非,宛如去韩国整了一次容。
有PE结构可知PE头部包括了IMAGE_DOS_HEADER、DOS Stub、IMAGE_NT_HEADERS、IMAGE_SECTION_HEADER五部分组成,其中IMAGE_NT_HEADERS有magic、IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER三部分组成。
重叠文件头将MZ文件头(IMAGE_DOS_HEADER)与PE文件头(IMAGE_NT_HEADERS)巧妙重叠一起,节约文件头空间。
由PE文件头部结构可知(需要知道PE文件头部基础结构),IMAGE_DOS_HEADER结构中两个重要字段e_magic和e_lfanew,第一个字段一般为“MZ”是PE文件最开始的标志,第二个字段标志的是除去IMAGE_DOS_HEADER头部和DOS Stub部分后IMAGE_FILE_HEADER结构的开始位置。
一般情况下e_lfanew =IMAGE_DOS_HEADER(40)+DOS Stub(vc下A0)=0XE0,也就是说一般e_lfanew字段的值为0XE0,对比UPack压缩后的文件e_lfanew字段的值变为0X10,如下图
下图是一个示意图文件的DOS头部压缩前后发生的变化。文件压缩后,DOS Stub部分已经消失,被IMAGE_FILE_HEADER结构占据。
在IMAGE_FILE_HEADER有一重要字段就是SizeOfOptionHeader;SizeOfOptionHeader表示IMAGE_OPTIONAL_HEADER的大小;修改该值,改变IMAGE_OPTIONAL_HEADER头部的大小,可插入解码代码等内容。
在一般的PE文件中,IMAGE_OPTIONAL_HEADER结构的大小是固定,所以SizeOfOptionHeader字段的值也是固定的,为0XE0,如下图notepad_origin.exe所示
在经过UPack压缩后,文件头部的SizeOfOptionHeader变成了0x148,如下图所示
比正常值0XE0要大的多。SizeOfOptionHeader的另一层含义是确定节区头(IMAGE_SECTION_HEADER)的起始偏移,将该字段的值由0XE0扩大到0x148,无疑增大了IMAGE_OPTIONAL_HEADER结构的大小。
UPack的基本特征就是把PE文件头变形,像非洲人的头发一样拧麻花,向文件头适当插入解码需要的的代码。增大SizeOfOptionHeader的值后,就在IMAGE_OPTIONAL_HEADER与IMAGE_SECTION_HEADER之间添加了额外空间。向这个额外区域添加解码代码,是一种超越文件头常规理解的巧妙方法。
二进制查看该额外区域的数据,该区域的起始位置在IMAGE_OPTIONAL_HEADER的结束位置0XD7(为什么是D7位置,后面会介绍),该区域的结束位置是0x170。
利用调试器查看该区域的部分反汇编代码如下图
下图是IMAGE_FILE_HEADER压缩前后的格式对比,压缩后的文件头部增大。
对比压缩前后的文件可知,IMAGE_OPTIONAL_HEADER结构中的NumberOfRvaAndSizes字段的值也变化,该字段表示的是数据目录的大小,改变该值也是为了向文件头插入自身代码。正常文件该字段的大小是0x10,也就是IMAGE_OPTIONAL_HEADER结构中最后一部分有有0x10个IMAGE_DATA_DIRECTORY数组,但是在UPack压缩有该值变成了0x0A,也就是说少了6个IMAGE_DATA_DIRECTORY结构数组,每个数组占8个字节。从下图可以看出IMAGE_OPTIONAL_HEADER结构的后半部分IMAGE_DATA_DIRECTORY结构刚好到0Xd7的位置,也即是说从0XD8一直到0x170之间都是作为空闲区域插入代码。
下图是IMAGE_FILE_HEADER压缩前后的格式对比,压缩后的DATA_DIRECTORY结构变小。
前面介绍了文件图的IMAGE_DOS_HEADER、DOS Stub、IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER、DATA_DIRECTORY的变形,接下来就是IMAGE_SECTION_HEADER的变形。
IMAGE_SECTION_HEADER是文件头部的最后的一部分。节区的变化包括了节区区域的增加,节区区域的减少,节区数量的增加等。UPack对PE文件的节区也做了变化,下图是原始notepad文件与压缩后的notepad文件节区对比
对比两幅图,变化前后节区的名称,虚拟大小,偏移,文件大小,文件起始偏移等都发生了变化。
查看压缩后的notepad文件的节区,发现第一个节区与第三节区的文件起始偏移与在文件中的大小完全一致。但是节区内存的起始RVA(VirtualOffset)项与内存大小(VirtualSize)值确不同。根据PE规范,这样做不会有什么问题(更准确的说,PE规范并未明确指出这样做不行)。UPck会对PE文件头、第一节区、第三节区进行重叠。
根据节区头中定义的值,PE装载器会将文件偏移0x10-0x200的区域分别映射到内存中的第一、第三节区。也就是说用相同的文件分别创建出不同位置的、大小不同的内存映像。
观察压缩的节区,文件的头部区域大小为200字节。相反,第二个节区大小(AD6C)非常大,原文件就压缩与此。另外需要注意的部分是内存中的第一个节区区域,它的内存尺寸为13000,与原文件的sizeofImage相同。
文件节区压缩示意图
也就是说,压缩在第二个节区的文件映像很可能会解压缩到第一个节区。文件开始装入内存后的示意图如下图。
http://blog.nsfocus.net/upack-works-case-analysis1/