前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PE格式第七讲,重定位表

PE格式第七讲,重定位表

作者头像
IBinary
发布2018-01-08 12:00:38
9710
发布2018-01-08 12:00:38
举报
文章被收录于专栏:逆向技术逆向技术

         PE格式第七讲,重定位表

一丶何为重定位(注意,不是重定位表格)

首先,我们先看一段代码,比如调用Printf函数,使用OD查看.

那么大家有没有想过这么一个问题,函数的字符串偏移是00407030位置,函数Call的地址是00401020的位置

但是如果模块首地址申请不到了,变为了00100000的位置,那么此时的偏移是不是都是错的了?

首先说下,一般重定位表格都是DLL中的,因为满足不了模块首地址的需求,所以会遇到函数的重定位问题.

那么如果磨坏地址变为了00100000的位置,那么对应的字符串位置是不是也要变为00107030的位置,而Call的地址,是不是也要变为00101020的位置

那么这个就叫做重定位,我们要把偏移,以及各种需要修正的位置,变为正确的.

二丶重定位表格如何设计?

首先我们自己先想一下,重定位的表格要如何设计?

我猜想,你要保存模块的地址  ,修改地址,偏移, 以及大小.

新的模块 ImageBase

旧的模块 iMageBase

修改的地址 

偏移

修改的大小

那么如果这样设计会不会出现问题?

会出现很多问题,比如占得字节太多了,如果是Kerner32.dll里面都是这样设计,那么得要多少内存.

那么进一步的优化

可不可以一个分页,保存修改的偏移,以及长度

分页: page  (DWORD) 占4个字节

大小: size (DWORD)     偏移:offset(DWORD)

表格设计为上面的,

感觉这样可以了.但是感觉还可以进一步的优化,大小,以及偏移都占4个字节,是不是浪费了

而且如果记录一个分页中的重定位的数据,那么偏移是不会超过12位的(二进制12位,转为10进制是1024),  那么如果一个DWORD存储文件偏移,那么高4位是没有用的.

而且我们发现,大小也是很占位置的.大小一个字节就可以表示了,比如0 做对齐使用,1修改高16位的偏移,2修改低16位的偏移,3修改4个字节大小

那么是不是可以合并了

page  (DWORD)

sizeofoffset

0x3005    代表的意思就是看高位,3代表我要修改4个字节,005代表修改的当前页的偏移位置.

三丶真正的重定位表格

看下重定位表格的真正的结构体吧.

代码:

代码语言:javascript
复制
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;            页存储的RVA
    DWORD   SizeOfBlock;              word类型数组的个数,也就是下面注释的
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

那么看看是不是和我们猜想的一样,我们随便找个DLL,在数据目录中定位重定位表格

1.寻找数据目录RVA偏移

我们首先要找到数据目录中重定位表格的RVA偏移然后判断属于哪个节,通过公式转化,得到在文件中的实际偏移位置.

得出RVA = 6000h

2.判断属于哪个节

我们发现,新增加了一个节,这个节就是重定位的节然后虚拟地址是6000位置,而且在文件偏移的位置也是6000h

那么我们就得出 FA = RVA了,那么就不用算了,可以确定,文件偏移位置就是6000就是重定位表的位置

3.定位文件偏移处,查看排列

然后可以看出 前八个字节分别保存页的RVA偏移,以及大小,.我们使用计算器计算一下,看看有多大

计算的出 160h,这个大小,保存的是数组大小+上我们八个字节的总大小,也就是说160 - 8 = 数组的大小了.

可以看出确实是怎么大,然后就到记录下一个分页了.

四丶数组解析查看

 那么按照我们的想法看上面重定位表中的数组的第一个,按照小尾方式读取则是

0x3005  那么高位是3那么就是要修改大小是4个字节,005则是代表偏移.

至于高位怎么查看,VC++6.0中的宏已经定义了.

代码:

代码语言:javascript
复制
#define IMAGE_REL_I386_ABSOLUTE         0x0000  // Reference is absolute, no relocation is necessary
#define IMAGE_REL_I386_DIR16            0x0001  // Direct 16-bit reference to the symbols virtual address
#define IMAGE_REL_I386_REL16            0x0002  // PC-relative 16-bit reference to the symbols virtual address
#define IMAGE_REL_I386_DIR32            0x0006  // Direct 32-bit reference to the symbols virtual address
#define IMAGE_REL_I386_DIR32NB          0x0007  // Direct 32-bit reference to the symbols virtual address, base not included
#define IMAGE_REL_I386_SEG12            0x0009  // Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address
#define IMAGE_REL_I386_SECTION          0x000A
#define IMAGE_REL_I386_SECREL           0x000B
#define IMAGE_REL_I386_REL32            0x0014  // PC-relative 32-bit reference to the symbols virtual address

这里只需要知道0 1 2 代表的意思即可,因为0x3005的高位是 用位运算 | 上去的,所以3代表的是1 和 2的组合

0  对齐使用

1.修改高16位  

2.修改低16位

1和2 使用位运算|起来就是修改4个字节.

1.定位修改位置

那么怎么定位要修改的位置那?

 公式:

现在的ImageBase(模块地址) +  当前分页大小的虚拟地址 +5的位置等于要修改的位置:

比如假设我们的现在的模块地址是00400000位置,而DLL以前的位置是10000000  而它以前的字符串的偏移是   10003045

首先定位修改地址:

00400000 + 1000 + 005 = 401005  那么在401005的位置就是你要修改的位置

比如我们在写一个

0x3096 =  400000 + 1000 + 96 = 401096  那么定位的位置就是401096是你要修改的偏移,大小是4个字节,高位为3  为什么是4个字节,一会看下内部存储

2.修改的偏移计算

比如我们调用一个printf 

push 10003096    "HelloWorld"

call    10004086  

那么我们要修正他的偏移

我们现在得知,以前的DLL偏移是  10000000    以前的字符串偏移是  10003096 ,不过因为DLL的模块地址没有满足,那么现在的模块地址变为了00400000的位置

那么我们要修正偏移

公式:

现在的ImageBase (00400000) - 以前的ImageBase(10000000) + 以前的偏移(10003096)

这样写汇编代码好写,如果便于理解的话,可以写成下面那样,只不过你需要知道的是汇编代码就是上面这种写法就行

以前的偏移(10003096)  - 以前的ImageBase(10000000) + 现在的ImageBase(00400000)  

= 3096 + 400000

= 403096  (计算出来的偏移)

那么push的位置就成了 403096了,已经重定位了.

五丶实战演练查看

 因为DLL中的重定位表中的项太多,所以这里使用一个EXE(没有导出函数的EXE),然后注入这个DLL,那么这个EXE就有重定位表格了.

首先我们先看DLL, 3005的位置要重定位

按照公式我们得出,要修改的位置是

现在模块地址 + 当前表中记录分页 + 数组中后三位的偏移(上面说过,如果按照分页存储,那么3位就可以表达一个分页需要记录的了)

那么现在  我们的EXE的模块地址是00500000 + 1000(重定位表中第一项成员) + 005  (这是一个数组,第一个成员是0x3005  取出后三位则是005)

得出修改的位置是  00501005的位置,我们OD中CTRL+ G看看这个位置是不是要修正.

代码乱了,那么我们可能断掉指令了,那么此时CTRL + A重新分析一下.

 可以看出,我们修正的位置是501005的位置,不过汇编代码在501004才能显示出来,501005后面正好是要修正的地址,那么只需要计算偏移填进去就可以了,大小是按照高4个字节, 现在0x3005 高位是3那么代表了要修正4个字节的偏移.

算出偏移位置:

偏移位置我们要进行反推了

因为OD已经帮我们重定位好了.

503000 = 现在的ImageBase - 以前的ImageBase + 以前的偏移 = 现在的偏移(503000)

那么现在计算以前的偏移

以前的偏移 = 现在的偏移 - 现在的ImageBase + 以前的ImageBase 

=  503000 - 50000 + 60000000 

= 3000 + 60000000

= 60003000 (以前的偏移)

那么算出了以前的偏移,我们就计算这4个字节要填写的偏移,也就是503000怎么得出来的

公式上面说了:

 Cur (缩写,代表当前的意思)  Old(代表旧的意思)  offset(代表偏移的意思)

CurImageBase - OldImageBase + OldOffset = 要填入的偏移

代入公式:

00500000  -  60000000  + 60030000 = 00503000 (要填写的文件偏移)

看下我们当前的模块地址:

Inject是我们当前的EXE的名称,模块地址在00500000的位置

DLL的模块地址是60000000  这个地址是我们通过修改DLL中的选项头中的ImageBase得到的.

 六丶总结

 上面讲的很细致

今天主要就是结构体会看,偏移会算即可.

总结一下公式

1.定位重定位的地址  (也就是在哪里修改)

首先从数组取出一项,(2个字节大小)

比如0x3005

公式:

定位修改地址  = 现在模块 + 当前结构记录分页的RVA  + 取出数组的2个字节的低3位

例子: 00401000 + 1000 + 005 = 世纪你要修改的地址,修改大小和取出word自己的第一位有关

2.计算出偏移地址,填写到定位地址的位置,使其偏移正确

现在的模块地址  - DLL模块地址 + 以前的偏移 = 实际修改的偏移

便于理解的公式:

以前的偏移 - DLL模块地址 + 现在模块地址 

3.计算出以前偏移

要计算出以前的偏移,你首先要得出现在的偏移,好在OD已经写好了,其实文件中也有存储的.(自己找吧)

以前的偏移 = 现在的偏移 - 现在模块地址 + DLL模块地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-10-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  •          PE格式第七讲,重定位表
    • 一丶何为重定位(注意,不是重定位表格)
      • 二丶重定位表格如何设计?
        • 三丶真正的重定位表格
          • 1.寻找数据目录RVA偏移
          • 2.判断属于哪个节
          • 3.定位文件偏移处,查看排列
        • 四丶数组解析查看
          • 1.定位修改位置
          • 2.修改的偏移计算
        • 五丶实战演练查看
          •  六丶总结
            • 1.定位重定位的地址  (也就是在哪里修改)
            • 2.计算出偏移地址,填写到定位地址的位置,使其偏移正确
            • 3.计算出以前偏移
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档