cha*_*umQ 3 paging system-calls linux-kernel
我正在尝试在模块中添加系统调用。我的理由是:
kprobes拦截系统调用处理程序会减慢系统调用处理程序的速度。我仍然对添加系统调用的其他方法持开放态度,但由于上述原因,我认为在模块中写入sys_call_table是完成我想做的事情的最干净的方法。
我已经从禁用的 kaslr 获取了系统调用表的地址System.map,并且我正在尝试清除页面保护,但某些写保护仍然阻碍我。
// 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;
Run Code Online (Sandbox Code Playgroud)
但是,我仍然收到权限错误,但我不知道它的执行机制:
[ 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
Run Code Online (Sandbox Code Playgroud)
关于如何禁用它有什么猜测吗?
小智 5
有一种方法不需要重新编译内核。由于内核会检测wp位是否被修改write_cr0,因此您可以提供自定义函数来绕过它。
inline void mywrite_cr0(unsigned long cr0) {
asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order));
}
Run Code Online (Sandbox Code Playgroud)
这是启用/禁用写保护的功能。我们使用
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);
}
Run Code Online (Sandbox Code Playgroud)
在您的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);
Run Code Online (Sandbox Code Playgroud)