gpt4 book ai didi

linux-kernel - 如何写入 Linux 内核中的 protected 页面?

转载 作者:行者123 更新时间:2023-12-04 04:50:06 30 4
gpt4 key购买 nike

我正在尝试在模块中添加一个系统调用。我的理由是:

  1. 这是一个研究项目,因此具体实现并不重要。
  2. 在内核核心中添加系统调用需要非常长的时间来重新编译。我可以用扩展的系统调用表编译一次,但不是每次都这样。即使使用增量编译、链接和归档最终二进制文件也需要很长时间。
  3. 由于该项目对时间敏感,使用 kprobes 拦截系统调用处理程序会减慢系统调用处理程序的速度。

我仍然对添加系统调用的其他方法持开放态度,但出于上述原因,我认为写入模块中的 sys_call_table 是完成我想做的事情的最干净的方法.

我已经从 System.map 中获取了系统调用表的地址,禁用了 kaslr,并且我正在尝试清除页面保护,但一些写保护仍然阻碍了我。

// following https://web.iiit.ac.in/~arjun.nath/random_notes/modifying_sys_call.html

// clear cr0 write protection
write_cr0 (read_cr0 () & (~ 0x10000));

// clear page write protection
sys_call_table_page = virt_to_page(&sys_call_table[__NR_execves]);
set_pages_rw(sys_call_table_page, 1);

// do write
sys_call_table[__NR_execves] = sys_execves;

但是,我仍然遇到权限错误,但我不知道它的执行机制:

[   11.145647] ------------[ cut here ]------------
[ 11.148893] CR0 WP bit went missing!?
[ 11.151539] WARNING: CPU: 0 PID: 749 at arch/x86/kernel/cpu/common.c:386 native_write_cr0+0x3e/0x70
...
Here was a call trace pointing to the write of sys_call_table
...
[ 11.332825] ---[ end trace c20c95651874c08b ]---
[ 11.336056] CPA protect Rodata RO: 0xffff888002804000 - 0xffff888002804fff PFN 2804 req 8000000000000063 prevent 0000000000000002
[ 11.343934] CPA protect Rodata RO: 0xffffffff82804000 - 0xffffffff82804fff PFN 2804 req 8000000000000163 prevent 0000000000000002
[ 11.351720] BUG: unable to handle page fault for address: ffffffff828040e0
[ 11.356418] #PF: supervisor write access in kernel mode
[ 11.359908] #PF: error_code(0x0003) - permissions violation
[ 11.363665] PGD 3010067 P4D 3010067 PUD 3011063 PMD 31e29063 PTE 8000000002804161
[ 11.368701] Oops: 0003 [#1] SMP KASAN PTI

full dmesg

关于如何禁用它的任何猜测?

最佳答案

有一种方法不需要重新编译内核。由于内核会在write_cr0中检测wp位是否被修改,您可以提供一个自定义函数来绕过它。

inline void mywrite_cr0(unsigned long cr0) {
asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order));
}

这里是启用/禁用写保护的函数。我们用mywrite_cr0 而不是 write_cr0

void enable_write_protection(void) {
unsigned long cr0 = read_cr0();
set_bit(16, &cr0);
mywrite_cr0(cr0);
}

void disable_write_protection(void) {
unsigned long cr0 = read_cr0();
clear_bit(16, &cr0);
mywrite_cr0(cr0);
}

在您的 mod_init 函数中,您可以使用 kallsyms_lookup_name("sys_call_table") 在运行时找出 sys_call_table 的地址,而不是编译时间。幸运的是,我们现在可以直接写入 sys_call_table 而无需处理 pageattr。

以下代码在 Linux Kernel 5.1.4 上测试

inline void mywrite_cr0(unsigned long cr0) {
asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order));
}

void enable_write_protection(void) {
unsigned long cr0 = read_cr0();
set_bit(16, &cr0);
mywrite_cr0(cr0);
}

void disable_write_protection(void) {
unsigned long cr0 = read_cr0();
clear_bit(16, &cr0);
mywrite_cr0(cr0);
}

static struct {
void **sys_call_table;
void *orig_fn;
} tinfo;

static int __init mod_init(void) {
printk(KERN_INFO "Init syscall hook\n");
tinfo.sys_call_table = (void **)kallsyms_lookup_name("sys_call_table");
tinfo.orig_fn = tinfo.sys_call_table[your_syscall_num];
disable_write_protection();
// modify sys_call_table directly
tinfo.sys_call_table[your_syscall_num] = sys_yourcall;
enable_write_protection();
return 0;
}

static void __exit mod_cleanup(void) {
printk(KERN_INFO "Cleaning up syscall hook.\n");
// backup syscall
disable_write_protection();
tinfo.sys_call_table[your_syscall_num] = tinfo.orig_fn;
enable_write_protection();
printk(KERN_INFO "Cleaned up syscall hook.\n");
}

module_init(mod_init);
module_exit(mod_cleanup);

关于linux-kernel - 如何写入 Linux 内核中的 protected 页面?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58512430/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com