71's blog

宏願縱未了 奮鬥總不太晚

0%

不影响程序功能的前提下压缩PE文件

划个水】是最近的作业题,做完后觉得挺有意思就记录一🦐

说白了就是修改文件的映射方式)

PE文件结构

微软有给出详细的文档说明:PE Format

除了MZ头、PE头这种标志位外,还需要重点关注一些数据:

  1. IMAGE_DOS_HEADER 中:

    Offset Description
    +0x3C Offset to New EXE Header(也就是下面的IMAGE_NT_HEADERS )
  2. IMAGE_NT_HEADERS 中:

Offset Description
IMAGE_FILE_HEADER + 0x10 Size of Optional Header
IMAGE_OPTIONAL_HEADER + 0x04 Size of Code
IMAGE_OPTIONAL_HEADER + 0x08 Size of Initialized Data
IMAGE_OPTIONAL_HEADER + 0x0C Size of Uninitialized Data
IMAGE_OPTIONAL_HEADER + 0x10 Address of Entry Point
IMAGE_OPTIONAL_HEADER + 0x14 Base of Code
IMAGE_OPTIONAL_HEADER + 0x18 Base of Data
IMAGE_OPTIONAL_HEADER + 0x1C Image Base
IMAGE_OPTIONAL_HEADER + 0x20 Section Alignment
IMAGE_OPTIONAL_HEADER + 0x24 File Alignment
IMAGE_OPTIONAL_HEADER + 0x38 Size of Image
IMAGE_OPTIONAL_HEADER + 0x24 Size of Headers
IMAGE_OPTIONAL_HEADER + 0x5C Number of Data Directories
IMAGE_OPTIONAL_HEADER + 0x60 RVA and Size of EXPORT Table
IMAGE_OPTIONAL_HEADER + 0x68 RVA and Size of IMPORT Table
IMAGE_OPTIONAL_HEADER + 0x70 RVA and Size of RESOURCE Table
…… ……
  1. IMAGE_SECTION_HEARDER.xxx (eg. text, rdata, data …)

    Offset Description
    +0x08 Virtual Size(section真正的大小)
    +0x0C RVA
    +0X10 Size of Raw Data(section在文件内对齐后的大小)
    +0X14 Pointer to Raw Data(section首地址在文件中的偏移)
  2. SECTION.text

push、pop等指令后面跟的内存地址,都是原程序映射后的地址。如果修改了映射的地址空间,这里也要做相应的修改。

  1. SECTION.rdata

如果修改了映射的地址空间,IAT、IDT、INT、IN四个数据结构存储的 RVA 地址都需要改成我们映射后的新地址。

修改过程

空间复用

为了尽可能地减少多余的字节,加上MZ头又只有标志位和偏移 0x3C 的位置有用,可以在 4D 5A 00 00 后面的空间写入 PE 头的内容。

要注意 0x3C 的位置应该指向 PE 头,这里我们是在文件 0x04 的位置写入的,因此这里应该修改为4。在PE头里,这个位置刚好也是 section alignment 的位置。

一般来说 section alignment 是一个页的大小 0x1000,现在修改为4,根据微软文档的说明,它后面的 file alignment 也应该要跟它对应,因此这里也把文件对齐粒度改为4。同时,后面所有的数据,最多只需要补全3个\x00,能很大地缩减空间(基本不用考虑对齐了属于是)。

地址映射

由于测试程序本身的大小小于 0x1000,如果按原本的方式进行映射,会出现数据错位的问题,把不属于这个位置的数据也映射进去了。(从一些失败的实验结果发现的,尽管指定了每个section的size、pointer、RVA,但内存对齐粒度仍为0x1000的话,加载不到正确的数据,具体原因因修改情况而异,拖进ida分析,算一下逻辑地址就知道了)

由于我们的内存对齐粒度改为4了,中间也没有用大量的 00 填充,其实可以直接拿程序本身的地址空间作为映射后的地址空间。相应地把所有RVA地址、size、base修改好,这时候就能正常映射了。

修改完程序的映射地址如下。从 ida 分析来看,虽然我们把整个程序映射后的空间缩小为文件大小(不足0x1000),但系统还是给程序分配了一个页的空间。

在这种修改方式下,程序大小从 0xA00 个字节变为 0x230 个字节,还是缩减了非常多空间的。