Linux 在使用 LKM 挂钩后冻结,例如 openat(2) 系统调用

Linux 在使用 LKM 挂钩后冻结,例如 openat(2) 系统调用

我出于学习目的编写了以下 LKM,其目标是挂钩系统调用(例如openat(2))。

问题是,成功禁用CR0.WP并设置sys_call_table[__NR_openat]挂钩后,系统冻结。

任何对 Linux 内核有深入了解的人都可以评论为什么会发生这种情况吗?

笔记:

为了运行 LKM,您必须将CAP_SET_EMPTY_OFFSET和替换为内核的相应偏移量,或者只是硬编码获得SYS_CALL_TABLE_OFFSET的地址。sys_call_tablesudo 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;

挂断电话时。

相关内容