首页 > 其他分享 >PE文件(十)重定位表

PE文件(十)重定位表

时间:2024-07-13 22:55:06浏览次数:9  
标签:定位 文件 ImageBase 装载 地址 PE

重定位表的引入

程序加载过程

在win32下,每一个PE文件(其可能由多个子PE文件组成)在运行时,操作系统会给分配一个独立的4GB虚拟内存,内存地址从0x00000000到0xFFFFFFFF。其中低2G为用户程序空间,高2G为操作系统内核空间。并且操作系统会将该文件中数据拉伸成内存中数据,从ImageBase开始,分配SizeOfImage大小空间

当一个主PE文件由很多个子PE文件组成,当我们运行主PE文件时,所有的PE文件共享操作系统分配的一个4GB虚拟空间。如下图我们用OD打开ipmsg.exe程序去查看其模块,可以发现,ipmsg.exe由很多其他PE文件组成,这些文件也叫做模块

在上图中,各PE文件的base也就是其在内存中的起始位置,size也就是其在内存中大小

上图是一个PE文件在4GB虚拟内存中的分布,它由多个PE文件组成。从ImageBase(0x00400000)开始,分配空间SizeOfImage(0x3D000)大小

之后装载需要用到的.dll:把ws2help.dll装载到从ImageBase(0x71A10000)开始,分配空间大小为SizeofImage(0x8000)。其他.dll也是如此

最后把EIP指向EOP(AddressOfEntryPoint),这个程序就可以执行了

注意:我们可以自定义每个PE文件的sizeofimage,具体流程是:打开VC->右键你的项目->setting->选择Link->Category设置为Output->在Base address选项中就可以自定义ImageBase了,之后这个程序编译以后,ImageBase就变成了我们所修改的了

程序编译时的问题

问题一:DLL装载地址冲突

一般情况下一个PE文件自身的子.exe文件的ImageBase不会和别的子.exe文件的ImageBase发生冲突

但是默认情况下DLL的ImageBase为0x10000000,因此如果一个PE文件的多个DLL没有合理的修改分配装载起始地址,就可能出现其ImageBase都是同一个地址,造成装载冲突

注意:如果一个DLL在装载到虚拟内存中时,操作系统发现其他DLL已经占用这块空间了,那么这个DLL会依据模块对齐粒度,往后找空余的空间存入,此时,.dll便有了其内存空间,这个过程,本人称作内存再分配。在程序每次运行时,内存再分配的情况都是不一样的

模块对齐粒度:和结构体的字节对齐一样是为了提高搜索的速度(空间换时间),模块间地址也是要对齐的。模块对齐粒度默认 为0x10000,也就是64K

问题二:编译后的绝对地址

一个.dll或.exePE文件中的全局变量,在程序编译完成后,其全局变量的绝对地址就写入PE文件了。如以下文件中a(人为的全局变量)和%d(系统的全局变量),编译完后地址值都是不变的。

 

假设如果这个PE文件在装载时,某DLL装载地址冲突,系统会对该.dll文件进行再分配内存空间。由于这个.dll文件的全局变量地址在编译完成后就不再改变,当程序执行时,程序会按这个绝对地址去寻找全局变量使用。而该.dll文件由于装载时再分配内存,就会出现找不到这个全局变量的问题。

不单是全局变量,.DLL中对外提供的函数的地址在编译完后也是不再改变的,而如果此时DLL装载时其分配内存空间发生了改变了,通过这些不变的函数地址也找不到需要的函数了

重定位表引入

所以如果一个PE文件出现了内存装载冲突的情况,那么就需要重定位表,来记录下来,有哪些地方的数据需要做修改、重新定位,保证在内存再分配后,操作系统能正确找到这些数据

比如再问题二中:这个PE文件各子PE文件没有按照其本来ImageBase去装载,而是进行了内存再分配。那么我们写入的全局变量a的存储地址就会发生变化,但是由于硬编码已经生成:A1 30 4A 42 00,那么重定位表就会把30 4A 42 00这个数据的地址记下来,等到运行时操作系统会根据重定位表找到这个数据,做一个重定位修改,即把0x00424a30这个绝对地址修改成它现在所在的绝对地址,保证全局变量a可以被准确找到,修改的操作由操作系统进行负责。

因为一个PE文件的.exe子PE文件一般只有一个,且是最先装载,所以装载位置和其ImageBase是一致的,不需要内存再分配,而.dll子PE文件有很多,所有操作系统需要考虑其装载的位置是不是预期的位置,如果不是,.dll子PE文件就需要提供重定位表

重定位表的位置

找到可选PE头中的最后一个成员数据目录项(有16个元素的结构体数组):找到第6个结构体,就是重定位表的数据目录

下图便是重定位表的位置:

根据重定位表数据目录的VirtualAddress,便可以获取重定位表的地址

重定位表结构

typedef struct _IMAGE_BASE_RELOCATION

{

DWORD VirtualAddress;

DWORD SizeOfBlock;

//在这里还有一堆未知的数据

} IMAGE_BASE_RELOCATION;

typedef IMAGE_BASE_RELOCATION ,* PIMAGE_BASE_RELOCATION;

该结构体虽然叫做重定位表结构体,但实际上本人认为它叫做块结构体更合适

这是因为一个完整重定位表是由多个块组成的,最后一个块的VirtualAddress和SizeOfBlock都是0x00000000,表示重定位表结束。如下图便是一个完整的重定位表:

1.VirtualAddress

宽度为4字节

由于4GB虚拟内存地址需要32位即4字节的数才能完整表示,所以当有10000个地方要修改,就需要记录下这10000个地方的地址,一个地址4字节,共需大小10000 * 4 = 40000字节空间,如此记录,一张表所占内存大小就过大了

因此操作系统会把一个PE文件分页,内存中一个页的大小为0x1000字节,相当于把文件分成了一小页一小页的。

那么如果在一页中有需要重定位的地方,重定位表就会给这个页安排一个块,这个块的VirtualAddress存储了此页的偏移起始地址(RVA)。由于一页的大小只有0x1000字节(4096),所以用12位二进制数就可以表示的下4096个地址,此时记录地址所需要的空间就大大减少了。由于内存对齐的缘故,所以把这个值用16位存放。多出来的高4位可以用来表示其他的含义,低12位表示需要修改的地方相对于所在页的偏移地址故具体项占16位,

2.SizeOfBlock

表示每个块的大小,单位为字节

3.具体项

在结构体中未知的一堆数据中,每两个字节叫一个具体项。

具体项的高4位表示类型:值为3,即0011。一个块中有多少个高4位为0011的具体项,就表示这个块当中有多少个地方需要做重定位修改

当具体项的值为0时,说明这个数据项的2个字节的数据用来做数据对齐用,可以不用修改。因此我们只需要关注高4位值为3的具体项就可以了。

一块中一共有多少个具体项用(此块的SizeofBlock - 4 - 4 )/ 2进行计算

具体项的低12位的值表示要修改的地方相对于所在块的VirtualAddress(也是项所在页)的偏移地址,该值加上该块的VirtualAddress的结果便是要修改的地方的在内存的绝对地址,这个地址上的数据则需要修改做重定位。

综上所述:一页中如果有要重定位的地方,重定位表给此页安排一块(一块对应一页)。此块的VirtualAddress存储此页的起始地址;具体项占16位,高4位表示类型,低12位表示要修改的地方相对于所在页的偏移地址。

页、块、节的关系

一个PE文件(可执行程序)运行时,装入虚拟内存时操作系统会对程序进行分页。重定位表会根据页进行分块。但程序中的节跟分页和分块没有任何关系

我们用LordPE打开一个有重定位表的PE文件,查看重定位表

发现:这个重定位表分了很多块,第175块中记录的是偏移地址为0xAF000的页中要修改重定位的地方,这些要修改的地方在.text节中。第176块中记录的是偏移地址为0xB000的页中要修改重定位的地方,这些要修改的地方在.data节中

标签:定位,文件,ImageBase,装载,地址,PE
From: https://blog.csdn.net/2301_78838647/article/details/140408261

相关文章

  • opencv中 在特征点匹配代码举例,以及queryIdx和trainIdx的用法
    一、用法在特征点匹配中,queryIdx和trainIdx是匹配对中的两个索引,用于指示匹配点在不同图像或特征向量中的位置。1.假设我们有两幅图像A和B,并使用特征点提取算法(如SIFT)从它们中提取出特征点和对应的描述子。2.在进行特征点匹配时,我们得到了一个匹配对,其中包含了两个特征点:特征点A......
  • Linux常用文件操作命令
    本章将和大家分享Linux常用的文件操作命令。废话不多说,下面我们直接进入主题。一、目录切换(cd命令)在Linux系统中,cd是一个用于切换当前工作目录的命令,它是"changedirectory"的缩写。基本用法如下所示:1、不带参数示例:cd或cd~如果cd命令后没有跟任何参数,它会将当前用户的......
  • Hypertable install of rhel6.0
    1.rpm 安装:(如果已存在,会提示冲突,使用--replacefiles)1.1 编译环境安装gccgcc-c++makecmake(在adminmachine上,放置rpm包的文件里依次执行下面的语句):  sudorpm-ivhcpp-4.4.6-4.el6.x86_64.rpm--replacefiles sudorpm-ivhlibgcc-4.4.6-4.el6.x86_64.rpm--......
  • Type Script的Any和Unknown有什么区别
    TypeScript中的Any和Unknown是两种用于处理不确定或未知类型值的类型,但它们之间存在显著的区别。以下是它们之间的主要区别:1.类型安全性Any:Any类型是一种特殊的类型,它表示任何类型。使用Any类型时,TypeScript编译器会关闭对该变量的类型检查,允许你对变量执行任何操作,在编......
  • Kolla-ansible部署openStack
    目录Kolla-ansible部署openStack1.简介2.环境准备3.部署3.1基础环境配置3.1.1配置主机名,所有节点操作,这里以openstack01为例3.1.2添加hosts3.1.3配置免密登录3.1.4关闭防火墙以及selinux3.1.5设置yum源3.1.6安装docker3.2配置kolla-ansible3.2.1安装相关依赖3.2.2部......
  • 文件路径分隔符的一个小坑
    文件分隔符在不同的系统上是不一样的,windows系统上的文件分隔符是”\“,但是在Linux系统上就是”/“在开发中需要注意这一点将分隔符换为File.separatorFile.separator //可根据系统的默认分隔符来进行变换源码解释/***Thesystem-dependentdefaultname-......
  • bet9链接不再对微软依赖,OpenKylin 首推AI PC 版本
    本文由 bet9链接 вт989点сс人工智慧(AI)和AIPC是最近两大热门词汇,国内的开源操作系统OpenKylin(开放麒麟)推出全新openKylinforAIPC版本。OpenKylin是一个基于Linux的开源作业系统,由OpenKylin社群维护,并得到包括Hygon和Phytium等在内的多间公司的支援。综合中......
  • PowerQuery 汇总系列 - 单个Excel工作薄文件、多工作表
    文章目录1.写在前面,多工作表汇总也有坑2.删除自动生成的步骤,重新开始3.删除自动生成的步骤,开始改写4.指定工作表汇总4.1.按单个关键词4.2.按多个关键词Authors@樊笼星海@w180361@Email:[email protected].写在前......
  • package.json 脚本配置使用环境文件
    脚本使用环境文件"scripts":{"dev":"vite","build:prod":"vitebuild","build:stage":"vitebuild--modestaging","preview":"vitepreview"}dev:运行开发服务器,默认使用.......
  • pysnooper
    [root@ansibledts]#pip3installpysnoop-ihttps://mirrors.aliyun.com/pypi/simpleWARNING:Runningpipinstallwithrootprivilegesisgenerallynotagoodidea.Try`pip3install--user`instead.CollectingpysnoopCouldnotfindaversionthatsatisfies......