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 = { };
virtualAddress.ulVirtualAddress = *pOutAddress;
ULONG pdbr; _asm{
mov eax, cr3;
mov pdbr, eax;
} PHYSICAL_ADDRESS phyAddress = { };
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 * ; // 页表项
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 = { };
virtualAddress.ulVirtualAddress = *pOutAddress;
ULONG pdbr; // 得到页目录指针物理地址
_asm{
mov eax, cr3;
mov pdbr, eax;
} // 映射为虚拟地址以便取值
PHYSICAL_ADDRESS phyAddress = { };
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] = { }; // 以下code在 DriverEntry 中 DWORD dwPE = ; // Protection Enable cr0[0]
DWORD dwWP = ; // Write Protect cr0[16]
DWORD dwPG = ; // Paging cr0[31]
DWORD dwPAE = ; // 物理地址扩展 cr4[5]
DWORD dwPSE = ; // Page Size Extension cr4[4]
DWORD dwCr0 = ;
DWORD dwCr4 = ; // 注册卸载函数
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 ( != 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 ( != dwWP){
RtlStringCchCatA(
g_szMemInfo,
BUFFERSIZE,
"内存写保护(WP)开启...\r\n"
);
}
else{
RtlStringCchCatA(
g_szMemInfo,
BUFFERSIZE,
"内存写保护(WP)禁止...\r\n"
);
} //----------------------------------------------------------------------------
// PG标志位
if ( != dwPG){
RtlStringCchCatA(
g_szMemInfo,
BUFFERSIZE,
"页机制(PG)启用\r\n"
);
}
else{
RtlStringCchCatA(
g_szMemInfo,
BUFFERSIZE,
"页机制(PG)禁止\r\n"
);
} //----------------------------------------------------------------------------
// PAE标志位
if ( != dwPAE){
RtlStringCchCatA(
g_szMemInfo,
BUFFERSIZE,
"物理地址扩展(PAE)已开启\r\n"
);
}
else{
RtlStringCchCatA(
g_szMemInfo,
BUFFERSIZE,
"物理地址扩展(PAE)未启用\r\n"
);
} //----------------------------------------------------------------------------
// PSE标志位
if ( != 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

jpg 改 rar 

Windows虚拟地址转物理地址(原理+源码实现,附简单小工具)的更多相关文章

  1. 深入理解Faiss 原理&源码 (一) 编译

    目录 深入理解Faiss 原理&源码 (一) 编译 mac下安装 安装mac xcode工具包 安装 openblas 安装swig 安装libomp 编译faiss 附录 深入理解Faiss ...

  2. SharedPreferences 原理 源码 进程间通信 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. Laya Timer原理 & 源码解析

    Laya Timer原理 & 源码解析 @author ixenos 2019-03-18 16:26:38 一.原理 1.将所有Handler注册到池中 1.普通Handler在handle ...

  4. 转:微信开发之使用java获取签名signature(贴源码,附工程)

    微信开发之使用java获取签名signature(贴源码,附工程) 标签: 微信signature获取签名 2015-12-29 22:15 6954人阅读 评论(3) 收藏 举报  分类: 微信开发 ...

  5. [转载 java 技术栈] eclipse 阅读跟踪 Java 源码的几个小技巧!

    本文基于Eclipse IDE,我们每天都使用的IDE其实提供了很多强大的功能,掌握它们,往往能够事半功倍. 1.Quick Type Hierarchy 快速查看类继承体系. 快捷键:Ctrl + ...

  6. renren-fast后端源码参考-配置和对应工具

    1. renren-fast后端源码参考-配置和对应工具 1.1. 前言 renren-fast是个开源的前后端分离快速开放平台,没有自己框架的同学可以直接使用它的,而我打算浏览一遍它的代码,提取一些 ...

  7. 实验 1:Mininet 源码安装和可视化拓扑工具

    实验 1:Mininet 源码安装和可视化拓扑工具 一.实验目的 掌握 Mininet 的源码安装方法和 miniedit 可视化拓扑生成工具. 二.实验任务 使用源码安装 Mininet 的 2.3 ...

  8. Docker源码安装附内网镜像安装演示

    Docker源码安装附内网镜像安装演示 系统版本要求 当前系统版本:CentOS Linux release 7.9.2009 (Core) 内核版本:3.10.0-1160.el7.x86_64 注 ...

  9. 读 Go 源码,可以试试这个工具

    原文链接: 读 Go 源码,可以试试这个工具 编程发展至今,从面向过程到面向对象,再到现在的面向框架.写代码变成了一件越来越容易的事情. 学习基础语法,看看框架文档,几天时间搞出一个小项目并不是一件很 ...

随机推荐

  1. Unity------Unity 脚本基类 MonoBehaviour 与 GameObject 的关系

    Unity 脚本基类 MonoBehaviour 与 GameObject 的关系 标签: unity脚本 2017-03-27 12:55 395人阅读 评论(0) 收藏 举报  分类: Unity ...

  2. 目标检测之rcnn---开启检测新高度优于dpm

    http://www.cnblogs.com/louyihang-loves-baiyan/p/4839869.html http://www.cnblogs.com/louyihang-loves- ...

  3. EF5+MVC4系列(9) Razor视图引擎的核心原理;@符号的使用;输出html的转义

    一:Razor视图引擎的核心原理 Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项 ,他是一个视图引擎 他的核心原理,就是当读取到 @符号的时候,就认为这是开始 ...

  4. 逻辑卷管理LVM 扩容LV容量实例(一)

    实验环境: 一台Linux 服务器添加两块硬盘,一块硬盘容量30G,另一块硬盘容量50G,采用VMware Workstation虚拟机进行模拟实验. 30G硬盘先分成一个分区,分区大小为25G,再创 ...

  5. 关于js中namespace命名空间模式

    命名空间有助于减少程序中所需要的全局变量的数量,并且同时有助于避免命名冲突或过长的名字前缀. 关于命名空间的例子: /** * 创建全局对象MYAPP * @module MYAPP * @title ...

  6. Xcode : svn 无法上传静态库(.a)文件

    1.打开终端,输入cd,空格,然后将需要上传的.a文件所在的文件夹(不是.a文件)拖拽到终端(此办法无需输入繁琐的路径,快捷方便) ,回车:2.之后再输入如下命令:svn add xxx.a,回车:3 ...

  7. Hopewell Project Sharing项目总结分享PPT

    这篇随笔记录的是2013年底,Hopewell Project已经成功验收后,开项目分享会所编写的PPT. 由于此项目是本人带领Team成员一起开发,而且关键技术是自己把控,所以公司希望能开个项目分享 ...

  8. 设置回车的默认按钮detectEnter

    场景: 页面有一个搜索文本框和搜索按钮.正常情况下,当我在搜索文本框输入关键字后按回车键就可以触发搜索按钮进行内容搜索,但由于页面上还有其它按钮,而且默认不是搜索按钮,怎样才能实现回车就触发我们的搜索 ...

  9. 和我一起学《HTTP权威指南》——客户端识别与cookie机制

    客户端识别与cookie机制 服务器需要区别是哪个客户端. 个性化接触 HTTP是匿名.无状态的请求/响应协议. Web站点希望: 对客户端的用户有更多的了解 追踪用户浏览页面的行为 因此,产生了几种 ...

  10. 安装MongoDB 到服务器

    用管理员身份运行CMD > cd C:\Program Files\mongodb\bin > C:\Program Files\mongodb\bin>mongod --dbpat ...