【发布时间】:2016-09-01 08:17:41
【问题描述】:
我正在尝试使用模块读取/写入内核内存,到目前为止,读取部分正在工作。 我有一个来自用户空间的程序,它打开我的模块创建的设备,并且可以从 x addr(内核内存地址)读取 n 个字节,这可以工作(使用 copy_to_user)。 我还通过使用 gdb 读取相同的地址来确认这一点:
gdb /bin/ls /proc/kcore
x/2b [addr]
当我尝试写入相同的内存地址时,问题就出现了。 一旦我尝试这个,我得到:
BUG: unable to handle kernel paging request at [addr]
(我正在阅读相同的地址)
我必须说我从 /proc/kallsyms 中获取了这个地址。
令我不安的是,虽然我们将物理内存映射到内核内存,这意味着即使我们没有足够的内存来映射我们所有的物理内存,至少映射到内核的范围内存应该存在,并且如果我能够从该地址读取,这意味着实际上该地址现在就存在。
代码如下:
case IOCTL_WRITE_KERNEL_MEMORY:
pr_info ("%s: IOCTL_WRITE_KERNEL_MEMORY\n", r2_devname);
if (data->addr < PAGE_OFFSET) {
pr_info ("%s: error - 0x%lx belongs to USERSPACE\n", r2_devname, (unsigned long)data->addr);
ret = -EINVAL;
return ret;
}
pr_info ("%s: addr: 0x%lx\n", r2_devname, (unsigned long)data->addr);
ret = copy_from_user ((void *)data->addr, data->buff, len);
if (ret) {
pr_info ("error: copy_from_user failed\n");
ret = -EINVAL;
return ret;
}
break;
data->addr 包含地址,data->buff 包含要读取的内容。这是通过来自用户空间的指针传递的:
case WRITE_KERNEL_MEMORY:
if (argc < 4) {
printf ("specify bytes to write\n");
break;
}
unsigned char c = 0xd;
data.buff = &c;
data.addr = 0xf8350000;
data.len = n_bytes;
printf ("ioctl: going to write: 0x%x\n", c);
printf ("ioctl: going to write: 0x%x\n", *data.buff);
ioctl_n = _IOR (R2_TYPE, 0x2, sizeof (struct r2k_data));
ret = ioctl (fd, ioctl_n, &data);
break;
和结构:
struct r2k_data {
unsigned long *addr;
unsigned long len;
unsigned char *buff;
};
我认为如果我可以从那个地址读取,我应该也可以写,即使我因为写保护而不能写,我想我应该得到另一个错误消息。
有人有想法吗?
非常感谢
【问题讨论】:
-
I think that if I could read from that addr, I should be able to write too, and even if i'm not able because of write protection, I think I should get another error message.- 不,可读取的内存不需要可用于写入。写入 readonly 内存区域 within kernel 会导致 common 错误消息unable to handle kernel paging request at;没有针对这种情况的具体消息。 -
嗨@Tsyvarev,你知道如何检查地址是否被写保护吗?也许从 include/linux/page-flags.h 检查一些 PG_flags ?谢谢
-
在检查标志之前,您应该找到所需地址的页面描述符。您希望在给定地址找到什么类型的信息?例如,内核代码通常是写保护的,代码中标记为
const的全局数据也是如此。 -
我找到了带有 virt_to_page 的页面描述符。其实这只是一个测试。在这个地址(我正在阅读的地址)处放置了函数 native_read_cr4。我不希望找到任何特别的东西,只是我可以从那个地址中读取,我很好奇我是否也可以写到那个地址。无论如何,我想在尝试之前测试地址是否可写。我正在查看 PG_* 标志,但从名称中我找不到正确的标志。谢谢。
-
我发现“native_read_cr4”函数的 addr 和模块内部创建的变量的 addre 之间的唯一区别是 native_read_cr4 的 addr 持有 PG_reserved,而另一个没有。所以我想我可以通过检查这个位来检查一个 addr 是否可写。
标签: c linux memory linux-kernel