利用/proc/pid/pagemap将虚拟地址转换为物理地址
内核文档: Documentation/vm/pagemap.txt
pagemap is a new (as of 2.6.25) set of interfaces in the kernel that allow
userspace programs to examine the page tables and related information by
reading files in /proc.There are four components to pagemap:
* /proc/pid/pagemap. This file lets a userspace process find out which
physical frame each virtual page is mapped to. It contains one 64-bit
value for each virtual page, containing the following data (from
fs/proc/task_mmu.c, above pagemap_read):* Bits 0-54 page frame number (PFN) if present
* Bits 0-4 swap type if swapped
* Bits 5-54 swap offset if swapped
* Bit 55 pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
* Bit 56 page exclusively mapped (since 4.2)
* Bits 57-60 zero
* Bit 61 page is file-page or shared-anon (since 3.5)
* Bit 62 page swapped
* Bit 63 page presentSince Linux 4.0 only users with the CAP_SYS_ADMIN capability can get PFNs.
In 4.0 and 4.1 opens by unprivileged fail with -EPERM. Starting from
4.2 the PFN field is zeroed if the user does not have CAP_SYS_ADMIN.
Reason: information about PFNs helps in exploiting Rowhammer vulnerability.If the page is not present but in swap, then the PFN contains an
encoding of the swap file number and the page's offset into the
swap. Unmapped pages return a null PFN. This allows determining
precisely which pages are mapped (or in swap) and comparing mapped
pages between processes.Efficient users of this interface will use /proc/pid/maps to
determine which areas of memory are actually mapped and llseek to
skip over unmapped regions.
下面是一个工具:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h> #define PAGEMAP_ENTRY 8
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF const int __endian_bit = ;
#define is_bigendian() ( (*(char*)&__endian_bit) == 0 ) int i, c, pid, status;
unsigned long virt_addr;
uint64_t read_val, file_offset, page_size;
char path_buf [0x100] = {};
FILE * f;
char *end; int read_pagemap(char * path_buf, unsigned long virt_addr); int main(int argc, char ** argv){
if(argc!=){
printf("Argument number is not correct!\n pagemap PID VIRTUAL_ADDRESS\n");
return -;
}
if(!memcmp(argv[],"self",sizeof("self"))){
sprintf(path_buf, "/proc/self/pagemap");
pid = -;
}
else{
pid = strtol(argv[],&end, );
if (end == argv[] || *end != '\0' || pid<=){
printf("PID must be a positive number or 'self'\n");
return -;
}
}
virt_addr = strtoll(argv[], NULL, );
if(pid!=-)
sprintf(path_buf, "/proc/%u/pagemap", pid); page_size = getpagesize();
read_pagemap(path_buf, virt_addr);
return ;
} int read_pagemap(char * path_buf, unsigned long virt_addr){
printf("Big endian? %d\n", is_bigendian());
f = fopen(path_buf, "rb");
if(!f){
printf("Error! Cannot open %s\n", path_buf);
return -;
} //Shifting by virt-addr-offset number of bytes
//and multiplying by the size of an address (the size of an entry in pagemap file)
file_offset = virt_addr / page_size * PAGEMAP_ENTRY;
printf("Vaddr: 0x%lx, Page_size: %lld, Entry_size: %d\n", virt_addr, page_size, PAGEMAP_ENTRY);
printf("Reading %s at 0x%llx\n", path_buf, (unsigned long long) file_offset);
status = fseek(f, file_offset, SEEK_SET);
if(status){
perror("Failed to do fseek!");
return -;
}
errno = ;
read_val = ;
unsigned char c_buf[PAGEMAP_ENTRY];
for(i=; i < PAGEMAP_ENTRY; i++){
c = getc(f);
if(c==EOF){
printf("\nReached end of the file\n");
return ;
}
if(is_bigendian())
c_buf[i] = c;
else
c_buf[PAGEMAP_ENTRY - i - ] = c;
printf("[%d]0x%x ", i, c);
}
for(i=; i < PAGEMAP_ENTRY; i++){
//printf("%d ",c_buf[i]);
read_val = (read_val << ) + c_buf[i];
}
printf("\n");
printf("Result: 0x%llx\n", (unsigned long long) read_val);
if(GET_BIT(read_val, )) {
uint64_t pfn = GET_PFN(read_val);
printf("PFN: 0x%llx (0x%llx)\n", pfn, pfn * page_size + virt_addr % page_size);
} else
printf("Page not present\n");
if(GET_BIT(read_val, ))
printf("Page swapped\n");
fclose(f);
return ;
}
测试:
用Qemu+vexpress-ca9:
内存: 1GB, 物理地址范围: 0x60000000->0x9FFFFFFF
通过查看/proc/pid/maps获得进程的地址空间的内存映射情况:
[root@vexpress ~]# cat /proc//maps
-001f3000 r-xp b3: /bin/busybox
001fa000-001fc000 rw-p 001ea000 b3: /bin/busybox
001fc000- rw-p : [heap]
b6c7f000-b6c80000 rw-p :
b6c80000-b6c8d000 r-xp b3: /lib/libnss_files-2.18.so
b6c8d000-b6c94000 ---p 0000d000 b3: /lib/libnss_files-2.18.so
b6c94000-b6c95000 r--p 0000c000 b3: /lib/libnss_files-2.18.so
b6c95000-b6c96000 rw-p 0000d000 b3: /lib/libnss_files-2.18.so
b6c96000-b6ca1000 r-xp b3: /lib/libnss_nis-2.18.so
b6ca1000-b6ca8000 ---p 0000b000 b3: /lib/libnss_nis-2.18.so
b6ca8000-b6ca9000 r--p 0000a000 b3: /lib/libnss_nis-2.18.so
b6ca9000-b6caa000 rw-p 0000b000 b3: /lib/libnss_nis-2.18.so
b6caa000-b6daa000 rw-p :
b6daa000-b6dca000 r-xp b3: /lib/ld-2.18.so
b6dca000-b6dd1000 ---p b3: /lib/ld-2.18.so
b6dd1000-b6dd2000 r--p 0001f000 b3: /lib/ld-2.18.so
b6dd2000-b6dd3000 rw-p b3: /lib/ld-2.18.so
b6dd3000-b6f06000 r-xp b3: /lib/libc-2.18.so
b6f06000-b6f0d000 ---p b3: /lib/libc-2.18.so
b6f0d000-b6f0f000 r--p b3: /lib/libc-2.18.so
b6f0f000-b6f10000 rw-p b3: /lib/libc-2.18.so
b6f10000-b6f13000 rw-p :
b6f13000-b6f26000 r-xp b3: /lib/libnsl-2.18.so
b6f26000-b6f2d000 ---p b3: /lib/libnsl-2.18.so
b6f2d000-b6f2e000 r--p b3: /lib/libnsl-2.18.so
b6f2e000-b6f2f000 rw-p b3: /lib/libnsl-2.18.so
b6f2f000-b6f31000 rw-p :
b6f31000-b6f39000 r-xp b3: /lib/libnss_compat-2.18.so
b6f39000-b6f40000 ---p b3: /lib/libnss_compat-2.18.so
b6f40000-b6f41000 r--p b3: /lib/libnss_compat-2.18.so
b6f41000-b6f42000 rw-p b3: /lib/libnss_compat-2.18.so
be958000-be979000 rw-p : [stack]
bed04000-bed05000 r-xp : [sigpage]
bed05000-bed06000 r--p : [vvar]
bed06000-bed07000 r-xp : [vdso]
ffff0000-ffff1000 r-xp : [vectors]
可以看看0x8000这个虚拟地址对应的物理地址:
[root@vexpress ~]# ./translate 0x8000
Big endian?
Vaddr: 0x8000, Page_size: , Entry_size:
Reading /proc//pagemap at 0x40
[]0x0 []0xf8 []0x9 []0x0 []0x0 []0x0 []0x0 []0xa0
Result: 0xa00000000009f800
PFN: 0x9f800 (0x9f800000)
可以看到, 对应的物理页帧是0x9F800,那么物理地址就是0x9F800000.
下面我们再做一个实验, 进程746的地址空间有一部分用来映射libc:
b6dd3000-b6f06000 r-xp b3: /lib/libc-2.18.so
b6f06000-b6f0d000 ---p b3: /lib/libc-2.18.so
b6f0d000-b6f0f000 r--p b3: /lib/libc-2.18.so
b6f0f000-b6f10000 rw-p b3: /lib/libc-2.18.so
此外, 进程835也会用到libc:
[root@vexpress ~]# cat /proc//maps
... ...
b6e0b000-b6f3e000 r-xp b3: /lib/libc-2.18.so
b6f3e000-b6f45000 ---p b3: /lib/libc-2.18.so
b6f45000-b6f47000 r--p b3: /lib/libc-2.18.so
b6f47000-b6f48000 rw-p b3: /lib/libc-2.18.so
... ...
可以看到, 进程746和835虽然都用了libc,但是对应的虚拟地址却不同,前者是0xb6dd3000, 而后者是0xb6e0b000, 我们知道对于共享库, 在内存只会存在一份代码, 那么物理地址也就是唯一的(代码段是唯一的,所有调用libc的进程共享,而数据段每个进程一个), 那么进程746的虚拟地址空间的0xb6dd3000(代码段)跟进程835的虚拟地址空间的0xb6e0b000(代码段)对应的物理地址应该是同一个, 下面验证一下:
进程746:
[root@vexpress ~]# ./translate 0xb6dd3000
virt_addr: 0xb6dd3000
Big endian?
Vaddr: 0xb6dd3000, Page_size: , Entry_size:
Reading /proc//pagemap at 0x5b6e98
[]0x68 []0xfa []0x9 []0x0 []0x0 []0x0 []0x0 []0xa0
Result: 0xa00000000009fa68
PFN: 0x9fa68 (0x9fa68000)
可以看到,物理地址是0x9FA68000
进程835:
[root@vexpress ~]# ./translate 0xb6e0b000
virt_addr: 0xb6e0b000
Big endian?
Vaddr: 0xb6e0b000, Page_size: , Entry_size:
Reading /proc//pagemap at 0x5b7058
[]0x68 []0xfa []0x9 []0x0 []0x0 []0x0 []0x0 []0xa0
Result: 0xa00000000009fa68
PFN: 0x9fa68 (0x9fa68000)
可以看到, 物理地址也是0x9FA68000, 从而证明了我们的猜想。
完。
利用/proc/pid/pagemap将虚拟地址转换为物理地址的更多相关文章
- How to translate virtual to physical addresses through /proc/pid/pagemap
墙外通道:http://fivelinesofcode.blogspot.com/2014/03/how-to-translate-virtual-to-physical.html I current ...
- 浅析Linux 64位系统虚拟地址和物理地址的映射及验证方法
虚拟内存 先简单介绍一下操作系统中为什么会有虚拟地址和物理地址的区别.因为Linux中有进程的概念,那么每个进程都有自己的独立的地址空间. 现在的操作系统都是64bit的,也就是说如果在用户态的进程中 ...
- cpu为什么使用虚拟地址到物理地址的空间映射,解决了什么样的问题?
当处理器读或写入内存位置时,它会使用虚拟地址.作为读或写操作的一部分,处理器将虚拟地址转换为物理地址.通过虚拟地址访问内存有以下优势: 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓 ...
- stuff in /proc/PID/
Table of Contents 1. /proc/PID/cwd 2. /proc/PID/clear_refs 3. /proc/PID/coredump_filter 4. /proc/PID ...
- Page (computer memory) Memory segmentation Page table 虚拟地址到物理地址的转换
A page, memory page, or virtual page is a fixed-length contiguous block of virtual memory, described ...
- X86在逻辑地址、线性地址、理解虚拟地址和物理地址
参考:http://bbs.chinaunix.net/thread-2083672-1-1.html 本贴涉及的硬件平台是X86.假设是其他平台,不保证能一一对号入座.可是举一反三,我想是全然可行的 ...
- linux /proc/pid进程信息说明
转:http://hi.baidu.com/sei_zhouyu/item/3ab5bc9fb2ea29c3b6253140 /proc/pid/是进程目录,存放的是当前运行进程的信息. 譬如apac ...
- [置顶] Linux 虚拟地址与物理地址的映射关系分析【转】
转自:http://blog.csdn.net/ordeder/article/details/41630945 版权声明:本文为博主(http://blog.csdn.net/ordeder)原创文 ...
- Linux 虚拟地址与物理地址的映射关系分析【转】
转自:http://blog.csdn.net/ordeder/article/details/41630945 版权声明:本文为博主(http://blog.csdn.net/ordeder)原创文 ...
随机推荐
- Submatrix Sum
Given an integer matrix, find a submatrix where the sum of numbers is zero. Your code should return ...
- three.js 初探
2014年3月3日 22:18:40 简单旋转立方体: http://blog.163.com/hailin_xin/blog/static/21816219020136103402812 简单球体入 ...
- java虚拟机规范(se8)——java虚拟机结构(五)
2.10 异常 java虚拟机中的异常用Throwable类或者它的子类的实例来表示.抛出一个异常会导致立即非本地(an inmediate nolocal)的控制转移,从发生异常的地方跳到处理异常的 ...
- android 通用 Intent
通用 Intent 本文内容显示详细信息 闹钟 日历 相机 联系人/人员应用 电子邮件 文件存储 本地操作 地图 音乐或视频 新笔记 电话 搜索 设置 发送短信 网络浏览器 使用 Android 调试 ...
- 前后端分离之mockjs基本介绍
安装与使用 # 安装 npm install mockjs #使用 Mock var Mock = require('mockjs') var data = Mock.mock({ // 属性 lis ...
- java 内部类和向上转型
当将内部类向上转型为其基类时,尤其是转型为一个接口的时候,内部类就有了用武之地,(从实现某个接口的对象,得到对接口的引用,与向上转型为这个对象的基类,实际上是一样的效果,),这是因为此内部类---某个 ...
- android短信验证
短信验证demo http://download.csdn.net/detail/crazy1235/8315279#comment 使用MOB平台开发,用法详见: http://blog.csdn. ...
- 一个浏览器Fuzzing框架的学习
一个浏览器Fuzzing框架的学习 关于框架 之前是LCatro师傅在小密圈分享的他写的这个Fuzzing框架(不过我以前翻github时好像就看到过),但是之前一直没啥时间搞这方面,这两天研究学习了 ...
- ***在PHP语言中使用JSON和将json还原成数组(json_decode()的常见错误)
在之前我写过php返回json数据简单实例,刚刚上网,突然发现一篇文章,也是介绍json的,还挺详细,值得参考.内容如下 从5.2版本开始,PHP原生提供json_encode()和json_deco ...
- (转载)How browsers work--Behind the scenes of modern web browsers (前端必读)
浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工 作原理,我们将看到,从你在地址栏输入google.com到你看到google主页过程中都发生了什么. 将讨论的浏览器 今天,有五种主流浏览器— ...