转载请注明出处:http://www.cnblogs.com/lanrenxinxin/p/4735027.html

在阅读NewBluePill源码的时候,看内存的那一块简直头疼,全是x64下的寻址,之前根本就没有接触过x64的内存寻址上的内容,看的晕头转向,决定先把x64下的寻址给弄明白了再回过头来看NewBluePill的源码,然后在网上一顿找,居然没有找到关于x64寻址的博客或者文章,简直痛苦啊,终于把x64的寻址问题弄清楚了,总结出来分享一下学习历程。

0x01    x64寻址简介

在保护模式,CPU发出的线性地址,内存管理单元(MMU),根据当前CR3寄存器所指向的页表物理地址将该线性地址翻译成物理地址进行内存访问,该过程称为地址翻译。

在x64体系结构中,线性地址的结构如图

在x64体系中只实现了48位的virtual address,高16位被用作符号扩展,这高16位要么全是0,要么全是1。

不同于x86体系结构,每级页表寻址长度变成9位,由于在x64体系结构中,普通页大小仍为4KB,然而数据却表示64位长,因此一个4KB页在x64体系结构下只能包含512项内容,所以为了保证页对齐和以页为单位的页表内容换入换出,在x64下每级页表寻址部分长度定位9位。

为了正确翻译x64的线性地址,其页表也从x86的2级变成了4级,翻译过程如图所示,在x64体系结构中,每级页表包含512项(2^9)下级目录的指针,该指针称为页表项,描述了存储下级

  • PML4T(Page Map Level4 Table)及表内的PML4E结构,每个表为4K,内含512个PML4E结构,每个8字节
  • PDPT (Page Directory Pointer Table)及表内的PDPTE结构,每个表4K,内含512个PDPTE结构,每个8字节
  • PDT (Page Directory Table) 及表内的PDE结构,每个表4K,内含512个PDE结构,每个8字节
  • PT(Page Table)及表内额PTE结构,每个表4K,内含512个PTE结构,每个8字节。

每个table entry 的结构都是8个字节64位宽,而virtual address中每个索引值都是9位,因此每个table都是512 x 8 = 4K字节。

0x02   页转换模型

X64,准确的说应该是IA32e paging 模型提供了三种页转换模型,

① 4K页面的转换表结构;

② 2M 页面的转换结构;

③ 1G页面的转换结构;

在64位模式下,处理器将48的虚拟地址转化为物理地址,在兼容模式下,转化32位的虚拟地址。

三种模型都是物理页帧的基地址加上页偏移得到物理地址,不同只是在于页帧的大小划分不同:

①4K页面: 使用PML4T,PDPT,PDT和PT 四级页转化表结构;

②2M页面:使用PML4T,PDPT 和PDT三级页转化表结构;

③1G 页面:使用PML4T和PDPT二级页表转化结构。

而在这里我们主要讨论的是4K页面大小的寻址方式,因为在个人计算机上,普遍都是4K

页面寻址,其他的方式也主要就是页面大小的差异。

0x03   最大物理地址

在Intel中使用MAXPHYADDR来表示最大的物理地址,我们可以通过CPUID的指令来获得处理支持的最大物理地址,然而这已经不在此次的讨论范围之内,我们需要知道的只是:

当MAXPHYADDR 为36位,在Intel平台的桌面处理器上普遍实现了36位的最高物理地址值,也就是我们普通的个人计算机,可寻址64G空间;

当MAXPHYADDR 为40位,在Inter的服务器产品和AMD 的平台上普遍实现40位的最高物理地址,可寻址达1TB;

当MAXPHYADDR为52位,这是x64体系结构描述最高实现值,目前尚未有处理器实现。

而对下级表的物理地址的存储4K页面寻址遵循如下规则:

① 当MAXPHYADDR为52位时,上一级table entry的12~51位提供下一级table物理基地址的高40位,低12位补零,达到基地址在4K边界对齐;

② 当MAXPHYADDR为40位时,上一级table entry的12~39位提供下一级table物理基地址的高28位,此时40~51是保留位,必须置0,低12位补零,达到基地址在4K边界对齐;

③ 当MAXPHYADDR为36位时,上一级table entry的12~35位提供下一级table物理基地址的高24位,此时36~51是保留位,必须置0,低12位补零,达到基地址在4K边界对齐。

0x04    实际转化

l  CR3

当CR4.PCIDE = 0时,CR3的结构如图,

CR3可以使用64位宽,但是它表示的PML4T的物理基地址同样受到之前所说的MAXPHYADDR的约束,图示的只是理想的MAXPHYADDR为52位时的情况。

而当CR4.PCIDE = 1的时:

R3的低12位提供一个PCID值,用来定义当前Process Context ID.

当对CR3进行更新时,CR3第63位决定是否需要处理器的TLB和paging-struct cache,这不在我们此次谈论的范围之内。

l  PML4E

接着再看PML4E的结构,如图:

PML4E并没有PS标志位,因此第7位是保留的,而PML4E提供的PDPT的物理基地址也受之前的MAXPHYADDR规则的约束。

l  PDPTE

然后就是PDPTE结构:

由于新增了1G 页面,因此在PDPTE结构里将控制1G的页面转化,由PDPTE.PS标志位进行转换,如图:

当PDPTE.PS=1,也就是PDPTE的第7位为1时,PDPTE将提供1G的物理页面地址;当PDPTE.PS=0,也就是PDPTE的第7位为0时,使用非1G的页面,将提供下一级的PDT的物理基地址,同样受MAXPHYADDR规则的约束。

1G页面下的PDPTE 的结构解析如下:

同样地,PDPTE提供的1G页面的物理地址也遵守MAXPHYADDR的规则,1G页面的地址低30将补0,意味着1G边界上对齐。

4K和2M页面下的PDPTE结构解析如下:

将提供下一级PDT的物理基地址,同样也遵循MAXPHYADDR规则,那么再根据PDE.PS再决定是使用2M页面还是4K页面。

l  PDE

PDE的结构和PDPTE类似,也是用PS(第7位)表示是使用2M的页面还是4K 的页面,下面是2M 页面的PDE结构解析:

同样对于页面的物理基地址也遵循MAXPHYAD原则。

接下来是4K 页面的 PDE 结构解析:

也遵循MAXPHYADDR 规则。

l  PTE

PTE的结构解析:

同样遵循MAXPHYADDR规则。

0x05  实际例子

上面写了很多都是原理性的东西,可能看完之后对于x64还没有很清晰的认识,我们以一个很简单的例子来加深对于x64结构体系的寻址的认识。

#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{char szName[20] = "HelloWorld";printf("szName:%x\n",szName);getchar();return 0;}

很简单的一个程序,就是打印出szName的虚拟地址,运行结果如下:

我们接下来要做的就是将0x2ffde8这个虚拟地址转换成物理地址,在物理页上找到我们的”HelloWorld”。

0x2ffde8  ===>  转换二进制:

000000000     000 0000 00     00 0000 001      0 1111 1111        1101 1110 1000

0                    0                    1                      0xff                      0xde8

PML4E索引       PDPTE索引          PDE索引              PTE索引                   页内偏移

目标进程的DirBase为0x7d838000,根据我们之前学习的寻址方式,应该是按照MAXPHYADDR为36位的规则,即上一级table entry的12~35位提供下一级table物理基地址的高24位,此时36~51是保留位,必须置0,低12位补零。

因为PML4E的索引为0,所以我们的目标PML4E项的值为0x02b00000~7d274867,

12~35位为0x07d274,低12位补零,则:

PDPTE的索引也为0,目标PDPTE项的值为 0x03000000~7d737867,

PS位(第7位)为0,12~35位为 0x 07d737 ,低12位补零,则:

因为PDE的索引为1,所以我们要加上8,目标PDE项的值为 :0x01500000~7d7bb867

PS位(第7位)为0,12~35位为 0x07d7bb,低12位补零,则:

PTE的索引为0xff,所以要加上0xff*8,得到目标PTE项的值为:0x89a00000~7d084867

12~35位为 0x07d084,低12补零,得到页面物理基地址,再加上页面偏移,我们是0xde8,则:

终于在物理页上看到了我们熟悉的“HelloWorld”。

x64 结构体系下的内存寻址相关推荐

  1. X86保护模式下的内存寻址

    段选择器 :32位汇编中16位段寄存器(CS.DS.ES.SS.FS.GS)中不再存放段基址,而是段描述符在段描述符表中的索引值,D3-D15位是索引值,D0-D1位是优先级(RPL)用于特权检查,D ...

  2. 【JVM】内存结构(下)

    10. 对象实例化内存布局与访问定位 10.1 对象的实例化 10.1.1 创建对象的方式 10.1.2 创建对象的步骤 public class ObjectTest {public static ...

  3. 【Windows 逆向】CE 地址遍历工具 ( CE 结构剖析工具 | 从内存结构中根据寻址路径查找子弹数据的内存地址 )

    文章目录 一.CE 结构剖析工具 二.从内存结构中根据寻址路径查找子弹数据的内存地址 一.CE 结构剖析工具 游戏中的数据结构 , 需要靠调试和观察 , 才能发现其中的规律 ; 之前发现的 静态地址 ...

  4. 深入理解Linux内核01:内存寻址

    目录 1. 内存地址 1.1 三种地址 1.1.1 逻辑地址(logical address) 1.1.2 线性地址(linear address) 1.1.3 物理地址(physical addre ...

  5. Linux操作系统原理与应用02:内存寻址

    目录 1. 内存寻址 1.1 X86寻址技术演变 1.1.1 8086引入段机制 1.1.2 80286引入保护模式 1.1.3 80386在段寄存器上构建保护模式 1.2 80x86寄存器简介 1. ...

  6. 内存寻址:逻辑地址到物理地址的转化

    内存寻址:逻辑地址到物理地址的转化 在计算机里,内存地址分为虚拟内存地址和物理内存地址. 数据存放在物理内存中,程序运行时使用的是虚拟内存,并通过虚拟内存地址访问数据和代码. 那操作系统是如何将虚拟内 ...

  7. 内存寻址:逻辑地址到物理地址转化

    我们知道,在计算机里,内存分为虚拟内存和物理内存. 数据是存放在物理内存中的,而程序中使用的是虚拟内存并通过虚拟内存地址来访问数据和代码的,那么操作系统是如何 将虚拟内存地址映射成为实际的物理内存的呢 ...

  8. 深入理解Linux内核-第3版 第二章 内存寻址 内核2.6.11 强调:本章出现参见其他章节的地方不做深究,留到看到对应章节时深究

    本章介绍寻址技术.值得庆幸的是,操作系统自身不必完全了解物理内存:如今的微处理器包含的硬件线路使内存管理既高效又健壮,所以编程错误就不会对该程序之外的内存产生非法访问. 作为本书的一部分,本章将详细描 ...

  9. 《LINUX3.0内核源代码分析》第一章:内存寻址

    https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要:本章主要介绍了LINUX3.0内存寻址方面的内容,重点对follow_page ...

最新文章

  1. 插入排序的基本原理及实现
  2. Nginx 学习笔记(五)nginx-vod-module 模块
  3. Spring Data JPA初使用 *****重要********
  4. java 文件解析异常_java中异常的解析
  5. 今天的绿得像碧玉的 飞鸽
  6. Android开发:《Gradle Recipes for Android》阅读笔记(翻译)5.2——使用Android Testing Support Library进行测试...
  7. PHP被浏览器解释成注释,HTML+CSS入门 在HTML中嵌入的php代码会被浏览器注释掉如何解决...
  8. 性能进阶:使用JMeter进行websocket测试
  9. fft 相位谱_数值积分——使用FFT来降低计算量
  10. POJ 1182 食物链(带权并查集)
  11. cocos2d环境及创建一个自己的项目
  12. hexo 环境变量_优雅的博客框架,快速、简洁、高效且主题丰富——Hexo
  13. 用户画像第四章(企业级360°用户画像_标签开发_挖掘标签_ 客户价值模型-RFM)
  14. c语言flag什么意思,立flag是什么意思flag是什么?立flag用语出处和使用方法
  15. 台达 PLC 绝对定位
  16. c++ encode 函数_encode 在C++中的用法
  17. win7网上邻居_CentOS7 Linux访问Win7的共享文件夹
  18. 周易六十四卦——比卦
  19. 葡,西两国发展史(大航海时代)启示
  20. 编码首行缩进使用Tab键好还是空格好?

热门文章

  1. Nature:将基因测序带到前所未有的精度,人类首次具有在任何组织中研究基因突变的能力
  2. 罗莎琳德·富兰克林:隐于幕后的DNA之母,以及她被误解却又伟大的短暂一生...
  3. python去除php、java、js、html、vue等类型注释字符方法实例
  4. js原生实现过渡效果的返回顶部功能实例
  5. java redis hash_我爱java系列---【redis中如何存取hash类型的值(key field value)】
  6. c 读取mysql另一个窗体中显示出来_二级ACCESS数据库4窗体的笔试题考点分析
  7. 两个重要极限_算法数学基础-概率论最重要两个结论:大数定律及中心极限定理...
  8. python第三方模块安装路径_Python第三方Window模块文件的几种安装方法
  9. uniapp 支付(支付宝,微信支付)
  10. 电力笔记-30个行业专业词汇(Ⅰ期)