在用户态进行虚拟空间地址向物理空间地址的转换

原创 2004年04月05日 23:34:00

http://flier_lu.blogone.net/?id=1428057


    在《自动获取 NT 系统服务描述表与函数名映射表》一文中,我给出了一个从虚地址向物理地址转换的经验函数。

以下为引用:

PHYSICAL_ADDRESS TPhysicalMemoryMapping::LinearAddressToPhysicalAddress(LPCVOID lpVirtualAddress)
{
  PHYSICAL_ADDRESS addr = { 0, 0 };

  if((DWORD)lpVirtualAddress < 0x80000000L || (DWORD)lpVirtualAddress >= 0xA0000000L)
    addr.QuadPart = (DWORD)lpVirtualAddress & 0x0FFFF000;
  else
    addr.QuadPart = (DWORD)lpVirtualAddress & 0x1FFFF000;

  return addr;
}



    这个函数实际上只处理了0x8000000 - 0xA0000000这段内存地址的转换,而对0xA0000000以上内存,则只是用 PA = VA & 0x0FFFF000 保障访问不会出错,这实际上并不能获取这段内存的实际内容。了解 WinNT/2K 内存布局的朋友应该知道,为了系统实现简便并保障地址转换效率,WinNT将0x80000000 - 0xA0000000内存端直接映射到物理内存,转换算法就是一个简单的 PA = VA & 0x1FFFF000;而对 0xA0000000 以上的虚拟地址,则是使用两级页表索引来实现虚地址页向物理地址页的映射(如启用 AWE 则为三级,这里暂不讨论,实现原理类似)。因此要真正访问 0xA0000000 以上虚地址内存,必须读取进程的 PDE/PTE 表。
    但问题是PDE页表一般存放在虚地址 0xC0300000,PTE 页表则从虚地址 0xC0000000 开始。也就是说这些页表本身,就存放在以页表才能访问的虚地址上。要查PDE/PTE表进行虚地址向物理地址转换,首先要知道 0xC0300000 和 0xC0000000 映射在哪个物理页上。这样一来就变成了先有鸡还是先有蛋的问题了,呵呵。 NT 内核本身,则可以通过进程 CR3 寄存器中保存的 PDE 页表起始物理地址直接访问,但不巧的是,访问 CR3 需要有 Ring 0 的状态。因此大多数介绍虚地址向物理地址转换的文章,在描述完两级映射之后,都是提供 Ring 0 层的代码演示实现,例如 webcrazy 的大作《小议Windows NT/2000分页机制》
    因为此类的文章较多了,这里我就不在罗嗦内存结构和转换算法了,有兴趣研究的朋友可以参考 Inside Win2K 3nd 和 webcrazy 的相关文章。

    既然无法知道 PDE/PTE 的物理地址,也不能直接访问 CR3,我就开始琢磨各种迂回的途径。昨天折腾了大半天,翻越了 ntos/ke 和 ntos/mm 目录下的一堆源码,在大大体验了一把 Open Source Windows NT 的优势之后,终于找到了一个还算完美的解决方法 :P

    我们知道 PDE/PTE 是进程相关的。因为每个进程都有自己的虚拟空间,因此必须有一整套独立的页表备查。核心在进行线程切换时,如果两个线程在一个进程内,无需做页表的切换;如果两个线程跨越了进程,就必须将目标线程所在进程的Context载入,这其中就包括我们所需要的 CR3 的内容。而在翻越代码后,我发现 CR3 的内容被保存到 EPROCESS::KPROCESS::DirectoryTableBase[0] 中。这个变量保存了 PDE 页表物理地址和 hyber space PTE 页表物理地址(以后再详细介绍)。
    于是实现思路就清晰了:
    1.取当前进程的 EPROCESS
    2.读取 PDE 页表物理地址
    3.通过分解虚地址获取 PDE/PTE 表的索引
    4.查表获得目标物理页地址,读取物理页内容。

1.取当前进程的 EPROCESS

    当前进程的 EPROCESS 地址在 ntoskrnl.exe 的空间中,因此可以通过直接映射内存访问。使用OpenProcess打开当前进程,获得句柄;使用NTDLL::ZwQuerySystemInformation函数获取所有核心句柄表,线性搜索到进程句柄,其指向的内核对象就是 EPROCESS。

2.读取 PDE 页表物理地址

    PDE 页表物理地址在 EPROCESS 结构的偏移 0x18 处

3.通过分解虚地址获取 PDE/PTE 表的索引

    将目标虚地址如 0xA01A8148 分解为三部分:

    DDDDDDDDDDTTTTTTTTTTBBBBBBBBBBBB
    01234567890123456789012345678901

    高10位是PDE索引;中间10位是PTE索引;末尾12位是页内字节索引。

4.查表获得目标物理页地址,读取物理页内容。

    PDE/PTE表项都是一个DWORD,其高20位定义下一级的索引。

以下为引用:

typedef struct _MMPTE_HARDWARE {
    ULONG Valid : 1;
    ULONG Write : 1;  // UP version
    ULONG Owner : 1;
    ULONG WriteThrough : 1;
    ULONG CacheDisable : 1;
    ULONG Accessed : 1;
    ULONG Dirty : 1;
    ULONG LargePage : 1;
    ULONG Global : 1;
    ULONG CopyOnWrite : 1; // software field
    ULONG Prototype : 1;   // software field
    ULONG reserved : 1;  // software field
    ULONG PageFrameNumber : 20;
} MMPTE_HARDWARE, *PMMPTE_HARDWARE;



    具体查表转换算法如下

以下为引用:

#define GetPdeAddress(base, va) (LPCVOID)((base) + (((ULONG)(va) >> 22) << 2))
#define GetPteAddress(base, va) (LPCVOID)((base) + ((((ULONG)(va) >> 12) & 0x3FF) << 2))

const PHYSICAL_ADDRESS TPhysicalMemoryMapping::LinearAddressToPhysicalAddress(LPCVOID lpVirtualAddress)
{
  PHYSICAL_ADDRESS addr = { 0, 0 };

  if((DWORD)lpVirtualAddress >= 0x80000000L && (DWORD)lpVirtualAddress < 0xA0000000L)
  {
    addr.QuadPart = (DWORD)lpVirtualAddress & 0x1FFFF000;
  }
  else
  {
    MMPTE_HARDWARE PDE, PTE;

    m_pManager->ReadPhysicalMemoryPage(GetPdeAddress(m_pManager->PTE.LowPart, lpVirtualAddress), &PDE, sizeof(PDE));
    m_pManager->ReadPhysicalMemoryPage(GetPteAddress(PDE.PageFrameNumber << PAGE_SHIFT, lpVirtualAddress), &PTE, sizeof(PTE));

    addr.LowPart = (PTE.PageFrameNumber << PAGE_SHIFT) + BYTE_OFFSET(lpVirtualAddress);
  }
  return addr;
}



    知道了目标页面的物理地址,就可以通过读取 /Device/PhysicalMemory 直接获取了

Linux虚拟地址空间概述

原文: http://my.oschina.net/u/1770090/blog/263326 1 虚拟地址空间概述      Linux进程虚拟地址空间是linux内存管理一个重要的...
  • weichushun
  • weichushun
  • 2015年11月10日 00:12
  • 726

内核态空间地址直接映射到用户态空间访问

【摘要】Linux中的内核空间到用户空间的地址映射让用户层应用可以直接访问内核地址,这就是mmap方法。应用程序通过内存映射可以直接访问设备的I/O存储区或DMA缓冲。内存映射使用户空间的一段地址关联...
  • xy010902100449
  • xy010902100449
  • 2015年07月23日 21:31
  • 3027

[知识点]物理地址(空间)与虚拟地址(空间)

物理地址(空间)与虚拟地址(空间) 注:这里我们以32位CPU为例,来简单的说明这个复杂的问题。 一、物理地址(空间)     因为CPU是32位的,其地址总线是32位的,所以其地址总线可编码...
  • jiasike
  • jiasike
  • 2014年08月26日 19:53
  • 1045

链接、装载与库——虚拟地址空间

第一章 温故而知新 part.2
  • kongkongkkk
  • kongkongkkk
  • 2017年03月16日 21:58
  • 296

(转)CPU地址空间小结

一、地址空间映射 这里要说的是Intel构架下的CPU地址空间布局,注意这里没有说是内存地址空间布局。        我们说的内存通常是指DRAM,DRAM相对于CPU也可以算是外部设备,CPU...
  • gjq_1988
  • gjq_1988
  • 2016年11月01日 15:42
  • 429

用户态程序如何判断一段虚地址空间是否有效

可以让OS内核帮我们检查。 但是似乎没有直接的接口,那就绕个弯子请他帮忙。 将这段虚地址空间的内容,写入一个无关紧要的文件。 这段虚地址空间是否有效,内核自然会帮我们检查的。 要知道,内核对用户空间传...
  • crazycoder8848
  • crazycoder8848
  • 2013年12月18日 11:16
  • 1075

浅析user用户态程序如何访问kernel空间的物理内存DDR和物理寄存器

浅析user用户态程序如何访问kernel空间的物理内存DDR和物理寄存器 fs_initcall(chr_dev_init); 创建如下的char设备在/dev/下: /dev/m...
  • u012183924
  • u012183924
  • 2017年04月06日 12:21
  • 503

物理地址通过什么协议转换为ip地址

arp协议地址解析协议,根据ip地址获取物理地址的一个tcp/ip协议 。 ppp协议点对点协议是为了同等单元之间传输数据包这样的简单链路设计的链路层协议 icmp协议控制报文协议,是tcp/ip子协...
  • u011046042
  • u011046042
  • 2015年10月25日 11:18
  • 3674

I/O空间介绍

I/O空间-----I/O端口和I/O内存   首先上图,如下:外设中的寄存器被称为I/O端口,外设中的内存被称为I/O内存。二者合起来统称为I/O空间。                ...
  • southcamel
  • southcamel
  • 2013年09月25日 23:17
  • 4162

Linux fork简介

linux fork简介
  • zhangxiao93
  • zhangxiao93
  • 2017年05月30日 20:52
  • 843
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:在用户态进行虚拟空间地址向物理空间地址的转换
举报原因:
原因补充:

(最多只允许输入30个字)