PE知识复习之PE的重定位表

          PE知识复习之PE的重定位表

一丶何为重定位  

    重定位的意思就是修正偏移的意思.  如一个地址位 0x401234 ,Imagebase = 0x400000 . 那么RVA就是 1234.  如果Imagebase 变了成了0x300000, 那么修正之后就是 ImageBase + RVA = 0X300000+1234 = 0x301234.

    首先我们知道.一个EXE文件.会调用很多DLL(PE) 有多个PE文件组成.

exe文件启动的基址 (ImageBase) 是0x40000. 假设我们调用三个DLL  A B C.

A DLL 在EXE展开的基址位置是0x10000000

那么恰巧 B DLL 展开的位置也是 0x1000000 两个DLL位置展开地方是一样的.那么就出现问题了.

如下图:

这时候操作系统就会给我们进行修正. 将B DLL 换个内存位置. 进行展开. 这也是为什么很多游戏外挂.等等.都选择DLL注入. 因为系统帮你重定位了各种信息. 代码写在DLL中即可.

如下图: B DLL 从0x20.... 展开了.规避了使用相同地址

虽然这样解决了入口基址不一样.内存展开不一样. 但是我们知道.PE文件中有很多RVA .RVA 是相对于ImageBase的偏移进行存放的. 如果PE文件中都是 RVA 那就好办了.

但是不一定呀.

如一下代码所示:

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int g_Value;
int main()
{
    g_Value = 10;

}
        

查看其反汇编

我们发现,在给全局变量赋值的的时候.地址是一个固定的.他不是 RVA

再次运行截图:

我们发现地址变了. 而且硬编码 是一个固定的. 0x012c813c ,他直接编译到二进制文件中了.

他把  ImageBase + RVA的值. 直接编译到二进制当中. 就是说.这个全局变量的地址. 是一个 RVA + IMAGEBASE 的地址. 但是他是直接编译到二进制中的.

问题所在:

  假设A编译的全局变量的地址是  RVA + iMAGEbase 假设是 0x1012345,那么A展开的位置是 0x10..... 那么全局变量地址是正确的. 但是如果B编译的时候.地址也是1012345. 但是模块基址加载不一样.那么就会出问题了.如下图:

根据上面我们发现了问题所在.所以现在我们需要一张表.记录那个地方需要进行重定位. 我们把这个地方的值改一下即可.

也就是要记录我们修改需要重定位的位置.以上图的代码进行反汇编查看.

也就是记录需要重定位的地方即可.

重定位表就是记录所有需要修正的地址.只要有了重定位表.我们就不用担心我们的ImageBase 没有占住位置.

非常重要的一张表.

二丶重定位表的定位以及结构

  重定位表.的定位在扩展头中的数据目录中. 数据目录的第6项就是重定位表的 RVA偏移.以及重定位表的大小.

定位到重定位表,那么有额外的结构体来描述重定位表.

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;         //存储的值以字节为单位.字节多大.表示了一个重定位快有多大.
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

看着重定位表就两个成员. 其实非常复杂. 我们设 VirtualAddress 为 x 设 SizeofBlock为Y

如下图所示,一个格子为1个字节.

第一行四个字节为x.也就是 Virtualaddress.. y则是第二个成员

SizeOfBlock 成员你的意思. 以字节为单位.代表重定位的快由多大. 我们知道.一个PE文件需要很多地方进行重定位的.比如这个记录的大小为16. 也就是两个重定位块,那么我们的重定位表的大小就是如下图所示:

下面则是新的重定位表.结构就是重定位表的结构,如果SzieofBlock大小为20个字节.那么重定位表大小就是20个自己.

第三个依次类推,重定位表的结束是不停的往下找,知道最后一个重定位表的结构成员你都为0

这样设计的原因:

  算术题解惑.

比如我们有地址 101234 101235 101236 这种修正的地址有10000个.

那么每个地址有4个字节的. 那么 4 * 10000 = 4万个字节. 也就是我们要准备一张4万个字节的表来保存重定位的.

但是我们发现一个规律.我们要修正的表的偏移都很近, 1234 1235 ....

那么我们可不可以这样那. 我们把 100000取出来. 两个字节存储1234 另外两个地址存储1235,不用准备四个字节了.小的偏移我们两个字节存储.这样的话我们的表的字节就会缩小一半.

VirtuallAddress 就是存储了 100000这个值,也就是需要 ""基址"" 公用的地址.

SizeofBlock 就是下面的偏移由多大, 我们要修正的偏移是 VirtualAddress + sizeofBlock下面的值.

如下图:

我们的重定位表,需要修正的基址是 0x11000,大小是54. 那么需要修正的偏移是 "36b0" "36bc" "36e0"....

我们基址 + 偏移就是要修正的位置.

偏移的概念:

重定位表,是按照一个物理页(4kb)进行存储的. 也就是一个4kb内存,有一个重定位块, 一个重定位表只管自己当前的物理页的重定位.

一个重定位表的记录偏移的大小是2个字节,也就是16位. 而记录偏移的大小. 是由 SizeofBlock决定的.

但是我们记录偏移的位置,12位就够了. 高4位.挪作他用. 并不是记录的才会修正偏移.只有高4位为3的时候.才会进行重定位(基址 + 偏移)

真正修复的位置 virtualaddress +  (高四位为3 ?  低12位偏移 : 无所谓的值.)

也就是高四位为3  Vir + 低12位偏移就等于真正要修复的RVA  例如 36b0 高位为3 低12位就是6b0  要修复的RVA = vir + 6b0  ,如果加上当前DLL的ImagebASE 才是真正要修复的虚拟地址 (VA) 我们计算出的是RVA

如果高位不为3,那么这个值是无所谓了.因为内存对齐的原因.

例如上图重定位表.  0x11000代表了当前要进行修复的块位置. 要修复偏移的地址第一个是36b0 . 高位为3是要进行修复.

所以低位为6B0. 所以修复的位置是 0x116b0的位置. 0x116b0 + 当前PE文件的ImageBase就是要进行重定位的位置

当前PE的Imagebase为0x400000  重定位地方为 0x4116b0位置.

我们第一个修正的位置是4116b0位置,从内存中.反汇编查看. 4116b0的位置的值是0x0041813c. 也就是说.这个位置的值.是我们需要重定位的. 也就是我们上面写的程序.为全局变量赋值的时候.全局变量的地址.需要进行更改.

 而需要重定位的值. 则是 全局变量的RVA值 + Imagebase 填写到这里面了. 全局变量是在内存中的data节存储着.所以观看前几篇博客.能知道如何定位全局变量在文件的位置.

三丶总结重定位

    重定位表有两个成员. VirtuallAddress sizeofBlock

    1.virtualladdress 记录了当前物理页需要进行重定位的起始地址.

    2.sizeofBlock 记录了重定位表多大.去掉8个字节(重定位表大小) 下面都是记录了重定位表需要重定位的偏移.

    3.偏移是2个字节存储. 12位存储偏移. 高4位存储是否进行重定位. 高4位为3则需要进行重定位. virtuall + 低12位 就是要修正的 RVA偏移.

    

原文地址:https://www.cnblogs.com/iBinary/p/9742670.html

时间: 2024-11-07 16:50:34

PE知识复习之PE的重定位表的相关文章

PE知识复习之PE文件空白区添加代码

PE知识复习之PE文件空白区添加代码 一丶简介 根据上面所讲PE知识.我们已经可以实现我们的一点手段了.比如PE的入口点位置.改为我们的入口位置.并且填写我们的代码.这个就是空白区添加代码. 我们也可以利用这个知识.实现PEDLL注入. 原理就是 修改入口. 跳转到我们空白区执行我们的代码.我们空白区进行重定位.调用Loadlibary. 并且load的是我们的DLL 实现功能就是 我们只要给PE注入了代码.那么这个PE程序一旦启动就会加载我们的DLL 关于PEDLL注入,后面会有博客分类中会讲

PE知识复习之PE的RVA与FOA的转换

PE知识复习之PE的RVA与FOA的转换 一丶简介PE的两种状态 首先我们知道PE有两种状态.一种是内存展开.一种是在文件中的状态.那么此时我们有一个需求. 我们想改变一个全局变量的初始值.此时应该怎么做.你知道虚拟地址.或者文件位置了.那么你怎么自己进行转换. 也就是说通过文件中的节数据找到在内存中这块数据的位置.或者反之. 寻找之前我们要先弄前几个概念. ImageBase:  模块基址.程序一开始的地址. VA: 全名virtualAddress 虚拟地址. 就是内存中虚拟地址. 例如 0

PE知识复习之PE新增节

PE知识复习之PE新增节 一丶为什么新增节.以及新增节的步骤 例如前几讲.我们的PE文件在空白区可以添加代码.但是这样是由一个弊端的.因为你的空白区节属性可能是只读的不能执行.如果你修改了属性.那么程序就可能出现问题.所以新增一个节可以实现我们的代码. 等等. 1.新增节的步骤 1.在最后一个节位置添加一个节.如果没有空白位置.自己需要给扩展头扩大.并且自己修正节的偏移. 2.修改文件头中节表个数. 3.添加的新节表修改节表的属性. 节.VirtualAddress .这个成员指定了这个节在内存

PE知识复习之PE的导出表

PE知识复习之PE的导出表 一丶简介 在说明PE导出表之前.我们要理解.一个PE可执行程序.是由一个文件组成的吗. 答案: 不是.是由很多PE文件组成.DLL也是PE文件.如果我们PE文件运行.那么就需要依赖DLL.系统DLL就是Kerner32.dll user32.dll等等.这些都是PE文件. 什么是导出表: 导出表就是当前的PE文件提供了那些函数.给别人用. 举个例子: PE文件相当于一个饭店.那么菜单就是导出表. 导出表解盲: 有人认为exe可执行文件.没有导出表.而DLL有导出表.这

PE知识复习之PE扩大节

PE知识复习之PE扩大节 一丶为什么扩大节 上面我们讲了,空白区添加我们的代码.但是有的时候.我们的空白区不够了怎么办.所以需要进行扩大节. 扩大节其实很简单.修改节数据对齐后的大小即可. 并且在PE文件中添加0数据进行填充即可. 首先看一下我们的节表 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //8个字节名字.自己可以起.编译器也可以给定.不重要. union { DWORD Physi

PE知识复习之PE的绑定导入表

一丶简介 根据前几讲,我们已经熟悉了导入表结构.但是如果大家尝试过打印导入表的结构. INT IAT的时候. 会出现问题. PE在加载前 INT IAT表都指向一个名称表. 这样说是没错的. 但是如果你打印过导入表.会发现一个问题. 有的EXE程序.在打印IAT表的时候.发现里面是地址. 原因: 我们的PE程序在加载的时候.我们知道. PE中导入表的子表. IAT表.会填写函数地址. 但是这就造成了一个问题.PE程序启动慢.每次启动都要给IAT表填写函数地址. 我们可不可以在文件中就给填写好.

PE知识复习之PE的两种状态

一丶熟悉PE的整体结构 从下面依次网上看.可以得出PE结构 其中DOS头有DOS头结构 也就是 IMAGE_DOS_HEADER 关于结构体的各项属性.前边已经写过了.本系列博客就是加深PE印象.理解复杂的原理. IMAGE_DOS_HEADER 大小 64个字节    十六进制 0x40字节 IMAGE_FILE_HEADER 大小 20个字节     十六进制 0x14字节 IAMGE_OPTIONAL_HEADER 224个字节 十六进制  0xE0 IMAGE_SECTION_HEADE

也谈PE重定位表

最近研究脱壳,遇到了dll,所以不可避免的需要修复重定位表.以前研究过也脱过不少壳,但都是exe从来没有手工修过重定位表,于是搜索之,恩有这么一篇:<PE重定位表学习手记>,有刚巧手上有看雪段钢等编著的<加密与解密>,一并阅读. <PE重定位表学习手记>中说到: 每个块的首部是如下定义: typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; } IMAGE_BAS

PE文件结构(五)基址重定位

PE文件结构(五) 参考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 基址重定位 链接器生成一个PE文件时,它会假设程序被装入时使用的默认ImageBase基地址(VC默认exe基地址00400000h,dll基地址10000000h),并且会把代码中所有指令中用到的地址都使用默认的基地址(例如 程序代码中 push 10001000,就是把10000000h当做了基地址,把push 10001000写入到文件中).如果一个exe程序中一个dll装载时的地址与其它dll地址发生冲突(因为