PE文件结构解析

预备知识

一、相关实验

本实验要求您已经认真学习和完成了《IMAGE_DOS_HEADER解析》、《PE头之IMAGE_FILE_HEADER解析》、《PE头之IMAGE_OPTIONAL_HEADER解析》、《节表头解析以及RVA与文件偏移地址的转换》。
本实验相关的基础知识都分散在前面几个实验中,所以如果你对其中有些概念还不是很熟悉的话,建议去回顾一下前面的几个实验。

实验目的

1)了解重定位的原理;
2)了解PE文件的重定位表结构;
3)手工解析PE文件的重定位表。

实验环境

在这里插入图片描述
服务器:Windows XP,IP地址:随机分配
辅助工具:C32Asm、LordPE

实验步骤一

重定位原理以及重定位表结构解析。
本实验将介绍数据目录表的第六个成员——重定位表。
在介绍PE文件的OptionalHeader的时候,其中有一个成员ImageBase,它指定了程序的首选装载基地址,也就是说,如果条件允许的话,PE文件被加载到内存时将会被加载到这个基地址。而如果这个地址已经被其他模块所占用了的话,那么这个PE文件就不能再放到这个位置了,它需要在进程空间的其他地方找到一个合适的位置来装载,因为加载的基地址发生了变化,该PE文件引用的一些全局变量或者函数都需要进行重定位处理。
下面通过一个具体的例子来讲解需要进行重定位的场景。假设在EXE文件中存在下面这样一段代码,用于弹出一个MessageBox:
在这里插入图片描述
其中第三个参数为消息框的标题,这里引用了一个全局变量,地址为0x00402000。该EXE文件在OptionalHeader中ImageBase的值为0x00400000,那么可以算出这个字符串的RVA值为0x00402000-0x00400000=0x2000。
如果可执行文件的实际装载地址就是0x00400000,那么上述代码不需要进行任何修改就可以直接运行。但是如果实际的装载地址不是0x00400000(假设为0x00870000),那么0x00402000指向的位置就不再是原来的字符串了,那么原来的字符串现在去哪里了呢?这可以简单的计算出来:实际装载地址+RVA=0x00870000+0x2000=0x00872000。这里进行一下变形,因为有RVA=原始地址-首选装载地址,所以最后的地址为新地址=实际装载地址-首选装载地址+原始地址,进行重定位修改之后的代码如下:
在这里插入图片描述
在上述的例子中,重定位起到的作用就是将第二条指令从push 00402000修改为push 00872000,经过重定位修正之后,程序就能正常运行了。经过重定位操作之后,PE文件被加载到任意一个基地址都可以正常的运行。
重定位操作由操作系统提供的装载器来完成,那么装载器是如何知道哪些数据需要进行重定位操作呢?这就涉及到我们即将介绍的重定位表了。
需要进行重定位的数据通常都会放在PE文件名为.reloc的节区中,数据目录表的第六个成员指向重定位表。重定位表存放着许多类型为IMAGE_BASE_RELOCATION的结构,该结构体在WinNT.h头文件中的定义如下:

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
    WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;

   
   
  • 1
  • 2
  • 3
  • 4
  • 5

该结构各个成员的介绍如下:
1.VirtualAddress,RVA,指向当前这一组重定位数据开始的地址,各个重定项的值加上VirtualAddress计算出实际需要重定位操作的RVA值;
2.SizeOfBlock,整个IMAGE_BASE_RELOCATION结构的大小,注意因为后面的TypeOffset数组的大小不固定,所以需要通过SizeOfBlock来指定整个结构的大小;
3.TypeOffset,需要重定位的项,数组的项数可以通过(SizeOfBlock-8)/2计算出来,数组每一项的大小占用2个字节,一共16位,其中高4位表示重定位的类型,低12位表示重定位的地址,低12位加上VirtualAddress得到实际上需要进行重定位操作的数据的RVA值。
常见的重定位类型有:
1.IMAGE_REL_BASED_ABSOLUTE,值为0,没有具体意义,用于对齐使用;
2.IMAGE_REL_BASED_HIGHLOW,值为3,表示指向的地址需要进行重定位修正;
3.IMAGE_REL_BASED_DIR64,值为10,出现在64位的PE文件中,对指向的整个地址修正。
重定位表的结构如下图所示:
在这里插入图片描述
可以看出重定位表由许多的IMAGE_BASE_RELOCATION结构组成,重定位表以一个VirtualAddress字段为0的IMAGE_BASE_RELOCATION结构结束。

实验步骤二

手工解析重定位表。
这里将手工走一遍重定位数据修正的过程。因为Windows XP自带的记事本notepad.exe不存在重定位表,所以选用Kernel32.dll来作为例子,具体的操作步骤描述如下:
1.打开桌面的LordPE工具,使用LordPE的PE编辑器载入C:\PE\ExportTable\Kernel32.dll文件,如下图所示:
在这里插入图片描述
2.点击LordPE界面右侧的“目录”按钮来查看数据目录表,在数据目录表中查看重定位表的起始RVA值为0x00118000,如下图所示:
在这里插入图片描述
3.使用LordPE的位置计算器,将RVA值0x00118000转换为文件偏移地址,得到0x00113000,如下图所示:
在这里插入图片描述
4.现在使用C32Asm打开C:\PE\ExportTable\Kernel32.dll,按下Ctrl+G快捷键跳转到0x00113000,可以看到IMAGE_BASE_RELOCATION结构体的VirtualAddress值为0x00001000,SizeOfBlock的值为0x00000070,也就是说整个IMAGE_BASE_RELOCATION结构的大小为0x70,那么根据公式(SizeOfBlock-8)/2可以计算出数组的元素个数为52,即52个WORD类型的数据,如下图所示:
在这里插入图片描述
从上图中可以看出,数组的第一项为0x362C(数据2C 36):对应的高4位为3,即IMAGE_REL_BASED_HIGHLOW,表示需要进行修正;低12位为0x62C,其加上VritualAddress得到0x62C+0x00001000=0x162C,这仍然是一个RVA值,使用LordPE的位置计算器将其转换为文件偏移地址得到0x00000A2C,如下图所示:
在这里插入图片描述
5.在C32Asm中按下快捷键Ctrl+G,然后跳转到0x00000A2C,这个地址处的DWORD类型的数据为0x7C810B40,如下图所示:
在这里插入图片描述
6.在LordPE可以看出,Kernel32.dll的OptionalHeader中的ImageBase值为0x7C800000(即默认的加载基地址),如下图所示:
在这里插入图片描述
7.假设Kernel32.dll实际的装载基地址为NewImageBase,根据第4步的分析可以知道需要进行重定位的数据的RVA为0x0000162C,将其转换为内存中的虚拟地址为:
VA=NewImageBase+0x162C。
8.根据第5步的操作,可以知道原始重定位数据的值为0x7C810B40,假设数据修正后的值为NewValue,那么可以推导出NewValue的计算过程如下:
RVA=OldValue-OldImageBase,
RVA=NewValue-NewImageBase,
那么有OldValue-OldImageBase=NewValue-NewImageBase,
即NewValue=OldValue-OldImageBase+NewImageBase。
代入数据后得到NewValue=0x7C810B40-0x7C800000+NewImageBase,即当Kernel32.dll被加载到内存中时,内存空间NewImageBase+0x162C处的数据应该为0x7C810B40-0x7C800000+NewImageBase
因为实验机器是Windows XP,而在Windows XP下测试的时候,发现notepad.exe进程没有需要进行重定位的模块,所以这里就不进行验证了,大家可以自行在Win7下验证,因为Win7引入了ASLR(Address Space Layout Randomization,地址空间布局随机化)机制,所以很多模块都需要进行重定位操作。

实验步骤三

编程解析重定位表。
在弄清楚重定位表的结构之后,编程实现重定位表的解析也就非常简单了,下面给出重定位表解析的过程:
1.通过对PE文件头部相关结构体的解析(IMAGE_DOS_HEADER、IMAGE_OPTIONAL_HEADER),可以得到数据目录表的数据;
2.从数据目录表数组的第六项可以得到重定位表的RVA以及大小;
3.将重定位表的RVA转换为文件偏移地址,就可以得到重定位表的IMAGE_BASE_RELOCATION结构体数组了;
4.根据IMAGE_BASE_RELOCATION结构的SizeOfBlock值,可以知道TypeOffset数组的大小;
5.解析重定位表的IMAGE_BASE_RELOCATION结构体数组,直到遇到VirtualAddress为0的IMAGE_BASE_RELOCATION项。
解析PE文件重定位表的代码位于C:\PE\RelocTbale\RelocParse.py。用于解析重定位表结构的代码如下所示:

def parse_relocation_table(self):
    """解析PE文件的重定位表结构"""
    print "Relocation Table"
    # 从数据目录表获取重定位表的RVA
    rva = self.get_dword(self.data_dirs[40:44])
    # 从数据目录表获取重定位表的SIZE
    size = self.get_dword(self.data_dirs[44:48])
    # 打印RVA和SIZE信息
    print "RVA: %08X\tSize:%08X" % (rva, size)
    # 如果RVA或者SIZE为0,表明没有重定位表
    if rva == 0 or size == 0:
        print "No relocation table data exists."
        return
    # 获取IMAGE_BASE_RELOCATION结构的文件偏移地址
    ibe_ptr = self.rva_to_offset(rva)
    # 获取整个重定位表的数据
    reloc_data = self.data[ibe_ptr:ibe_ptr+size]
    index = 0
    # 遍历重定位表
    while index < size:
        # 获取IMAGE_BASE_RELOCATION结构的VirtualAddress的值
        virtual_address = self.get_dword(reloc_data[index:index+4])
        # 获取IMAGE_BASE_RELOCATION结构的SizeOfBlock的值
        size_of_block = self.get_dword(reloc_data[index+4:index+8])
        # 获取IMAGE_BASE_RELOCATION结构的TypeOffset的数据
        type_offset = reloc_data[index+8:index+size_of_block]
        # 计算TypeOffset数组的项数
        type_offset_num = (size_of_block-8)/2
        # 打印信息
        print "VirtualAddress = %08X\tSizeOfBlock = %08X" % (virtual_address, size_of_block)
        # 遍历TypeOffset数组
        for i in xrange(0, type_offset_num):
            # 获取数组项的值
            tmp = self.get_word(type_offset[i*2:i*2+2])
            # 读取高4位
            type = ((tmp & 0xF000)>>12)
            # 读取低12位,并加上VirtualAddress
            rva = (tmp & 0x0FFF) + virtual_address
            # 将计算得到的RVA值转换为文件偏移地址
            offset = self.rva_to_offset(rva)
            # 读取文件偏移地址处的4字节数据并转换为DWORD
            data = self.get_dword(self.data[offset:offset+4])
            # 打印信息
            print "[%4d] RVA = %08X  Offset = %08X  Type = %X  Data = %08X" % (i, rva, offset, type, data)
        # 遍历下一个IMAGE_BASE_RELOCATION结构
        index += size_of_block
>现在来运行程序测试一下,并将解析得到的结果和LordPE解析得到的结果进行对比。打开cmd并切换到C:\PE\RelocTable,输入命令RelocParse.py Kernel32.dll > tmp.txt来运行脚本,如下图所示(因为产生的结果较多,所以这里将输出数据重定向到tmp.txt文件):
在这里插入图片描述
打开tmp.txt查看解析结果,发现与LordPE解析得到的结果是一致的,如下图所示:
在这里插入图片描述
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值