openat(2) 시스템 호출과 같은 LKM 후크를 사용한 후 Linux가 정지됩니다.

openat(2) 시스템 호출과 같은 LKM 후크를 사용한 후 Linux가 정지됩니다.

나는 시스템 호출(예: )을 연결하는 것을 목표로 학습 목적으로 다음 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;

전화를 끊을 때.

관련 정보