RVA与RWA的关系

原理比较简单:首先判断这个地址是否在PE头中,如果在,文件偏移和内存偏移相等,如果存在于文件的区段中,则利用以下公式:
内存偏移 - 该段起始的RVA(VirtualAddress) = 文件偏移 - 该段的PointerToRawData
内存偏移 = 该段起始的RVA(VirtualAddress) + (文件偏移 - 该段的PointerToRawData)
文件偏移 = 该段的PointerToRawData + (内存偏移 - 该段起始的RVA(VirtualAddress))

DOS头,PE头,块表在内存中的偏移和在磁盘中的偏移是一致的。其余的内存偏移和磁盘偏移是需要转换的。

各种地址的概念:

基址(Image Base):PE文件装入内存后的起始地址。

相对虚拟地址(Relative Virtual Address,RVA):在内存中相对于PE文件装入地址的偏移位置,是一个相对地址。

虚拟地址(Virtual Address,VA):装入内存中的实际地址。

文件偏移(Fill Offset):PE文件存储在磁盘上时,相对于文件头的偏移位置。16进制文件编辑器打开后的地址为文件偏移地址。

虚拟地址(VA) = 基址(Image Base) + 相对虚拟地址)(RVA)

将RVA转换为File Offset,给大家一个非常经典的公式:

设:VK为相对虚拟地址RVA与文件偏移地址File Offset的差值

VA=ImageBase+RVA

File Offset = RVA -VK

File Offset =
VA-ImageBase-VK

+---------+---------+---------+---------+---------+---------+
|  段名称   虚拟地址  虚拟大小  物理地址  物理大小   标志   |
+---------+---------+---------+---------+---------+---------+
|  Name 
   VOffset    VSize    ROffset   
RSize      Flags |
+---------+---------+---------+---------+---------+---------+
|  .text   00001000 
 00000092  00000400  00000200  60000020|
|  .rdata  00002000 
 000000F6  00000600  00000200  40000040|
|  .data   00003000 
 0000018E  00000800  00000200  C0000040|
|  .rsrc   00004000 
 000003A0  00000A00  00000400  C0000040|
+---------+---------+---------+---------+---------+---------+
文件虚拟偏移地址和文件物理偏移地址的计算公式如下:

>>>>>>>VaToFileOffset(虚拟地址转文件偏移地址)
如VA = 00401000 (虚拟地址)
ImageBase = 00400000 (基地址)
VRk = VOffset - ROffset = 00001000 - 00000400
= C00 (得出文件虚拟地址和文件物理址之间的VRk值)
FileOffset = VA - ImageBase - VRk = 00401000 -
00400000 - C00 = 400(文件物理地址的偏移地址)

如VA = 00401325,则:
FileOffset = VA - ImageBase - VRk = 00401325 -
00400000 - C00 = 725

>>>>>>FileOffsetToVa(文件偏移地址转虚拟地址)
如FileOffset = 435(文件偏移地址)
VA = FileOffset + ImageBase + VRk = 435 +
00400000 + C00 = 00401035(虚拟地址)

通常,区块中的数据在逻辑上是关联的。PE 文件一般至少都会有两个区块:一个是代码块,另一个是数据块。每一个区块都需要有一个截然不同的名字,这个名字主要是用来表达区块的用途。例如有一个区块
叫.rdata,表明他是一个只读区块。注意:区块在映像中是按起始地址(RVA)来排列的,而不是按字母表顺序。
另外,使用区块名字只是人们为了认识和编程的方便,而对操作系统来说这些是无关紧要的。微软给这些区块取了个有特色的名字,但这不是必须的。当编程从PE 文件中读取需要的内容时,如输入表、输出表,不能以区块名字作为参考,正确的方法是按照数据目录表中的字段来进行定位。

各种区块的描述:

区块一般是从OBJ 文件开始,被编译器放置的。链接器的工作就是合并左右OBJ 和库中需要的块,使其成为一个最终合适的区块。链接器会遵循一套相当完整的规则,它会判断哪些区块将被合并以及如何被合并。

合并区块:
链接器的一个有趣特征就是能够合并区块。如果两个区块有相似、一致性的属性,那么它们在链接的时候能被合并成一个单一的区块。这取决于是否开启编译器的 /merge 开关。事实上合并区块有一个好处就是可以节省磁盘的内存空间……注意:我们不应该将.rsrc、.reloc、.pdata 合并到++的区块里。

区块的对齐值:

之前我们简单了解过区块是要对齐的,无论是在内存中存放还是在磁盘中存放~但他们一般的对齐值是不同的。

PE 文件头里边的FileAligment 定义了磁盘区块的对齐值。每一个区块从对齐值的倍数的偏移位置开始存放。而区块的实际代码或数据的大小不一定刚好是这么多,所以在多余的地方一般以00h 来填充,这就是区块间的间隙。

例如,在PE文件中,一个典型的对齐值是200h ,这样,每个区块都将从200h 的倍数的文件偏移位置开始,假设第一个区块在400h 处,长度为90h,那么从文件400h 到490h 为这一区块的内容,而由于文件的对齐值是200h,所以为了使这一区块的长度为FileAlignment 的整数倍,490h 到 600h 这一个区间都会被00h
填充,这段空间称为区块间隙,下一个区块的开始地址为600h 。

PE 文件头里边的SectionAligment 定义了内存中区块的对齐值。PE 文件被映射到内存中时,区块总是至少从一个页边界开始。

一般在X86 系列的CPU 中,页是按4KB(1000h)来排列的;在IA-64
上,是按8KB(2000h)来排列的。所以在X86 系统中,PE文件区块的内存对齐值一般等于 1000h,每个区块按1000h 的倍数在内存中存放。

RVA
和文件偏移的转换

在前边我们探讨过RVA 这个词,但对于初次接触PE 文件的朋友来说,显得尤其陌生和无奈。中国人不喜欢老外的缩写,但总要**着接受……不过,在有了前边知识的铺垫之后,现在来谈这个概念大家伙应该能够得心应手了。起码不用显得那么的费解和无奈~

RVA 是相对虚拟地址(Relative Virtual Address)的缩写,顾名思义,它是一个“相对地址”。PE 文件中的各种数据结构中涉及地址的字段大部分都是以 RVA 表示的,有木有??

更为准确的说,RVA 是当PE 文件被装载到内存中后,某个数据位置相对于文件头的偏移量。举个例子,如果 Windows 装载器将一个PE 文件装入到 00400000h 处的内存中,而某个区块中的某个数据被装入 0040**xh 处,那么这个数据的 RVA 就是(0040**xh - 00400000h )= **xh,反过来说,将 RVA 的值加上文件被装载的基地址,就可以找到数据在内存中的实际地址。

DOS 文件头、PE 文件头和区块表的偏移位置与大小均没有变化。而各个区块映射到内存后,其偏移位置就发生了变化。

RVA 使得文件装入内存后的数据定位变得方便,然而却给我们要定位位于磁盘上的静态PE 文件带来了麻烦。

如何换算 RVA 和文件偏移呢?

当处理PE 文件时候,任何的 RVA 必须经过到文件偏移的换算,才能用来定位并访问文件中的数据,但换算却无法用一个简单的公式来完成,事实上,唯一可用的方法就是最土最笨的方法:

步骤一:循环扫描区块表得出每个区块在内存中的起始 RVA(根据IMAGE_SECTION_HEADER
中的VirtualAddress 字段),并根据区块的大小(根据IMAGE_SECTION_HEADER 中的SizeOfRawData 字段)算出区块的结束 RVA(两者相加即可),最后判断目标 RVA 是否落在该区块内。

步骤二:通过步骤一定位了目标 RVA 处于具体的某个区块中后,那么用目标 RVA 减去该区块的起始 RVA ,这样就能得到目标 RVA 相对于起始地址的偏移量 RVA2.

步骤三:在区块表中获取该区块在文件中所处的偏移地址(根据IMAGE_SECTION_HEADER 中的PointerToRawData 字段), 将这个偏移值加上步骤二得到的 RVA2 值,就得到了真正的文件偏移地址。

参考

书:《加密与解密》

视频:小甲鱼 解密系列 视频

区块

在区块表 后面的就是一个一个区块,每个区块占用对齐值的整数倍,一般的文件都有代码块
跟 数据块( 它们的名字一般为.text 跟 .data 但这是可以修改的)。每个区块的数据具有相同的属性。编译器先在obj中生成不同的区块, 链接器再按照一定的规则合并不同obj跟库中的快。例如每个obj中肯定有.text 块, 连接器就会把它们合并成一个单一的.text 块;再如,如果两个区块具有相同的的属性就有可能被合并成一个块。

文件偏移与RVA转换

因为磁盘对齐跟内存对齐可能不同。还有每个块必须是对齐值的整数倍即在磁盘中可执行文件的每个块的大小是磁盘对齐值的整数倍,程序加载到内存中后块的大小是内存对齐值的整数倍。会导致文件偏移与RVA转换的不同,需要转换。

例子:转换输入表的RVA与文件偏移

通过查看可执行文件的二进制可以找到输入表的RVA,但是不能找到输入表的文件偏移。

先查看这个可执行文件的二进制,可以发现这个程序的磁盘对齐为200h内存对齐为1000h(IMAGE_OPTIONAL_HEADER中的SectionAlignment跟FileAlignment)

图片1

用LoadPE可以方便的得到:

图片2

再从可执行文件的二进制找到输入表的RVA(IMAGE_OPTIONAL_HEADER中的DataDirectory[1]就是输入表的信息)

图片3

也可以通过LoadPE方便的查看(点击目录,查看输入表的RVA):

图片4

转化文件偏移与RVA,通过上面的信息还不够,还需要每个区块在文件中跟在内存中的起始地址,可以用LoadPE方便的查看(点击 区段 即可):

图片5

输入表的RVA为204Ch,可以通过区段表知道,它在 .rdata 区段中,它相对 .rdata 头 的值为 4Ch。.rdata 断在磁盘中开始 的 文件偏移值为A00h,

 用4Ch 加 A00h 即可得到输入表的文件偏移为A4Ch,通过LoadPE的“位置计算器” 可验证输入表的文件偏移值确实是A4Ch。

图片6

RVA与RWA的关系的更多相关文章

  1. 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)

    0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...

  2. VA&RVA 和 RVA to RAW

    VA&RVA VA指的是进程虚拟内存的绝对地址,RVA(Relative Virtual Address,相对虚拟地址)指从某个基准位置(ImageBase)开始的相对地址.VA与RVA满足下 ...

  3. .NET、NET Framewor以及.NET Core的关系(二)

    什么是CLR,.NET虚拟机? 实际上,.NET不仅提供了自动内存管理的支持,他还提供了一些列的如类型安全.应用程序域.异常机制等支持,这些 都被统称为CLR公共语言运行库. CLR是.NET类型系统 ...

  4. Angular2入门系列教程3-多个组件,主从关系

    上一篇 Angular2项目初体验-编写自己的第一个组件 好了,前面简单介绍了Angular2的基本开发,并且写了一个非常简单的组件,这篇文章我们将要学会编写多个组件并且有主从关系 现在,假设我们要做 ...

  5. 死磕内存篇 --- JAVA进程和linux内存间的大小关系

    运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...

  6. 页面中多个script块之间的关系

     一:函数声明与函数定义表达式在函数调用间的区别 <script type="text/javascript"> doA(); var doA = function(a ...

  7. Android Ormlite 学习笔记2 -- 主外键关系

    以上一篇为例子,进行主外键的查询 定义Users.java 和 Role.java Users -- Role 关系为:1对1 即父表关系 Role -- Users 关系为:1对多 即子表关系 下面 ...

  8. ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系

    ASP.NET Core的路由是通过一个类型为RouterMiddleware的中间件来实现的.如果我们将最终处理HTTP请求的组件称为HttpHandler,那么RouterMiddleware中间 ...

  9. 问题记录:EntityFramework 一对一关系映射

    EntityFramework 一对一关系映射有很多种,比如主键作为关联,配置比较简单,示例代码: public class Teacher { public int Id { get; set; } ...

随机推荐

  1. Open with Sublime 右键打开

    Open with Sublime Sublime 的右键打开没有了,可以使用下面的bat命令添加.   @echo off SET st3Path=D:\Program Files\Sublime ...

  2. Javascript/CSS/HTML/vue/angularJS/react/jquery/DOM前端编程经典电子书pdf下载

    高级进阶必读 你所不知道的系列,高级开发必掌握. JavaScript这门语言简单易用,很容易上手,但其语言机制复杂微妙,即使是经验丰富的JavaScript开发人员,如果没有认真学习的话也无法真正理 ...

  3. fork 可能导致subprocess崩溃

    https://docs.python.org/zh-cn/3/library/multiprocessing.html 在 3.8 版更改: 对于 macOS,spawn 启动方式是默认方式. 因为 ...

  4. ISO/IEC 9899:2011 条款6.3——转换

    6.3 转换 1.有些操作符将操作数的值自动地从一种类型转换为另一种.本子条款指定了从这么一个隐式转换所要求的结果,以及从一个投射操作(一个显式转换)所要求的结果.在6.3.1.8中所列出的信息概括了 ...

  5. bind智能DNS + bindUI管理系统(postgresql + bind dlz)

    # 软件环境: * Centos 7.6 * bind-9.14.1.tar.gz * postgresql 11 * python 3.7 * django 2.2.1 QPS:单节点1590 qp ...

  6. 【Tomcat】Tomcat 原理架构(一)

    Tomcat是什么 开源的 Java Web 应用服务器,实现了 Java EE(Java Platform Enterprise Edition)的部 分技术规范,比如 Java Servlet.J ...

  7. Qt编写自定义控件69-代码行数统计

    一.前言 代码行数统计主要用来统计项目中的所有文件的代码行数,其中包括空行.注释行.代码行,可以指定过滤拓展名,比如只想统计.cpp的文件,也可以指定文件或者指定目录进行统计.写完这个工具第一件事情就 ...

  8. 机器学习中的数学-线性判别分析(LDA)

    前言在之前的一篇博客机器学习中的数学(7)——PCA的数学原理中深入讲解了,PCA的数学原理.谈到PCA就不得不谈LDA,他们就像是一对孪生兄弟,总是被人们放在一起学习,比较.这这篇博客中我们就来谈谈 ...

  9. Python第一阶段06

    1.面向对象编程: class Dog: def __init__(self, name): self.name = name def bulk(self): print("%s 汪汪汪.. ...

  10. WebException 请求被中止: 操作超时

    HTTP 请求时出现 :WebException 请求被中止: 操作超时 处理HTTP请求的服务器 CPU 100% ,重启后正常.