Linux下访问内存物理地址

Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。

首先我们看下mem这个设备文件,/dev/mem是linux下的一个字符设备,源文件是~/drivers/char/mem.c,这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。

1.设备驱动的方法

下面是mem.c文件里定义的file_operations结构,提供了llseek,read,write,mmap以及open等方法。
static struct file_operations mem_fops =
{
       .llseek   = memory_lseek,
       .read   = read_mem,
       .write   = write_mem,
       .mmap   = mmap_mem,
       .open   = open_mem,
};
因此我们可以通过一般驱动的使用方法,将内存完全当作一个设备来对对待。应用程序如下:
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
       int fd;
       char *rdbuf;
       char *wrbuf = "butterfly";
       int i;
       fd = open("/dev/mem",O_RDWR);
       if(fd < 0)
       {
         printf("open /dev/mem failed.");
       }
       read(fd,rdbuf,10);

       for(i = 0;i < 10;i++)
       {
         printf("old mem[%d]:%c\n",i,*(rdbuf + i));
       }
       lseek(fd,5,0);
       write(fd,wrbuf,10);
       lseek(fd,0,0);//move f_ops to the front
       read(fd,rdbuf,10);
       for(i = 0;i < 10;i++)
       {
         printf("new mem[%d]:%c\n",i,*(rdbuf + i));
       }

       return 0;
}
执行结果如下:将内存最开始10个字节的内容进行替换。
[root@VOIP-IPCAM app]# ./memtest
old mem[0]:b
old mem[1]:u
old mem[2]:t
old mem[3]:t
old mem[4]:e
old mem[5]:r
old mem[6]:f
old mem[7]:l
old mem[8]:y
old mem[9]:!
new mem[0]:b
new mem[1]:u
new mem[2]:t
new mem[3]:t
new mem[4]:e
new mem[5]:b
new mem[6]:u
new mem[7]:t
new mem[8]:t
new mem[9]:e
2.系统调用的方法

细心的你可能会发现,既然你前面说了这个文件里存放的就是内存的地址及内容信息,那我可不可以直接查看到呢,答案是:可以的。linux内核的开发者为我们提供了一个命令hexedit,通过它就可以将/dev/mem的内容显示出来(如果你使用cat /dev/mem将会看到乱码),执行hexedit /dev/mem的结果如下:
00000000 62 75 74 74   65 62 75 74   74 65 72 66   6C 79 21 20   butterfly!
00000010 20 20 20 20   20 20 20 20   20 20 20 20   20 20 20 20
00000020 20 20 20 20   20 20 20 20   20 20 20 20   20 20 20 20
00000030 6F EF 00 F0   6F EF 00 F0   57 EF 00 F0   6F EF 00 F0   o...o...W...o...
00000040 02 11 00 C0   4D F8 00 F0   41 F8 00 F0   34 85 00 F0   ....M...A...4...
00000050 39 E7 00 F0   59 F8 00 F0   2E E8 00 F0   D2 EF 00 F0   9...Y...........
00000060 A4 E7 00 F0   F2 E6 00 F0   6E FE 00 F0   53 FF 00 F0   ........n...S...
00000070 53 FF 00 F0   A4 F0 00 F0   C7 EF 00 F0   1C 42 00 C0   S............B..
从上图可见,最左边显示的是地址,接下来24列显示的是各内存字节单元内容的ASCII码信息,最右边显示的是对应的字符信息。让人欣慰的是,这个文件可以直接修改,按下tab键进入修改模式,修改过程中修改内容会以粗体显示,按下F2保存后粗体消失。上面的butterfly就是通过这种方式修改的。

既然内存的地址以及内容信息全部被保存在mem这个设备文件里,那么我们可以想到通过另外一种方式来实现对物理地址的读写了。那就是将mem设备文件和mmap系统调用结合起来使用,将文件里的物理内存地址映射到进程的地址空间,从而实现对内存物理地址的读写。下面谈一下mmap系统调用。

mmap的函数原型为:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset),该函数定义在/usr/include/sys/mman.h中,使用时要包含:#include<sys/mman.h>,mmap()用来将某个文件中的内容映射到进程的地址空间,对该空间的存取即是对该文件内容的读写。参数说明如下:

start:指向欲映射到的地址空间的起始地址,通常设为null或者0.表示让系统融自动选定地址,映射成功后该地址会返回。

length:表示映射的文件内容的大小,以字节为单位。

prot:表示映射区域的保护方式,有如下四种组合:
--PROT_EXEC 映射区域可执行 ,
--PROT_READ 映射区域可读 ,
--PROT_WRITE 映射区域可写,
--PROT_NONE 映射区域不能被访问

flags:映射区域的一些特性,主要有:
--MAP_FIXED 如果映射不成功则出错返回,
--MAP_SHARED 对映射区域的写入数据会写回到原来的文件
--MAP_PRIVATE 对映射区域的写入数据不会写回原来的文件
--MAP_ANONYMOUS
--MAP_DENYWRITE 只允许对映射区域的写入操作,其他对文件直接写入的操作将被拒绝
--MAP_LOCKED 锁定映射区域

在调用mmap()时,必须要指定MAP_SHARED或MAP_PRIVATE。

fd:open()返回的文件描述符。
offset:为被映射文件的偏移量,表示从文件的哪个地方开始映射,一般设置为0,表示从文件的最开始位置开始映射。offset必须是分页大小(4096字节)的整数倍。

应用程序如下:
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>//mmap head file
int main (void)
{
   int i;
   int fd;
   char *start;
   char *buf = "butterfly!";

   //open /dev/mem with read and write mode
   fd = open ("/dev/mem", O_RDWR);
   if (fd < 0)
   {
          printf("cannot open /dev/mem.");
          return -1;
   }

   //map physical memory 0-10 bytes
   start = (char *)mmap(0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if(start < 0)
   {
      printf("mmap failed.");
      return -1;
   }
   //Read old value
   for (i = 0; i < 10; i++)
   {
          printf("old mem[%d]:%c\n", i, *(start + i));
   }
   //write memory
   memcpy(start, buf, 10);
   //Read new value
   for (i = 0;i < 10;i++)
   {
      printf("new mem[%d]:%c\n", i,*(start + i));
   }
   munmap(start, 10); //destroy map memory
   close(fd);   //close file
   return 0;
}
程序执行结果如下:
[root@VOIP-IPCAM app]# ./rwphy
old mem[0]:b
old mem[1]:u
old mem[2]:t
old mem[3]:t
old mem[4]:e
old mem[5]:b
old mem[6]:u
old mem[7]:t
old mem[8]:t
old mem[9]:e
new mem[0]:b
new mem[1]:u
new mem[2]:t
new mem[3]:t
new mem[4]:e
new mem[5]:r
new mem[6]:f
new mem[7]:l
new mem[8]:y
new mem[9]:!
“/dev/mem是个很好玩的东西,你竟然可以直接访问物理内存。这在LINUX下简直是太神奇了,这种感觉象一个小偷打算偷一个银行,可是这个银行戒备森严,正当这个小偷苦无对策时,突然发现在一个不起眼的地方有个后门,这个后门可以直接到银行的金库。”
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在汇编语言中,直接访问Linux物理内存可以通过"特权级"实现。特权级可以理解为权限级别,越高的特权级别可以访问更多的硬件资源,包括物理内存。 首先,我们需要编写一个内核模块来获取linux物理内存的基址。内核模块是在操作系统内核中加载和运行的代码。内核模块可以使用特权级别访问物理内存。 假设我们定义了一个内核模块,并命名为"phys_mem_mod"。我们可以在模块中使用以下代码来获取物理内存的基址: ``` #include <linux/module.h> #include <linux/kernel.h> void* base_addr; int init_module(void) { base_addr = phys_to_virt(0); //通过物理地址0获取基址 printk(KERN_INFO "Physical memory base address: %p\n", base_addr); return 0; } void cleanup_module(void) { printk("Unloading phys_mem_mod module.\n"); } MODULE_LICENSE("GPL"); ``` 在代码中,我们使用了phys_to_virt()函数将物理内存地址0转换为虚拟地址,并将其存储在base_addr变量中。通过打印base_addr变量,我们可以得到物理内存的基址。 在编写完内核模块代码后,将其编译为ko文件并加载到Linux内核中。加载后,内核模块将输出物理内存的基址。 此外,为了直接访问物理内存,我们还需要在内核模块中使用特殊的指令来读取或写入特定的物理内存地址。具体的操作将根据实际需求而定,并需要注意特权级别的限制。 总之,通过编写内核模块并使用特权级别,我们可以实现在汇编语言中直接访问Linux物理内存。请记住,在进行这样的操作时,需要仔细考虑安全性和操作系统的稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值