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(线性地址)中的Directory和Table其实就是个索引,在未开启PAE的情况下,PDE、PTE均是32bit(4字节,所以要Table*4),以上只是原理上的描述,实际上,PDE、PTE的后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的情况下,PDE、PTE均是64bit(8字节,所以要*8),同样PDE、PTE的后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后虚拟地址到物理地址的转换》

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

#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

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

  1. 企业网站源码PHP附企业小程序源码

    时间不会为任何人停留,企业网站开发也不例外.随着时代的变化,趋势也在不断变化,品牌的数字化形象现在比以往任何时候都更加重要.重点已转向移动:但是,网站开发同样重要. 企业网站源码技术选型 演示:m.j ...

  2. 【小程序源码】检讨书生成微信小程序工具源码-安装搭建简单

    对于经常写检讨的小伙伴来说,福音来了 因为这是一款检讨书生成小程序 所以再也不用为了写检讨而烦恼了哦 支持自定义字数下线,主题自定义 支持多种类型检讨比如:学生党的,男朋友,领导演讲稿,共青团申请书等 ...

  3. 【小程序源码】精美UI强大娱乐功能组合微信小程序源码下载,安装简单

    这是一个多娱乐功能的小程序 具体由以下功能组合: 在线音乐 短视频去印 外卖CPS(外卖平台优惠劵) 打车CPS(打车平台优惠劵) 头像功能(多分类头像,另外还支持姓氏头像制作) 图片加水印 表情包功 ...

  4. windows C++ Opengl基础框架源码

    windows C++ Opengl基础框架源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SD ...

  5. java简单计算器课程设计_java仿windows简易计算器课程设计 源码+报告

    [实例简介] java仿windows简易计算器课程设计 源码+报告 课直接运行. [实例截图] [核心代码] Java课设-简易计算器 └── Java课设-简易计算器 ├── Java课程设计.d ...

  6. k线顶分型 python_顶底分型-(K线分类及顶底分型的一种数学原理 源码 贴图)...

    好股票软件下载网(www.goodgupiao.com)提示:您正在下载的是:顶底分型-(K线分类及顶底分型的一种数学原理 源码 贴图) 参考缠论,研究了很多天终于将顶底分型进行了具体的数学量化,涵盖 ...

  7. Unity Fog 原理 源码分析 案例

    Unity Fog 原理 源码分析 案例 效果图 简述 背景知识 clip空间坐标的范围 d3d (near,0), unity remapping to (0,far) 越靠近相机z值越小 open ...

  8. android 2.2下载地址,Windows下载android2.2完整源码

    前两天看到有人给我留言,才发现很久没写过东西了.不过,最近也没做什么东西.下载了一个cygwin,在Windows下载android完整代码. 这个项目的名字来源于:GNU.Cygnus.Window ...

  9. Windows 12 网页版HTML源码

    资源简介 安装和使用 下载代码,无需安装,打开start.html即可 功能可以自行增加或者二开等等 [在线预览] https://tjy-gitnub.github.io/win12/start.h ...

最新文章

  1. mysql where关键字_MySQL WHERE 子句
  2. 网管应当如何管理Windows操作系统
  3. 网站分析数据收集方式详解
  4. TFS 2015 敏捷开发实践 – 看板的使用
  5. 第一次听人用男女关系讲 N(Non-Blocking)I(进)O(出),涨姿势了
  6. tf.train.exponential_decay
  7. 软件项目管理0724:见供应商的体会
  8. Remastersys制作自己的Linux发行版
  9. 5G 时代,AI 如何破竹而出? | AI ProCon
  10. 化学人学python有前途吗-转载:python之蟒开启理论计算化学的新时代
  11. pythonwin1064位_在Windows 10 64位中安装Matplotlib
  12. 765. 情侣牵手--(每日一难phase2--day14)
  13. NRF24L01模块的2个注意点
  14. paypal快速支付流程图
  15. 666RPG(计数dp)
  16. easyui datagrid deleteRow(删除行)的BUG或者updateRow值更新了不展示问题
  17. 手机备忘录里的照片怎么保存到相册里
  18. 中小型企业开发门户网站我建议你选择阿里云的云速美站
  19. 2018年android常用的框架介绍
  20. 淘宝店铺如何做补单计划?

热门文章

  1. php简单分页,php简单实现分页查询的方法
  2. mysql blob取值_MySQL 数据类型:
  3. pymol怎么做底物口袋表面_怎么从文献中发掘一篇新文章?
  4. dart服务器开发性能,DartVM服务器开发(第四天)--代码优化
  5. python中下划线开头的命名_Python 中各种下划线的骚操作:_、_xx、xx_、__xx、__xx__、_classname_...
  6. arm板telnetd为什么运行不了_一种基于ARM的嵌入式系统开发的方案详细讲解
  7. 8086标志寄存器FLAG
  8. 8086除法指令DIV,IDIV
  9. css中的媒体查询_CSS中的媒体查询
  10. dbms支持哪几种数据模型_DBMS中不同类型的数据模型