有一种方法不需要重新编译内核。由于内核会检测wp位是否被修改write_cr0
,您可以提供自定义函数来绕过它。
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);
}
In your 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);