原文发表于百度空间,2008-11-02
==========================================================================

先定义一下用到的几个变量:
char *hModule=NULL;//映射后的基址
PIMAGE_OPTIONAL_HEADER pOptHeader;//扩展头
PIMAGE_DATA_DIRECTORY pRelocTable=NULL;//指向重定位表
PIMAGE_BASE_RELOCATION pRelocBlock;//指向重定位块
WORD *pRelocData;//16位的重定位数据指针
PE头的定位和分析比较简单,不再多说。
首先,先判断重定位表是否存在:
pRelocTable=&(pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
判断pRelocTable->VirtualAddress和pRelocTable->Size是否为0即可。
若不为0,则重定位表存在。

ModulBase+pRelocTable->VirualAddress即可定位到重定位表。
重定位表的结构如下图所示:

重定位表由一个个的重定位块组成,如果重定位表存在的话,必定是至少有一个重定位块。
因为每个块只负责定位0x1000大小范围内的数据,因此如果要定位的数据范围比较大的话,
就会有多个重定位块存在。

每个块的首部是如下定义:
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION;

把内存中需要重定位的数据按页的大小0x1000分为若干个块,而这个VirtualAddress就是每个块的起始RVA.只知道块的RVA当然还不行,我们要知道每一个需要重定位数据的具体地址。
每个需重定位的数据其地址及定位方式用两个字节来表示,记为RelocData,紧跟在IMAGE_BASE_RELOCATION结构之后,如图所示。
每个块中重定位信息的个数如何确定?
这个可由每个块结构中的Size来确定。Size的值是以DWORD表示的当前整个块的大小,先减去IMAGE_BASE_RELOCATION的大小,因为重定位数据是16位WORD的,再除以2,就得到个重定位数据的个数。由Size可以直接到达下一个重定位块,如图所示:0x5E850000+0x000000EC=0x5E8500EC即为第二个重定位块的地址,直至某个块首结构的VirtualAddress为0,表明重定位表结束。

每个块中重定位数据的个数确定了,如何得知具体每个需进行重定位的数据的地址呢?

每个16位重定位信息包括低12位的重定位位置和高4位的重定位类型。要得到重定位的RVA,IMAGE_BASE_RELOCATION'的'VirtualAddress'需要加上12位位置偏移量. 类型是下列之一:

    IMAGE_REL_BASED_ABSOLUTE () 使块按照32位对齐,位置为0。
IMAGE_REL_BASED_HIGH () 高16位必须应用于偏移量所指高字16位。
IMAGE_REL_BASED_LOW () 低16位必须应用于偏移量所指低字16位。
IMAGE_REL_BASED_HIGHLOW () 全部32位应用于所有32位。.
IMAGE_REL_BASED_HIGHADJ () 需要32位,高16位位于偏移量,低16位位于下一个偏移量数组元素,组合为一个带符号数,加上32位的一个数,然后加上8000然后把高16位保存在偏移量的16位域内。
IMAGE_REL_BASED_MIPS_JMPADDR () Unknown
IMAGE_REL_BASED_SECTION () Unknown
IMAGE_REL_BASED_REL32 () Unknown

以第一个重定位数据0x34AC为例,其高四位表明了重定位类型为3,即IMAGE_REL_BASED_HIGHLOW,Win32环境下的重定位基本都是这个类型的。

其低12位则表明了相对于VirtualAddress的RVA偏移量。VirtualAddress即需重定位的数据块的起始RVA,再加上这低12位的值就得到了具体的需要进行重定位处理的数据的RVA。感觉说得有点乱,总之就是VirtualAddress与每一个16位重定位数据一起可以得到一个具体要进行重定位处理的数据的RVA。

也就是说:
要进行重定位处理的数据的RVA=VirtualAddress+RelocData&0x0FFF 
=0x00001000+(0x34AC)&0x0FFF
=0x000014AC

再加上模块基址,就得到了在内存中的真实地址了
由ModuleBase=0x5E830000,可得这个重定位数据的地址为:0x5E830000+0x000014AC=0x5E8314AC
转到OD的反汇编窗口,Ctrl+G跳到这个地址,可以看到:

OD自动标上了下划线,表明这是一个重定位数据,接下来的几个数据也可以一一进行对应.
至此,重定位算是搞明白了。

那如何进行修正呢?
把需要修正的数据减去IMAGE_OPTINAL_HEADER中的ImageBase,再加上当前加载的实际基址,就可以了。至此重定位完成!当然,也可以根据别的基址进行重定位。比如加载ntoskrnl.exe时,按照ntoskrnl.exe在内存中加载的实际基址0x804E0000(我系统上的数据)进行重定位之后,然后就可以干很多事情了~~比如查找原始SSDT,或者进行 InlineHook检测,搜索未导出函数等等,总之比较有用。
下面是我程序中的一段代码:

pRelocBlock=(PIMAGE_BASE_RELOCATION)(m_hModule + m_pRelocTable->VirtualAddress);
//printf("After Loaded,Reloc Table=0x%08X\n",pRelocBlock);
do
{ //处理一个接一个的重定位块,最后一个重定位块以RAV=0结束
//需要重定位的个数,是本块的大小减去块头的大小,结果是以DWORD表示的大小
//而重定位数据是16位的,那就得除以2
int numofReloc=(pRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION))/;
//printf("Reloc Data num=%d\n",numofReloc);
//重定位数据是16位的
WORD offset=;
WORD *pRelocData = (WORD*)((char*)pRelocBlock + sizeof(IMAGE_BASE_RELOCATION));
for (i=;i<numofReloc;i++)//循环,或直接判断*pData是否为0也可以作为结束标记
{
DWORD *RelocAddress=;//需要重定位的地址 //重定位的高4位是重定位类型,
if (((*pRelocData)>>) == IMAGE_REL_BASED_HIGHLOW)//判断重定位类型是否为IMAGE_REL_BASED_HIGHLOW
{ //计算需要进行重定位的地址 //重定位数据的低12位再加上本重定位块头的RAV即真正需要重定位的数据的RAV
minioffset=(*pRelocData)&0xFFF;//小偏移 //模块基址+重定位基址+每个数据表示的小偏移量
RelocAddress=(DWORD*)(hModule + pRelocBlock->VirtualAddress+offset); //对需要重定位的数据进行修正
//修正方法:减去IMAGE_OPTINAL_HEADER中的基址,再加上实际基址即可
*RelocAddress=*RelocAddress - pOptHeader->ImageBase + hModule;
}
//指向下一个重定位数据
pRelocData++;
}
//指向下一个重定位块
pRelocBlock=(PIMAGE_BASE_RELOCATION)((char*)pRelocBlock + pRelocBlock->SizeOfBlock);
}while (pRelocBlock->VirtualAddress);

这些工作只是自己加载PE过程的一部分,自己加载PE有很多用处,主要是因为加载的是原始的文件,只要不是固化挂钩,都可以自己加载然后与内存中的进行对比,用处还是不少的~

参考:
1.sudami的《SDT Restore v0.2 学习手记》。不过那个源码中用了太多自定义的结构,使得读起来稍有些困难,其实这些结构都是有现成定义的。
2.看雪论坛的《PE文件格式》

【旧文章搬运】PE重定位表学习手记的更多相关文章

  1. 逆向-PE重定位表

    重定位表 ​ 当链接器生成一个PE文件时,会假设这个文件在执行时被装载到默认的基地址处(基地址+RVA就是VA).并把code和data的相关地址写入PE文件.如果像EXE一样首先加载就是它image ...

  2. Windows PE 重定位表编程(枚举重定位地址)

    原理之前单独总结过,在这里: http://blog.csdn.net/u013761036/article/details/54051347 下面是枚举重定位信息的代码: // ReLocation ...

  3. 【旧文章搬运】Windows句柄表分配算法分析(一)

    原文发表于百度空间,2009-03-30========================================================================== 阅读提示: ...

  4. 【旧文章搬运】Windows句柄表格式

    原文发表于百度空间,2009-02-28========================================================================== 句柄是Wi ...

  5. 【旧文章搬运】Windows句柄表分配算法分析(实验部分)

    原文发表于百度空间,2009-03-31========================================================================== 理论结合实 ...

  6. 【旧文章搬运】Windows句柄表分配算法分析(三)

    原文发表于百度空间,2009-03-30========================================================================== 三.当需要 ...

  7. 【旧文章搬运】Windows句柄表分配算法分析(二)

    原文发表于百度空间,2009-03-30========================================================================== 四.句柄表 ...

  8. PE格式第七讲,重定位表

    PE格式第七讲,重定位表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶何为重定位(注意,不是重定位表格) 首先, ...

  9. Windows PE第6章 栈与重定位表

    第六章 栈与重定位表 本章主要介绍栈和代码重定位.站和重定位表两者并没有必然的联系,但都和代码有关.栈描述的是代码运行过程中,操作系统为调度程序之间相互调用关系,或临时存放操作数而设置的一种数据结构. ...

随机推荐

  1. spring boot 添加mybatis,以及相关配置

    首先在pom.xml文件里加入 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifa ...

  2. 前端高频面试题 CSS篇

    通过从网上看到的面经,总结的一些高频的前端CSS面试题,有很多不会,于是找资料,并通过代码实现,自己提供了一些参考答案. 目录 1.怎样会产生浮动? 2.如何清除浮动? 3.盒子模型,以及IE和其他浏 ...

  3. 总结下JavaWeb应用里正确显示中文需要的设置

    1.前台页面需要加的设置: <%@ page contentType="text/html; charset=UTF-8"%> html标签后加上<meta ht ...

  4. antd 如何让 Row 中的 Col 自动换行?

    1.解决方案 在需要换行处,设置一个空的 Col // 空白(特殊情况处理) const empty = ( <Col md={6} sm={24}></Col> ); .

  5. HDU 2018 母牛的故事 [补]

    今天刚考完试,和杨曙光玩了RPG,实在不想看题了 /***************************************************/ 母牛的故事 Time Limit: 200 ...

  6. Linux上Libevent的安装

    1.下载wget -O libevent-2.0.21-stable.tar.gz https://github.com/downloads/libevent/libevent/libevent-2. ...

  7. ITOO高校云平台之考评系统项目总结

    高校云平台,将云的概念引入到我的生活, 高校云平台主要是以各大高校的业务为基础设计开发,包含权限系统,基础系统.新生入学系统.考评系统,成绩系统.选课系统,视频课系统.3月份參加云平台3.0的开发,至 ...

  8. 利用ctypes调用Fortran程序

    本来python下面调用fortran最傻瓜方便的办法就是f2py,但是若fortran和C混合编程的代码,分别指定gfortran和gcc为编译器,在windows下面f2py直接报错 那么ctyp ...

  9. android自己定义开关控件

    近日在android项目要使用开关控件.可是android中自带的开关控件不太惬意,所以就打算通过自己定义View写一个开关控件 ios的开关控件当然就是我要仿照的目标. 先上图:   waterma ...

  10. Mysql性能优化笔记

    一,索引 1.Innodb索引使用的是B+树 2.尽量简化where条件,比如不要出现 where id + 3 = 5,这无法使用索引 3.索引很大时,可以冗余一列来模拟哈希索引 4.小的表不需要使 ...