我出于学习目的编写了以下 LKM,其目标是挂钩系统调用(例如openat(2)
)。
问题是,成功禁用CR0.WP
并设置sys_call_table[__NR_openat]
挂钩后,系统冻结。
任何对 Linux 内核有深入了解的人都可以评论为什么会发生这种情况吗?
笔记:
为了运行 LKM,您必须将CAP_SET_EMPTY_OFFSET
和替换为内核的相应偏移量,或者只是硬编码获得SYS_CALL_TABLE_OFFSET
的地址。sys_call_table
sudo cat /proc/kallsyms | grep sys_call_table
代码:
#include <linux/module.h> /* Needed by all modules */
#include <linux/syscalls.h>
#include <linux/capability.h>
#include <linux/set_memory.h> /* Needed by set_memory_rw */
/*
* Avoid tainting the kernel, however the kernel will still be tainted if the
* module is not signed
*/
MODULE_LICENSE("GPL");
#define CAP_SET_EMPTY_OFFSET 0x45fe0
#define SYS_CALL_TABLE_OFFSET 0x2e0
static unsigned long* __sys_call_table;
unsigned long *find_sys_call_table(void)
{
unsigned long rodata_start;
rodata_start = (unsigned long)&__cap_empty_set - CAP_SET_EMPTY_OFFSET;
return (unsigned long*)(rodata_start + SYS_CALL_TABLE_OFFSET);
}
static inline void __write_cr0(unsigned long val)
{
asm volatile ("mov %0, %%cr0": : "r" (val));
}
static inline void disable_write_protection(void)
{
preempt_disable();
barrier();
__write_cr0(read_cr0() & (~ 0x00010000));
}
static inline void enable_write_protection(void)
{
__write_cr0(read_cr0() | 0x00010000);
barrier();
preempt_enable();
}
typedef asmlinkage long (*orig_openat_t)(int, const char*, int, umode_t);
orig_openat_t orig_openat;
asmlinkage long
hooked_openat(int dirfd, const char __user* pathname, int flags, umode_t mode)
{
//printk(KERN_INFO "%s: hooked openat\n", __func__);
return orig_openat(dirfd, pathname, flags, mode);
}
static int __init syscall_init(void)
{
printk(KERN_DEBUG "%s: started\n", __func__);
printk(KERN_INFO "%s: cr0: %lx\n", __func__, read_cr0());
__sys_call_table = find_sys_call_table();
printk(KERN_INFO "%s: __sys_call_table: %px\n", __func__, __sys_call_table);
orig_openat = (orig_openat_t)__sys_call_table[__NR_openat];
printk(KERN_INFO "%s: orig_openat: %px\n", __func__, (void*)orig_openat);
disable_write_protection();
printk(KERN_INFO "%s: cr0: %lx\n", __func__, read_cr0());
__sys_call_table[__NR_openat] = (unsigned long)hooked_openat;
printk(KERN_INFO "%s: hooked_openat: %px\n", __func__, (void*)hooked_openat);
enable_write_protection();
printk(KERN_INFO "%s: cr0: %lx\n", __func__, read_cr0());
return 0;
}
static void __exit syscall_release(void)
{
disable_write_protection();
__sys_call_table[__NR_openat] = (unsigned long)orig_openat;
enable_write_protection();
printk(KERN_DEBUG "%s: exited\n", __func__);
}
/* set the entry point of the module */
module_init(syscall_init);
/* set the exit point of the module */
module_exit(syscall_release);
答案1
我怀疑问题是你没有设置orig_openat
在任何地方;你需要
orig_openat = (orig_openat_t) _sys_call_table[__NR_openat];
_sys_call_table[__NR_openat] = (unsigned long)hooked_openat;
挂断电话时。