zoukankan      html  css  js  c++  java
  • 虚拟内存的简单介绍及PE文件的内存映射

    虚拟内存的简单介绍

    计算机中的内存分为物理内存和虚拟内存,一般情况下,物理内存对于我们是不可见的,而且其原理也非常复杂,需要进入内核层(Ring0)才可能与物理内存打交道。通常我们所看到的全都是虚拟内存,如图2所示。
    在这里插入图片描述
    图2 虚拟内存映射关系,为上图
    通过图2不难发现,系统为每一个正在运行的进程都分配了4GB的虚拟内存,在虚拟内存与物理内存之间是虚拟内存管理器控制着它们之间的调度,虚拟内存与进程都是用户层的概念,因此我们可以轻易接触之,而虚拟内存管理器则处于用户层与系统内核层之间,物理内存则完全处于系统内核层。

    我们来粗略地算算,就算是一般的家用计算机同时运行的进程最少也要有20个,那么就需要80GB的虚拟内存了,但是我们真正的物理内存可能只有512MB,使用这么点空间怎么能满足那么巨大的需求呢?就算是虚拟内存,这相差的也未免有些悬殊了。其实不然,虚拟内存只是进程的一笔虚拟财富,或者说是虚拟空间,只有当需要进行实际的内存操作时,虚拟内存管理才会将“虚拟地址”与“物理地址”联系起来。

    举一个通俗些的例子:进程就相当于公民,内存管理器就相当于政府,物理内存就相当于这个国家的实际空间,虚拟内存就相当于这个国家对公民依法律规定可用空间的总大小。

    进程不使用虚拟内存时,这些虚拟内存只是一些地址,是虚拟存在的,比如人们不去旅游或做其他事情时,这些空间资源都是公用的,是不属于他以及某一个人的;而进程使用虚拟内存时,虽然它理论上可以应用4GB的虚拟内存空间,但是内存管理器只会将它目前所需要的虚拟内存地址映射到物理内存地址上,而且它们之间没有什么必然的联系,比如人们想要出游,虽然他可以到全国任何一处游览并暂时居住,但是政府只保证他得到目前所去的地点的游览权,而不会因此将全国的游览空间与住宿空间向其开放(并且他目前所浏览的景区与全国可以浏览的景区也没有必然的联系)。操作系统的实际物理空间虽然远远小于进程的虚拟内存空间之和,但仍能正常调度,这就像全国的实际可用空间虽然远远小于13亿公民理论可使用的生存空间之和,但是政府仍然可以正常调度一样。

    需要注意的是,Windows系统中还有一个叫硬盘虚拟内存的概念,如图3所示。
    在这里插入图片描述
    图3 硬盘虚拟内存系,为上图
    所谓的虚拟内存,其作用就是将硬盘划分出一块区域,使系统将其当作物理内存来使用。因为它不是真真正正的内存,而是由硬盘虚拟出来的,所以将其称为“虚拟内存”。为了便于区分,我们将其称之为“硬盘虚拟内存”。硬盘虚拟内存在系统中所扮演的角色与真正的物理内存是一样的,同属于“这个国家的实际空间”,只不过这块空间没有在地球上,而是在火星上而已(一块属于这个国家的飞地)。

    因此大家不要将这两个虚拟内存的概念混淆。在知道了虚拟内存的概念后,下面就可以介绍PE文件与虚拟内存之间的映射了。

    PE文件的内存映射

    在进行免杀时,我们最经常接触的就是寻找文件偏移地址与寻找内存地址这两项工作,而正是这两项简单的工作能帮助大家更好地理解这部分内容。

    如果想理解PE文件与虚拟内存之间的映射关系,我们首先需要学习以下重要的概念。

    文件偏移地址(File Offset):PE文件数据在硬盘中存放的地址就称为文件偏移地址,例如我们使用WinHex查看文件时的Offset就是文件偏移地址。所谓的文件偏移地址就是指文件在磁盘上存放时相对于文件开头的偏移。

    装载基址(Image Base):装载基址是指PE文件装入内存时的基地址,如果非要将其与文件偏移地址关联,我们就可以理解为“文件偏移地址的装载基址为0x00000000”,当然这是错误的,但是这样更有利于您的理解。一般情况下,EXE文件的装载基址都为0x00400000,而DLL文件一般都是0x10000000,但这并不是绝对的,都是可以更改的,特别是DLL文件更是如此。

    虚拟内存地址(Virtual Address,VA):虚拟内存地址就是指PE文件被装入内存之后的地址。

    相对虚拟地址(Relative Virtual Address,RVA):相对虚拟地址指的是在没有计算装载基址的情况下的内存地址。

    虚拟内存地址、装载基址与相对虚拟地址之间的关系如下:
    在这里插入图片描述
    我们来仔细看一下图4。
    在这里插入图片描述
    图4 PE文件映射到虚拟内存,为上图
    由图4不难看出,PE文件在映射到虚拟内存后是有很大变化的。首先,PE文件的0字节(第一个字节)所对应的虚拟内存地址为0x00400000,这个就是装载基址(Image Base)了。

    我们知道,文件偏移地址(File Offset)是相对于文件开始处0字节的偏移,而相对虚拟地址(RVA)则是相对于装载基址0x00400000处的偏移。由于操作系统在装载PE文件时仍然是按照PE文件中的各种数据结构来映射的,所以说文件偏移地址与相对虚拟地址(RVA)是有一定的相似之处的。它们之间的差异则是由于数据单位存放的位置不同而造成的。

    首先,PE文件的数据一般是以0x200字节为基本单位进行组织存放的。当一个数据区段(Section)不足0x200字节时,余下的空间则用“00”填充。当一个数据区段超过0x200字节时,下一个0x200数据块将被分配给这个空间使用,余下的空间仍然是用“00”填充。因此PE文件的区段大小永远是0x200的整数倍。其次,当PE文件装入内存时,PE文件的数据存放一般将以0x1000字节为基本单位进行组织存放。其分配标准与磁盘数据组织方式一样,不足0x1000字节的则用“00”填充,超过0x1000的就新分配一个0x1000的数据块。因此内存中的区段大小永远是0x1000的整数倍。

    下表展示的是区段信息,可有助于大家理解。
    在这里插入图片描述
    那么,我们怎么样才能将虚拟内存地址转换为文件偏移呢?

    由于区段装入到内存之后的偏移与文件偏移存在差异,所以当我们进行文件偏移与虚拟内存地址之间的换算时,就要看所转换的地址位于第几个区段内了。由此可以得出以下公式:
    在这里插入图片描述
    计算出RVA后我们就知道参与运算的VA属于哪一个区段了,如果这个值位于0x0F000~0x18000之间,那么就是属于.data区段的,如果大于等于0x18000,那么就是属于.rsrc区段的。

    知道位于哪个区段后,我们就可以用这个计算后的RVA减去区段的起始虚拟偏移,算出VA相对于此区段的起始偏移量,最后加上此区段的文件偏移即可得出VA的文件偏移地址了。具体推导如下:
    在这里插入图片描述
    由此我们得出以下最终结果:
    在这里插入图片描述
    举一个例子,假如我们要由一个PE文件虚拟内存地址算出一个文件偏移地址,现在知道此PE文件的Image Base为0x00400000,PE区段的情况为上表中所示,已知的虚拟内存地址为0x00411210。

    RVA=0x00411210-0x00400000=0x11210,由于0x11210小于.rsrc的起始虚拟偏移,大于.data的起始虚拟偏移,因此我们判定此数据位于.data内。

    由上表中我们知道,区段.data的起始文件偏移为0x600,起始虚拟偏移为0xF000。

    因此可以得出文件偏移Offset=0x600+(0x11210-0xF000)=0x11810。

    就这样,我们计算出了0x00411210这个虚拟内存地址所对应的文件偏移是0x00011810。

  • 相关阅读:
    javascript中的this指向
    面向对象和面向过程、对象、类、实例
    javascript
    实例011:养兔子
    day13匿名函数
    实例010:给人看的时间
    python基础学习day12 生成器与推导式
    实例009:暂停一秒输出
    python基础学习day11函数的进阶
    第一部分:趣味算法入门;第八题:冒泡排序(并与选择排序对比)
  • 原文地址:https://www.cnblogs.com/laohaozi/p/12537595.html
Copyright © 2011-2022 走看看