Windows虚拟地址转物理地址(原理+源码实现,附简单小工具)

 

                                                                                                           By Lthis

上个月就想写了,一直没时间...网上大概搜了一下,原理与操作倒是一大堆,一直没看到源码实现,总得有人动手,这回轮到我了。东西写得很烂,请大牛勿喷。一直觉得靠源码的方式驱动学习是非常好的一种学习方法,比较直观!声明一下,本教程只有讨论开启PAE与关闭PAE两种,至于PSE是否开启没有管...我的虚拟机默认PSE貌似是开启滴?不知是不是写的小工具有问题....对于x64下的等我有时间再写吧。

东西都上传在压缩包中了,Codes文件夹下是工程源码,Demo文件夹下是测试案例,Tool文件夹放的是小工具的Demo和源码。

我的环境:开发环境(win7 sp1 x64 + vs2013社区版 update5 + wdk8.1

      测试环境(vm10 + win7 sp1 x86

一、先说说未开启PAE的情况,祭出intel手册的经典图例:

 

这幅图就是虚拟地址转为物理地址的原理图(4k页面),看图说话,用伪代码描述一下:

 

1.Directory Entry(PDE)     = PDBR[Directory];

 

2.Page-Table Entry(PTE) = PDE + Table * 4;

 

3.Physical Address  = PTE + Offset;

 

由上可知,Linear Address(线性地址)中的DirectoryTable其实就是个索引,在未开启PAE的情况下,PDEPTE均是32bit4字节,所以要Table*4),以上只是原理上的描述,实际上,PDEPTE的后3位是属性值,所以需要把后3位抹掉。

 

下边上关键代码,基本都步骤都写了注释了,有需要的可以封装成函数。此外,本段代码只是测试用,写的很不规范,比如,在调用MmMapIoSpace应该调用MmUnMapIoSpace释放内存。

 

            // 得到ring3传入的虚拟地址size_t* pOutAddress = (size_t*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);VIRTUAL_ADDRESS virtualAddress = { 0 };virtualAddress.ulVirtualAddress = *pOutAddress;ULONG pdbr;_asm{mov eax,  cr3;mov pdbr, eax;}PHYSICAL_ADDRESS phyAddress = { 0 };phyAddress.LowPart = pdbr;PULONG pPdbr = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);KdPrint(("pdbr = 0x%08X, 映射后的地址0x%p\n", pdbr, pPdbr));// pPdbr[ulDirBaseIdx] 页目录项ULONG ulDirBaseIdx = virtualAddress.stVirtualAddress.dirBaseIndex;ULONG ulDirIdx = virtualAddress.stVirtualAddress.dirIndex;KdPrint(("第一级,已找到页目录所在项:pPdbr[%d]:0x%08X", ulDirBaseIdx,pPdbr[ulDirBaseIdx]));ULONG ulDir = pPdbr[ulDirBaseIdx] & 0xFFFFF000;            // 抹去后3位得到真正的页目录项
ULONG ulDirPlus = ulDir + ulDirIdx * 4;                    // 页表项phyAddress.LowPart = ulDirPlus;PULONG pDirPlus = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);KdPrint(("第二级,已找到页表项:ulDirPlus = 0x%08X, 映射后的地址0x%p\n", ulDirPlus, pDirPlus));ULONG ulPageTable = *pDirPlus & 0xFFFFF000;                // 抹去后3位得到真正的页表项// 得到物理地址ULONG ulPhyAddress = ulPageTable + virtualAddress.stVirtualAddress.offset;// 映射为虚拟地址,获取其值进行验证phyAddress.LowPart = ulPhyAddress;PWCHAR pPhyAddress = (PWCHAR)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);KdPrint(("虚拟地址:0x%08X, 对应物理地址:0x%08X, Value:%S\n", *pOutAddress, ulPhyAddress, pPhyAddress));// 传出对应物理地址*pOutAddress = ulPhyAddress;

 

 

二、开启PAE的情况

 

 

 

同样是4k页面的,伪代码描述如下:

 

1.Dir.Pointer Entry(PDPTE)  = PDPTR[Directory Pointer];

 

2.Director Entry(PDE)  = PDPTE + Directory * 0x8;

 

3.Page-Table Entry(PTE)  = PDE + Table * 0x8;

 

4.Physical Address  = PTE+Offset;

 

在开启PAE的情况下,PDEPTE均是64bit8字节,所以要*8),同样PDEPTE的后3位是属性值,所以需要把后3位抹掉。

 

关键代码如下:

            // 得到传入的ring3层虚拟地址size_t* pOutAddress = (size_t*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);VIRTUAL_ADDRESS virtualAddress = { 0 };virtualAddress.ulVirtualAddress = *pOutAddress;ULONG pdbr;// 得到页目录指针物理地址
            _asm{mov eax,  cr3;mov pdbr, eax;}// 映射为虚拟地址以便取值PHYSICAL_ADDRESS phyAddress = { 0 };phyAddress.LowPart = pdbr;PULONG pPdbr = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);KdPrint(("pdbr = 0x%08X, 映射后的地址0x%p\n", pdbr, pPdbr));// 定位页目录指针表并获取页目录表物理页地址// ulDirAddress 为页目录表物理页地址ULONG ulPointerIdx = virtualAddress.stVirtualAddress.dirPointer;ULONG ulDirBaseAddress = pPdbr[ulPointerIdx];ulDirBaseAddress &= 0xFFFFF000;            // 中间物理地址// 定位页表项ULONG ulDirAddress = ulDirBaseAddress + virtualAddress.stVirtualAddress.dirIndex * 0x8;phyAddress.LowPart = ulDirAddress;PULONG pPageTable = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);ULONG ulPageTable = *pPageTable;ulPageTable &= 0xFFFFF000;                 // 中间物理地址// 定位物理页面ulPageTable += virtualAddress.stVirtualAddress.tableIndex * 0x8;phyAddress.LowPart = ulPageTable;PULONG pPageBase = (PULONG)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);ULONG ulPageBase = *pPageBase;ulPageBase &= 0xFFFFF000;// 得到物理地址ULONG ulPhyAddress = ulPageBase + virtualAddress.stVirtualAddress.offset;// 映射为虚拟地址,获取其值进行验证phyAddress.LowPart = ulPhyAddress;PWCHAR pPhyAddress = (PWCHAR)MmMapIoSpace(phyAddress, sizeof(PHYSICAL_ADDRESS), MmNonCached);KdPrint(("虚拟地址:0x%08X, 对应物理地址:0x%08X, Value:%S\n", *pOutAddress, ulPhyAddress, pPhyAddress));// 传出对应物理地址*pOutAddress = ulPhyAddress;pIrp->IoStatus.Information = cout;

以上代码步骤是参考安于此生的文章写的,看不懂的可以先看看安于此生的文章《启用PAE后虚拟地址到物理地址的转换》

另附上小工具源码,该工具用于检测系统是否开启PAEPSE等。

 

#define BUFFERSIZE    0x3000
char g_szMemInfo[BUFFERSIZE] = { 0 };// 以下code在 DriverEntry 中
    DWORD dwPE  = 0;                // Protection Enable    cr0[0]DWORD dwWP  = 0;                // Write Protect        cr0[16]DWORD dwPG  = 0;                // Paging                cr0[31]DWORD dwPAE = 0;                // 物理地址扩展            cr4[5]DWORD dwPSE = 0;                // Page Size Extension    cr4[4]DWORD dwCr0 = 0;DWORD dwCr4 = 0;// 注册卸载函数pDriverObj->DriverUnload = driverUnload;_asm{pushad;mov eax, cr0;mov dwCr0, eax;// PE标志位and eax, 0x01;mov dwPE, eax;mov eax, cr0;// WP标志位and eax, 0x10000;mov dwWP, eax;mov eax, cr0;// PG标志位and eax, 0x80000000;mov dwPG, eax;// PAE//mov eax, cr4; 机器码如下_emit 0x0F;_emit 0x20;_emit 0xE0;mov dwCr4, eax;and eax, 0x20;mov dwPAE, eax;// PSE_emit 0x0F;_emit 0x20;_emit 0xE0;and eax, 0x10;mov dwPSE, eax;popad;}KdPrint(("PE  = 0x%08X\r\n",dwPE));KdPrint(("WP  = 0x%08X\r\n",dwWP));KdPrint(("PG  = 0x%08X\r\n",dwPG));KdPrint(("PAE = 0x%08X\r\n",dwPAE));KdPrint(("PSE = 0x%08X\r\n",dwPSE));KdPrint(("Cr0 = 0x%08X\r\n",dwCr0));KdPrint(("Cr4 = 0x%08X\r\n",dwCr4));//----------------------------------------------------------------------------// PE标志位if (0 != dwPE){RtlStringCchCatNA(g_szMemInfo, BUFFERSIZE, "----------------------保护模式(PE=1)-------------------\r\n",BUFFERSIZE - sizeof("----------------------保护模式(PE=1)-------------------\r\n"));}else{RtlStringCchCatNA(g_szMemInfo,BUFFERSIZE ,"----------------------实地址模式(PE=0)-------------------\r\n",BUFFERSIZE - sizeof("----------------------实地址模式(PE=0)-------------------\r\n"));}//----------------------------------------------------------------------------// WP标志位if (0 != dwWP){RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"内存写保护(WP)开启...\r\n");}else{RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"内存写保护(WP)禁止...\r\n");}//----------------------------------------------------------------------------// PG标志位if (0 != dwPG){RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"页机制(PG)启用\r\n");}else{RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"页机制(PG)禁止\r\n");}//----------------------------------------------------------------------------// PAE标志位if (0 != dwPAE){RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"物理地址扩展(PAE)已开启\r\n");}else{RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"物理地址扩展(PAE)未启用\r\n");}//----------------------------------------------------------------------------// PSE标志位if (0 != dwPSE){RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"页面大小扩展(PSE)已开启\r\n");}else{RtlStringCchCatA(g_szMemInfo,BUFFERSIZE,"页面大小扩展(PSE)未启用\r\n");}KdPrint(("%s\r\n", g_szMemInfo));

 

最后,看看效果运行图。Demo是在ring3层定义一个Unicoe字符串:“Lthis,然后将其虚拟地址传入ring0层,ring0解析后传出对应的物理地址。

开启PAE下运行的效果:

 

 

 

 

未开启PAE的运行效果:

 

附件地址:链接:http://pan.baidu.com/s/1kTENdnL 密码:g5j7

 

转载于:https://www.cnblogs.com/Lthis/p/4746795.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

飞机大战小游戏1.0版本

小时候大家应该都玩过飞机大战吧,这就是仿的一个飞机大战,但是没有写的很全,只能玩一次,死掉之后需要刷新页面玩第二次,话不说多,上代码: 初始页面: 整个的html代码还是很少&#xf…

记一次Jquery获取值的典型错误

直接上代码: 代码很简单,通过Post的形式提交参数,但是发现提交的data总是空,昨晚有点纳闷,今天一看才发现。。。 获取值得时候的顺序有问题,获取值应该是在onclick事件中。 综上:写Jquery的时间…

Skype For Business 2015实战系列14:创建Office Web App服务器场

Skype For Business 2015实战系列14:创建Office Web App服务器场前面的操作中我们已经成功的安装了Office Web App Server,今天我们将创建Office Web App服务器场。具体步骤如下:配置证书:登陆到OWA服务器,打开服务器管理器,点击“…

SQL Server 2008 基础

SQL Server 2008 基础SQL流程TDS是一种协议,一系列描述两个计算机间如何传输数据的规则。象别的协议一样,它定义了传输信息的类型和他们传输的顺序。总之,协议描述了“线上的位”,即数据如何流动。表格数据流协议是建立在TCP/IP N…

如何通过Git GUI将自己本地的项目上传至Github

ithud是一个程序员以后成长都会使用到的,先不说很多优秀的开源框架都在这上面发布,光是用来管理自己的demo都已经让人感到很方便,用得也很顺畅。而真正让我下定决心使用github的原因是因为两次误操作,将自己所有的学习demo全都删除…

【JSP笔记】第三章 JSP内置对象【上】

2019独角兽企业重金招聘Python工程师标准>>> 1.内置对象简介&#xff1a;JSP内置对象是WEB容器创建的一组对象&#xff0c;不使用new关键就可以是用的对象。 <% out.println(123); %> 2.九大内置对象&#xff1a; outrequestresponsesessionapplication Page …

java 并发编程多线程_多线程(一)java并发编程基础知识

线程的应用如何应用多线程在 Java 中&#xff0c;有多种方式来实现多线程。继承 Thread 类、实现 Runnable 接口、使用 ExecutorService、Callable、Future 实现带返回结果的多线程。继承 Thread 类创建线程Thread 类本质上是实现了 Runnable 接口的一个实例&#xff0c;代表一…

Docker监控方案(TIG)的研究与实践之Influxdb

2019独角兽企业重金招聘Python工程师标准>>> 前言&#xff1a; Influxdb也是有influxdata公司(www.influxdata.com )开发的用于数据存储的时间序列数据库.可用于数据的时间排列。在整个TIG(Telegrafinfluxdbgrafana)方案中&#xff0c;influxdb可算作一个中间件&…

Java第三阶段学习(三、字符流、转换流)

一、字节流读取中文时出现的问题&#xff1a; 文件中有中文时&#xff0c;用字节流读取会出现乱码的问题&#xff0c;因为一个中文为两个字节。 二、字符编码表 编码表&#xff1a;其实就是生活中字符和计算机二进制的对应关系表。 1、ascii&#xff1a; 一个字节中的7位就可以…

windows下OpenSSL加密证书安装步骤与使用方法

OpenSSL加密证书一般用于签名认证&#xff0c;含私钥和公钥。在Linux系统中&#xff0c;OpenSSL一般是已经安装好了&#xff0c;可以直接使用。而在Windows系统中&#xff0c;是需要安装使用的。 最近在使用支付平台时&#xff0c;用到了OpenSSL&#xff0c;鉴于此分享给大家&a…

java源码影视源码搭建教程_新版千月影视app源码+搭建教程

使用notepad批量替换URL【http://】为你的域名(被替换的域名访问有成品不能发布 需要修改的到前台confing里面查询)&#xff0c;替换名称【鲸鹰影视】为你的应用名称&#xff1b;服务端&#xff1a;1.将替换好的后端源码打包上传至站点根目录后解压&#xff1b;2.配置网站伪静态…

Uboot USB模式(RK3288变砖头的解决办法)

RK3288启动后有三种模式&#xff0c;可以分别进行操作。 第一种是normal也就是正常的启动模式。这个模式无法刷固件。一般板子通电就是这个模式 第二种是loader模式。就是刷固件模式。这个模式可以刷各种image。按住recover按键再通电&#xff0c;通过uboot的检测进入这个模式 …

DEV GridView嵌套

/// <summary> /// 绑定主表和明显表到GridView /// </summary> /// <param name"machineProduct">主表</param> /// <param name"configureData">字表</param> private void Mas…

局域网大型文件分发的可能解决方案

客户原来的做法是把文件上传到服务器&#xff0c;然后后形成一个普通的HTTP地址下入网站后台系统&#xff0c;然后客户端用户看到后&#xff0c;则下载下来。但是随着文件越来越大&#xff0c;客户端下载量增加&#xff0c;在局域内网环境中这种文件分发方式的弊端立现。服务器…

一个Option请求引发的深度解析

在当前项目中&#xff0c;前端通过POST方式访问后端的REST接口时&#xff0c;发现两条请求记录&#xff0c;一条请求的Request Method为Options&#xff0c;另一条请求的Reuest Method为Post。想要解决这个疑惑还得从以下3个概念说起。 Http Options Method RFC2616标准&#x…

ionic+AnjularJs实现省市县三级联动效果

建议对ionic和AnjularJs有一定了解的人可以用到&#xff0c;很多时候我们要用到选择省份、城市、区县的功能&#xff0c;现在就跟着我来实现这个功能吧&#xff0c;用很少的代码&#xff08;我这里是根据客户的要求&#xff0c;只显示想要显示的部分省份和其相对应的城市、区县…

Confluence 6 附件存储文件系统的分级

从 Confluence 3.0 开始&#xff0c;附件的存储方式有了重大的改变和升级。如果你是从 Confluence 2.10 及其早期版本升级上来的&#xff0c;请参考 Upgrading Confluence 页面中推荐的升级路径&#xff0c;同时请阅读 Confluence 3.0 文档中 Hierarchical File System Attachm…

你不知道的JavaScript-0

【数组】 删除数组的几种方法&#xff1a; https://www.cnblogs.com/Joans/p/3981122.html http://www.cnblogs.com/qiantuwuliang/archive/2010/09/01/1814706.html 【数字转换】 parseInt(num, radix): 【宽松相等和严格相等】 允许在相等比较中进行强制类型转换&#xff0c…

真是,原来可以这样啊

一下午&#xff0c;解决了两个问题。。。。。 先列上这两个真是Bug的问题&#xff1a; 1、数据库有个表book&#xff0c;里面有个字段 create_time Datetime类型的字段&#xff0c;这个字段是 not null 的。下午下代码往数据库里插入数据时&#xff0c;总是提示&#xff0c;cre…

java运行时异常中文_JAVA——运行时异常(RuntimeException)

Exception中有一个特殊的子类异常RuntimeException运行时异常。如果在函数内抛出该异常&#xff0c;函数上可以不用声明&#xff0c;编译一样通过。如果在函数上声明了该异常。调用者可以不用进行处理。编译一样通过。之所以不用在函数上声明&#xff0c;是因为不需要让调用者处…