前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的:

在这里,IE的iexplorer.exe导入了Kernel32.dll的GetCommandLineA函数,可以看到这是个间接call,00401004这个地址的内存里保存了目的地址,根据图中显示的符号信息可知,00401004这个地址是存在于iexplorer.exe模块中的,实际上也就是一项IAT的地址。这个是IE6的exe中的例子,当然在dll中如果导入其他dll中的函数,结果也是一样的。这样就有一个问题,代码里call的地址是一个模块内的地址,而且是一个VA,那么如果模块基地址发生了变化,这个地址岂不是就无效了?这个问题如何解决?

答案是:Windows使用重定位机制保证以上代码无论模块加载到哪个基址都能正确被调用。听起来很神奇,是怎么做到的呢?其实原理并不很复杂,这个过程分三步:

1.编译的时候由编译器识别出哪些项使用了模块内的直接VA,比如push一个全局变量、函数地址,这些指令的操作数在模块加载的时候就需要被重定位。

2.链接器生成PE文件的时候将编译器识别的重定位的项纪录在一张表里,这张表就是重定位表,保存在DataDirectory中,序号是 IMAGE_DIRECTORY_ENTRY_BASERELOC。

3.PE文件加载时,PE 加载器分析重定位表,将其中每一项按照现在的模块基址进行重定位。

以上三步,前两部涉及到了编译和链接的知识,跟本文的关系不大,我们直接看第三步,这一步符合本系列的特征。

在查看重定位表的定义前,我们先了解一下他的存储方式,有助于后面的理解。按照常规思路,每个重定位项应该是一个DWORD,里面保存需要重定位的RVA,这样只需要简单操作便能找到需要重定位的项。然而,Windows并没有这样设计,原因是这样存放太占用空间了,试想一下,加入一个文件有n个重定位项,那么就需要占用4*n个字节。所以Windows采用了分组的方式,按照重定位项所在的页面分组,每组保存一个页面其实地址的RVA,页内的每项重定位项使用一个WORD保存重定位项在页内的偏移,这样就大大缩小了重定位表的大小。

有了上面的概念,我们现在可以来看一下基址重定位表的定义了:

[cpp] view plain copy
  1. typedef struct _IMAGE_BASE_RELOCATION {
  2. DWORD   VirtualAddress;
  3. DWORD   SizeOfBlock;
  4. //  WORD    TypeOffset[1];
  5. } IMAGE_BASE_RELOCATION;
  6. typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

VirtualAddress:页起始地址RVA。

SizeOfBlock:表示该分组保存了几项重定位项。

TypeOffset:这个域有两个含义,大家都知道,页内偏移用12位就可以表示,剩下的高4位用来表示重定位的类型。而事实上,Windows只用了一种类型IMAGE_REL_BASED_HIGHLOW  数值是 3。

好了,有了以上知识,相信大家可以很容易的写出自己修正重定位表的代码,不如自己做个练习验证一下吧。

本文 by evil.eagle 转载的时候请注明出处。http://blog.csdn.net/evileagle/article/details/12886949

最后,还是总结一下,哪些项目需要被重定位呢?

1.代码中使用全局变量的指令,因为全局变量一定是模块内的地址,而且使用全局变量的语句在编译后会产生一条引用全局变量基地址的指令。

2.将模块函数指针赋值给变量或作为参数传递,因为赋值或传递参数是会产生mov和push指令,这些指令需要直接地址。

3.C++中的构造函数和析构函数赋值虚函数表指针,虚函数表中的每一项本身就是重定位项,为什么呢?大家自己考虑一下吧,不难哦~

PE文件结构详解(六)重定位相关推荐

  1. 【转】PE文件结构详解--(完整版)

    (一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...

  2. PE文件结构详解 --(完整版)

    From:https://blog.csdn.net/adam001521/article/details/84658708 PE结构详解:https://www.cnblogs.com/zheh/p ...

  3. PE文件结构详解(一)基本概念

    (一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...

  4. PE文件结构详解(五)延迟导入表

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/12718845 PE文件结构详解(四)PE导入表讲了一般的 ...

  5. PE文件结构详解(四)PE导入表

    PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...

  6. PE文件结构详解(三)PE导出表

    上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...

  7. PE文件结构详解(二)可执行文件头

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/11903197 在PE文件结构详解(一)基本概念里,解释了 ...

  8. PE文件结构详解(三)

    0x01 前言 上一篇讲到了数据目录表的结构和怎找到到数据目录表(DataDirectory[16]),这篇我们我来讲讲数据目录表后面的另一个结构--区块表. 0x01 区块 区块就是PE载入器将PE ...

  9. PE 格式详解与试验

    PE 格式详解与试验 可执行文件结构分析 DOS头 文件头 可选头 PE RVA 地址与文件地址转换 块表 Section Header 导入表 Data Directory 基址重定位 reloc ...

最新文章

  1. php发送指令,PHP发送AT指令实例代码
  2. Git 分支 - rebase 变基
  3. iptables 防火墙
  4. VTK:简单操作之FloatingPointExceptions
  5. linux 进程 控制终端,linux系统编程之进程(五):终端、作业控制与守护进程
  6. 吴裕雄 python 神经网络——TensorFlow实现AlexNet模型处理手写数字识别MNIST数据集...
  7. 学术谱系树:来看看你导师的师承
  8. SPEC-RFC3261总述
  9. [转]mysql慢查询日志
  10. 深入理解JavaScript的设计模式
  11. linux: dirent.h 使用
  12. uniapp实现微信公众号登录获取openid
  13. Leetcode广度优先搜索笔记2 腐烂的橘子
  14. 本地创建git仓库并提交到码云
  15. L1-030——一帮一学习小组”是中小学中常见的学习组织方式,老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组。
  16. 【计算机网络】频带和频段(图解易懂)
  17. 组合导航初始对准(1)
  18. 5、隐私计算--同态加密
  19. 4.4亿赎金,6800GB数据窃取,勒索病毒攻击到底怎么防?
  20. 使整个网页变黑白色(灰色)的特效代码

热门文章

  1. gateway网关配置入门
  2. logback 的拆分 Appender
  3. GraphQL入门之Schema和类型规范
  4. MySQL常用存储引擎之MyISAM
  5. js 实现2的n次方计算函数_JS中数据结构与算法---排序算法
  6. python两个数相加时_怎么用python让两个小数相加
  7. Android 设计模式 - 装饰者模式
  8. The 'microsoft.jet.oledb.4.0' provider is not registered on the local machin
  9. Docker极简入门
  10. 23种设计模式之《单例模式》