利用/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)原创文 ...
随机推荐
- RabbitMQ消费端消息的获取方式(.Net Core)
1[短链接]:BasicGet(String queue, Boolean autoAck) 通过request的方式独自去获取消息,断开式,一次次获取,如果返回null,则说明队列中没有消息. 隐患 ...
- HTTP::UserAgent注意问题
例用 HTTP::Request 设置头信息时, 比如 add-content , 第二次再执行 add-content 时, content 内容会追加, 并不会重新添加. 当下次再 add-con ...
- 【Python】Flask系列-模板笔记
Jinja2模板 Jinja2模板传参 如何渲染模板: 模板放在templates文件夹下 从flask中导入render_template函数. 在视图函数中,使用render_template函数 ...
- vim命令学习
文本编辑器vim vim常用操作 vim是一个强大的全屏幕文本编辑器,是Linux上最常用的文本编辑器,它的作用是建立,编辑,显示文本文件. vim没有菜单,只有命令. 输入a或i或o进入编辑命令,下 ...
- 2018 ICPC 沈阳网络赛
2018 ICPC 沈阳网络赛 Call of Accepted 题目描述:求一个算式的最大值与最小值. solution 按普通算式计算方法做,只不过要同时记住最大值和最小值而已. Convex H ...
- 003_cd pushd popd三个命令的区别
一. It depends. In zsh you can configure cd to push the old directory on the directory stack automati ...
- sphinx 同时使用多个索引进行检索探究
2014年2月15日 11:24:34 结论: 1.一次性使用多个索引进行查询的时候,返回的结果集中的fields字段没有什么清楚的意义(也没有找到文档对它的说明) 2.如果程序中一次搜索使用了多个索 ...
- google地图的url参数
Google Maps Intents for Android The Google Maps app for Android exposes several intents that you can ...
- [转]编译hadoop
安装maven hadoop源码是使用maven组织管理的,必须下载maven.从maven官网下载,下载地址是http://maven.apache.org/download.cgi,选择 apac ...
- Hazelcast是什么
Hazelcast是什么 “分布式”.“集群服务”.“网格式内存数据”.“分布式缓存“.“弹性可伸缩服务”——这些牛逼闪闪的名词拿到哪都是ITer装逼的不二之选.在Javaer的世界,有这样一个 ...