墙外通道:http://fivelinesofcode.blogspot.com/2014/03/how-to-translate-virtual-to-physical.html

I currently work on a project where I need to make translations for virtual addresses of user-level application to physical addresses in Linux. I implemented my own system call to do that, but had hard times with verifying the results I'm getting.

Later I found out that in newer kernels there is a really nice virtual file in the /proc file system to get this information. I tried to cat it, doing cat /proc/self/pagemap, and got terrible binary output in my console.

So, it looks like working with this file is not such a pleasant experience. It's a binary file with all that it implies. I found couple of scripts that access this file and provide you with a nice text result, but unfortunately those were written in perl and ruby, and I needed to run it on very minimalistic embedded system. I needed something that fits into a single binary.

Long story short, I decided to bite the bullet and write a tool in C. My contribution might be helpful for someone, that's why I'm sharing this code.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <stdint.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;
char path_buf [0x100] = {};
FILE * f;
char *end; int read_pagemap(char * path_buf, unsigned long virt_addr); int main(int argc, char ** argv){
//printf("%lu\n", GET_BIT(0xA680000000000000, 63));
//return 0;
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 = strtol(argv[], NULL, );
if(pid!=-)
sprintf(path_buf, "/proc/%u/pagemap", pid); 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 / getpagesize() * PAGEMAP_ENTRY;
printf("Vaddr: 0x%lx, Page_size: %d, Entry_size: %d\n", virt_addr, getpagesize(), 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, 63))
if(GET_BIT(read_val, ))
printf("PFN: 0x%llx\n",(unsigned long long) GET_PFN(read_val));
else
printf("Page not present\n");
if(GET_BIT(read_val, ))
printf("Page swapped\n");
fclose(f);
return ;
}

And now how you use it. It's very simple. Of course you need to compile it. Then you need to find out what mapping your target process does have. You can do that by reading /proc/pid/maps file. Fortunately that file is human readable.
When you know a valid virtual address, you can pass it to our tool to get actual value from pagemap, including physical frame number. Here is an example:

$ #let's find get virtual address of a page
$ cat /proc/self/maps
-0040b000 r-xp : /bin/cat
0060a000-0060b000 r--p 0000a000 : /bin/cat
0060b000-0060c000 rw-p 0000b000 : /bin/cat
0223a000-0225b000 rw-p : [heap]
7fe7e15e1000-7fe7e1cc3000 r--p : /usr/lib/locale/locale-archive
7fe7e1cc3000-7fe7e1e80000 r-xp : /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e1e80000-7fe7e2080000 ---p 001bd000 : /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e2080000-7fe7e2084000 r--p 001bd000 : /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e2084000-7fe7e2086000 rw-p 001c1000 : /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e2086000-7fe7e208b000 rw-p :
7fe7e208b000-7fe7e20ae000 r-xp : /lib/x86_64-linux-gnu/ld-2.17.so
7fe7e228d000-7fe7e2290000 rw-p :
7fe7e22ab000-7fe7e22ad000 rw-p :
7fe7e22ad000-7fe7e22ae000 r--p : /lib/x86_64-linux-gnu/ld-2.17.so
7fe7e22ae000-7fe7e22b0000 rw-p : /lib/x86_64-linux-gnu/ld-2.17.so
7fffce6b6000-7fffce6d7000 rw-p : [stack]
7fffce722000-7fffce724000 r-xp : [vdso]
ffffffffff600000-ffffffffff601000 r-xp : [vsyscall]
$ #don't forget alsr, normally only /bin/cat will remain same
$ #so let's pick 0x00400000. Now we run our program.
$ #First argument is pid, "self" is a legal option too, the second is virtual address
$ ./pagemap self 0x00400000
Reading /proc/self/pagemap at 0x2000
Result: 0a60000000008c445

We got  0x0a60000000008c445 as a result. There are some bits showing that the page is valid, along with the size of the page. You can reed more in Linux documentation: https://www.kernel.org/doc/Documentation/vm/pagemap.txt. Basically, the physical page number is 0x8c445.

Note, that in different kernel versions bits 56-60 have different meaning. In most current versions, they are forced to zero, however in kernel version 3.11.0 they represent page size.

How to translate virtual to physical addresses through /proc/pid/pagemap的更多相关文章

  1. PatentTips - Maintaining shadow page tables in a sequestered memory region

    BACKGROUND Malicious code, known as malware, which includes viruses, worms, adware, etc., may attack ...

  2. ARM linux内核启动时几个关键地址【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/06/2396787.html 1.       内核启动地址1.1.   名词解释ZTEXTAD ...

  3. 利用/proc/pid/pagemap将虚拟地址转换为物理地址

    内核文档: Documentation/vm/pagemap.txt pagemap is a new (as of 2.6.25) set of interfaces in the kernel t ...

  4. linux kernel内存映射实例分析

    作者:JHJ(jianghuijun211@gmail.com)日期:2012/08/24 欢迎转载,请注明出处 引子 现在android智能手机市场异常火热,硬件升级非常迅猛,arm cortex ...

  5. EGLImage与纹理

    http://blog.csdn.net/sunnytina/article/details/51895406 Android使用Direct Textures提高glReadPixels.glTex ...

  6. pagemap, from the userspace perspective

    pagemap, from the userspace perspective --------------------------------------- pagemap is a new (as ...

  7. stuff in /proc/PID/

    Table of Contents 1. /proc/PID/cwd 2. /proc/PID/clear_refs 3. /proc/PID/coredump_filter 4. /proc/PID ...

  8. Linux下如何在进程中获取虚拟地址对应的物理地址【转】

    转自:http://blog.csdn.net/kongkongkkk/article/details/74366200 如果让你编写一个程序,来获取虚拟地址对应的物理地址..你会试着操作MMU吗.. ...

  9. intel:spectre&Meltdown侧信道攻击(四)—— cache mapping

    前面简单介绍了row hammer攻击的原理和方法,为了更好理解这种底层硬件类攻击,今天介绍一下cpu的cache mapping: 众所周知,cpu从内存读数据,最开始用的是虚拟地址,需要通过分页机 ...

随机推荐

  1. (转载)new Thread的弊端及Java四种线程池的使用

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new ...

  2. C++中重载操作符[ ]

    1.首先说说为什么要重载操作符[ ] 主要是因为系统只给了整数类型(int)的重载函数,即只能在方括号中输入整数进行查找,但有时候我们可能存放数据时,下标的类型是自定义的,希望也能像数组直接通过下标访 ...

  3. 设置DataGridView中表头颜色

    默认的DataGridView表头颜色实在不是太好看,想设置下,上google搜了一通, 都说这样设置 this.dataGridView1.ColumnHeadersDefaultCellStyle ...

  4. caoni大业 spring boot 跳坑记

    IDEA环境 win10 跑得刚刚,到xp系统就戈壁 报错 Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.g ...

  5. HTTP1.1协议-RFC2616-中文版

    转自:http://www.cnblogs.com/k1988/archive/2010/01/12/2165683.html 说明 本文档规定了互联网社区的标准组协议,并需要讨论和建议以便更加完善. ...

  6. ABP框架系列之一:(Entity-实体)

    Entities are one of the core concepts of DDD (Domain Driven Design). Eric Evans describe it as " ...

  7. ZOJ 3229 Shoot the Bullet (有源有汇有上下界最大流)

    题意:一个人要给女孩子们拍照,一共 n 天,m 个女孩子,每天他至多拍 d[i] 张照片,每个女孩子总共要被至少拍 g[i] 次.在第 i 天,可以拍 c[i] 个女孩子,c[i] 个女孩子中每个女孩 ...

  8. input标签之外是否一定添加form标签

    原文转载自:https://blog.csdn.net/lamanchas/article/details/78753031 input标签外是否添加form标签需要按情形区分:应用场景的区别:1.所 ...

  9. Python开发——3.基本数据类型之列表、元组和字典

    一.列表(list) 1.列表的格式 li = [11,22,"kobe",["lakers","ball",11],(11,22,),{& ...

  10. Nginx unit 源码安装初体验

    Nginx unit 源码安装初体验 上次介绍了从yum的安装方法(https://www.cnblogs.com/wang-li/p/9684040.html),这次将介绍源码安装,目前最新版为1. ...