PE文件结构详解(六)重定位
前面两篇 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保存重定位项在页内的偏移,这样就大大缩小了重定位表的大小。
有了上面的概念,我们现在可以来看一下基址重定位表的定义了:
- typedef struct _IMAGE_BASE_RELOCATION {
- DWORD VirtualAddress;
- DWORD SizeOfBlock;
- // WORD TypeOffset[1];
- } IMAGE_BASE_RELOCATION;
- 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文件结构详解(六)重定位的更多相关文章
- PE文件结构详解(五)延迟导入表
PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...
- PE文件结构详解(四)PE导入表
PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...
- PE文件结构详解(三)PE导出表
上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,D ...
- PE文件结构详解(二)可执行文件头
在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...
- PE文件结构详解(一)基本概念
PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那 ...
- PE文件结构详解
(注:最左边是文件头的偏移量.) IMAGE_DOS_HEADER STRUCT { +0h WORD e_magic // Magic DOS signature MZ(4Dh 5Ah) DOS可执 ...
- PE文件结构详解(三)
0x01 前言 上一篇讲到了数据目录表的结构和怎找到到数据目录表(DataDirectory[16]),这篇我们我来讲讲数据目录表后面的另一个结构——区块表. 0x01 区块 区块就是PE载入器将PE ...
- PE文件详解(六)
这篇文章转载自小甲鱼的PE文件详解系列原文传送门 之前简单提了一下节表和数据目录表,那么他们有什么区别? 其实这些东西都是人为规定的,一个数据在文件中或者在内存中的位置基本是固定的,通过数据目录表进行 ...
- PE文件详解(八)
本文转载自小甲鱼PE文件详解系列教程原文传送门 当应用程序需要调用DLL中的函数时,会由系统将DLL中的函数映射到程序的虚拟内存中,dll中本身没有自己的栈,它是借用的应用程序的栈,这样当dll中出现 ...
随机推荐
- MVC 增加手机站
首先在全局路由(RouteConfig)中加入以下图片中的代码.
- AMQ学习笔记 - 14. 实践方案:基于ZooKeeper + ActiveMQ + replicatedLevelDB的主从部署
概述 基于ZooKeeper + ActiveMQ + replicatedLevelDB,在Windows平台的主从部署方案. 主从部署可以提供数据备份.容错[1]的功能,但是不能提供负载均衡的功能 ...
- 《锋利的jQuery》心得笔记--Three Sections
第六章 1. JavaScript的Ajax的实现步骤: 1) 定义一个函数用来异步获取信息 function Ajax(){ } 2) 声明: var xmlH ...
- c++11之右值引用
本文大部分来自这里,并不是完全着行翻译,如有不明白的地方请参考原文. 在c++中,创建临时对象的开销对程序的影响一直很大,比如以下这个例子: String getName(){ return “Kia ...
- el表达式获取cookie
${cookie.name}将获得对应cookie的对象,比如我们用jsp将一段cookie发送给客户端. Cookie cookie = new Cookie("username" ...
- 【风马一族_Python】 决策树
<机器学习实战>第三章 决策树 ------------------------------------- #1 trees.py 计算给定数据集的香农熵 ---------------- ...
- 常用的php数组排序函数
分享几个php数组排序函数,每个函数出去sort是排序的意思前缀字母的含义分别代表: a 索引 k 数组键 r 逆向 u 用户自定义 顺序排序函数 sort — 对数组排序 ksort — 对数组按 ...
- apache http server 局域网无法访问
apache 本地配置完成测试成功,但局域网内无法访问. 1.主要是本本地的防火墙设置有关,修改防火墙设置就成了 控制面板->系统和安全->Windows 防火墙->允许程序通过Wi ...
- MAC上 nodejs express 安装
最近在MAC上搭建 nodejs环境以及安装 express 框架,遇到了一些问题,不过最后总算还是安装成功了,下面是操作步骤 1.node js 安装 访问nodejs官网进入下载mac上的安装包 ...
- 【转】RunTime.getRunTime().addShutdownHook用法
Runtime.getRuntime().addShutdownHook(shutdownHook); 这个方法的含义说明: 这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行 ...