用户态的程序都是在内存保护模式下使用内存,无法直接访问物理内存。同时用户程序使用的地址,也并不是物理地址,而是逻辑地址。至于这些逻辑地址对应的物理内存在哪里,用户进程本身并不知道。
通过用户程序若想访问物理内存,我们需要通过内核才能实现。本文介绍基于内核模块的方式,实现在Linux中用户态程序访问所有物理内存。
1、内核模块编写
通过文件读写的方式,实现物理地址访问。将物理地址,作为参数pos传递。
ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos )
在内核代码中,是无法直接访问物理地址的,代码能访问的都是逻辑地址。此时我们需要先将物理地址转换成逻辑地址,才能在代码中对地址读写。
物理地址转换成逻辑地址方法:
1)根据物理地址,计算出对应的页面号和页内偏移
page_number = *pos / PAGE_SIZE;
page_indent = *pos % PAGE_SIZE;
2)将页面号找到对应的页面指针
注意在2.6.32及以上内核中,没有导出mem_map符号,只能通过
pfn_to_page()来找到对应的页面指针。
#if 0
pp = pfn_to_page( page_number);
#else
pp = &mem_map[ page_number ];
#endif
3)通过kmap映射成逻辑地址
from = kmap( pp ) + page_indent;
映射成逻辑地址后,我们直接通过from指针来访问物理地址pos了。
2、模块加载和使用方式
1)编译模块
[root@localhost Access_Physical_Memory]# ls
dram.c fileview.cpp Makefile
[root@localhost Access_Physical_Memory]# make
2)加载模块
[root@localhost Access_Physical_Memory]# insmod dram.ko
3)创建字符设备
模块代码中,将字符设备号设为85。这个设备号也可以自己改,与系统不冲突就行。
[root@localhost Access_Physical_Memory]# mknod /dev/dram c 85 0
[root@localhost Access_Physical_Memory]#
3、物理内存数据查看
我们使用简单的程序fileview来看物理内存中的实际数据,物理地址可以手工输入的。
点击下载:内核模块和fileview源码
源码主要参考来源:http://cs.usfca.edu/~cruse/cs635/
我们使用简单的程序fileview来看物理内存中的实际数据,物理地址可以手工输入的。
>>请教一下这个是怎么输入的,试了几个键都不行,一页一页的太难翻了。
看了下代码注释,知道怎么操作了:)
你好,make的时候报错,在dram.c文件里未定义变量“num_physpages”。请问该变量如何定义和获取?
获取系统物理内存页面数量,在不同内核版本,方法可能会有变化。
编译时,未定义,说明函数接口发生变化,需要在对应内核版本查找一下。如3.19.8内核,获取的方法是get_num_physpages()。
Hi Chen,非常感谢。我的Linux内核是3.13.0,添加#include 之后,通过get_num_physpages()函数获取了“num_physpages”变量。成功编译dram和fileview之后,通过fileview读取到了/dev/dram的内容,我的运行环境是分配了3GB物理内存的虚拟机。
我通过pagedown浏览了fileview读取的内存内容,大致计算了一下有1.4GB左右的全0数据。再对比通过“cat /proc/meminfo”获取的空闲内存大小也同样为1.4GB左右。数据基本基本吻合,这说明fileview和dram对物理内存(最起码是大部分物理内存)读取是有效的。
但是,我有2个问题:
1. fileview和dram的组合,能否实现对无特殊设置的普通Linux(例如:我的环境,vmware+Ubuntu)的物理内存的读取?
2. 我准备将fileview和dram的组合移植到arm+Android平台上。该平台可以通过硬件底层手段设置某部分内存不可读,基于这个假设:
fileview和dram的组合,若读取到了不可读的物理内存地址,是显示该物理地址数据全0?还是有其他的错误处理?
(通过阅读代码,如果读取了不可读的物理内存地址,dram内部应该会在pfn_to_page函数出错,并且fileview的获取和显示无特殊处理。)
Thanks in advance
关键要看dram模块代码中如何映射物理内存地址的。通过单一的映射方式,应该访问不了所有物理内存地址空间。若物理地址对应的确实为Physical Memeory,那么Fileview是可以读取的。但有些物理地址对应的是PCI-E配置空间或芯片寄存器,这部分可能读取到值为0(即不能通过这种方式访问)。如e820图中,reserved区域,可能没法通过这种方式访问。你可以试一下,若可以,请回复一下。谢谢。
[ 0.000000] e820: BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x00000000000993ff] usable
[ 0.000000] BIOS-e820: [mem 0x0000000000099400-0x000000000009ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000000e0000-0x00000000000fffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003fffffff] usable
[ 0.000000] BIOS-e820: [mem 0x0000000040000000-0x00000000400fffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000040100000-0x000000007e4fefff] usable
[ 0.000000] BIOS-e820: [mem 0x000000007e4ff000-0x000000007e8fefff] reserved
[ 0.000000] BIOS-e820: [mem 0x000000007e8ff000-0x000000007eefefff] ACPI NVS
[ 0.000000] BIOS-e820: [mem 0x000000007eeff000-0x000000007effefff] ACPI data
[ 0.000000] BIOS-e820: [mem 0x000000007efff000-0x000000007effffff] usable
[ 0.000000] BIOS-e820: [mem 0x000000007f000000-0x000000008fffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000feb00000-0x00000000feb03fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fec00000-0x00000000fec00fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fed18000-0x00000000fed18fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fed1c000-0x00000000fed8ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000ffc00000-0x00000000ffffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000267fffffff] usable
[ 0.000000] NX (Execute Disable) protection: active
hi Chen,
Sorry for the late reply.
你说的是:dram的内存映射方式单一,这种方式可能不能对映射所有的物理内存。那Linux下有没有更加通用并被大家普遍认可的工具,能够读取所有物理内存空间的内容呢?
我测试了下fileview程序读取e820,以下是结果(其中*BIOS-e820表示该区域有内容,+BIOS-820表示该区域全FF,-BIOS-820表示该区域全00)。此外,还有部分地址超过我虚拟机物理地址范围(3G),无法读取。
[ 0.000000] e820: BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009f3ff] usable
[ 0.000000] *BIOS-e820: [mem 0x000000000009f400-0x000000000009ffff] reserved
[ 0.000000] +BIOS-e820: [mem 0x00000000000ca000-0x00000000000cbfff] reserved
[ 0.000000] *BIOS-e820: [mem 0x00000000000dc000-0x00000000000fffff] reserved
[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000bfedffff] usable
[ 0.000000] BIOS-e820: [mem 0x00000000bfee0000-0x00000000bfefefff] ACPI data
[ 0.000000] BIOS-e820: [mem 0x00000000bfeff000-0x00000000bfefffff] ACPI NVS
[ 0.000000] BIOS-e820: [mem 0x00000000bff00000-0x00000000bfffffff] usable
—–虚拟机分配的是物理内存为3G,以下地址超过物理地址上线,无法读取—–
[ 0.000000] BIOS-e820: [mem 0x00000000e0000000-0x00000000efffffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fec00000-0x00000000fec0ffff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
[ 0.000000] BIOS-e820: [mem 0x00000000fffe0000-0x00000000ffffffff] reserved
[ 0.000000] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
[ 0.000000] e820: remove [mem 0x000a0000-0x000fffff] usable
[ 0.000000] e820: last_pfn = 0xc0000 max_arch_pfn = 0x400000000
[ 0.000000] e820: [mem 0xc0000000-0xdfffffff] available for PCI devices
[ 0.802455] e820: reserve RAM buffer [mem 0x0009f400-0x0009ffff]
[ 0.802458] e820: reserve RAM buffer [mem 0xbfee0000-0xbfffffff]
从实验结果看来,e820 reserved区域的读取结果为非00。
您好 在make的时候报错
make[2]: *** [/opt/yo/Access_Physical_Memory/Access_Physical_Memory/dram.o] 错误 1
make[1]: *** [_module_/opt/yo/Access_Physical_Memory/Access_Physical_Memory] 错误 2
make[1]:正在离开目录 `/usr/src/linux-headers-4.4.0-31-generic’
make: *** [default] 错误 2
请问该怎么修改
想请教一下,在kernel如何获取某一块物理内存地址对应的内容呢, 想比较一下这块内存是否被修改过?