PE文件手工压缩

发布时间 2023-10-18 02:43:18作者: ve1kcon

本文要压缩的PE文件来自软件漏洞这门课上布置的作业,代码逻辑很简单,直接运行就能看出来,就是调库来弹两个对话窗口。笔者主要记录一下对这个文件的分析和一步步实现手工压缩的过程。在此提供原文件的下载方式供大家复现:PE程序下载

先是介绍各个部分要修改、要注意的地方,然后是说明一下以“在修改文件对齐方式后删除冗余数据”为主要方式,通过这种思路来压缩PE文件的做法。

PE文件各部分分析

0x0_DOS头+DOS_Stub

首先是开头0x4字节的MZ头不能动,然后0x3C的地方需要指向NT头的PE签名,除了这两个地方,DOS头和DOS_Stub的其他部分都可以删了,可以看到我把这些全都标识为1后程序正常运行。

下面也是一样的,我把能删的或者不会影响这个简单程序正常运行的地方都置1了,其实都是一点点试出来的,慢慢地改,就能发现到那些地方一被修改后,程序就不能正常运行了。

image

0xb0_NT头

NT头的前六个字节不能动,分别是四个字节的PE签名、两个字节的CPU的MAchine码,然后是两个字节的节区数目。

(下面开始以pfile的地址来说明)0xC4的可选头大小为固定值0xe0(0xc8-0x1a8=0xe0)

再往下,文件信息标志不能动,0xC8开始是可选头,前两个字节的可选头类型不能动。然后就到了0xD8的 Address of Entry Point,指向程序入口RVA,这个千万不能修改错。0xE4的镜像基址不能动,0xE8和0xEC的两个对齐大小需要注意。0xF8的主子系统版本号不能动,0x100的镜像在内存中大小,感觉改大了无所谓,别改小了导致不够用就行,这后面的 Size of Headers 标识着PE头的大小,要注意修改,不然就无法寻址到后面的节区了。0x10C开始的两个字节的子系统号和两个字节的DLL标志不能动,0x124是数据目录项数,是能改的,而且这个程序只需要导入表和导入地址表,但是一改就报错,所以先没管了... 然后这两个表的rva和size都是要去修改的,它俩的相对偏移也不能变

image

0x1A8_节表

这个程序有三个节,自然就有三个节表,分别是.text节表,.rdata节表,.data节表

这三个表的前八个字节都别去动,后面的真实大小不用管,要修改的是RVA,Size,Pointer,然后最后的四个字节的特征值也不能改,其他的都无所谓,每一个节表头都是这样

0x400_.text

注意改这些rva地址,分析的方法是丢进ida去看代码段的每一句汇编代码分别对应了什么字节码,对比着就很容易把这些直接寻址的地方找出来,要注意的是最后两个地址其实是与.rdata段存储的dll的名字有关的

image

0x600_.rdata

同样是丢进ida看,注意改这些rva地址,慢慢去算要修正成什么rva,得跟压缩前的指向同样的地方,改错了就无法寻址到了

image

0x800_.rdata

不需要做任何修改

开始压缩

先删除DOS_Stub,修改0x3C处的RVA地址指向新的PE头,然后NT头基本都不用动,删除多余的0后,就看着改下 Address of Entry Point, Section Alignment, File Alignment, Size of Image, Size of Headers, 然后是 IMPORT TableIMPORT Address Table 的RVA和Size,对齐方式我两个都改成了0x4,这是最小的对齐单位了,另外,对齐方式设成一样其实对后续修改文件也有好处,这样pflie就和rva就一致了,改一些直接寻址的地方就不用考虑这么多了,毕竟有寻址的地方要做修改的话,找到目标位置看pfile是多少就改成多少就好

最后的三个节表按上述分析里说的 去改相应的地方,代码节和数据节删除掉多余的部分后,也是按上面分析中的图去修改红框处那些rva地址,最后修改完是732字节,如图所示

image

记录下小坑

NT头

NT头里的Size of Headers要看好来改,不然直接找不到下面那些块了,数据表的IMPORT Address Table里的东西也是要改的,这个比较容易遗漏

.rdata

rdata节里面,直接把后面的所有'\x00'都删掉是不可以的,我一开始改的时候就注意到一个问题,发现如果把后面的'\x00'都删掉了,最后面存储的库名就会和data段的数据拼接起来,这样的话去进行字符串匹配肯定是会出问题的,所以得要'\x00'来截断,果不其然,去看了下.rdata的真实大小是0x92,这就是原程序为什么非要多加两个'\x00'在后面了,所以我们修改的.rdata的Size of Raw Data得是0x94,用'\x00'补齐

后续压缩思路

想要继续往下压缩就得走别的思路了,首先可以看见还有很多没有用到的标识成'\x00'的数据,但是又删不得,就好比,头有用,尾有用,如果删掉了中间部分,头尾拼接在一起,就不能用 ”头+偏移“ 的方式去寻址到尾了,所以情况的这种核心思路就是见缝插针,即将有用的数据填充到一个都是'\x00'的新地方,然后修改索引这些数据的指针指向这个新的地址

其次就是靠合并,比如将DOS头和NT头合二为一;以及将数据节和代码节合二为一,这样能省下一个节表的空间....