0

I understand that this module could not use the system call table address in /boot/System.map-$(uname -r) because of KASLR. By booting with nokaslr, the module worked. With that solved, i moved on to actually hook the kill function, but could not; insmod again returns "Killed" on module load, while dmesg now logs a write access permission error, which occurs on the emphasized line.

I thought that the purpose of unsetting the write protection bit on the control register was to "unset write protection". Is there anything other than that (or my code) preventing the kernel module to overwrite the system call table?

Code

#include <linux/module.h>
#include <linux/kernel.h>

typedef asmlinkage int (*sys_kill_ptr_t)(pid_t, int);

static sys_kill_ptr_t sys_kill_ptr;
static unsigned long *syscall_table;

static inline void set_cr0_wp_bit(void)   { write_cr0(read_cr0() & ~0x1000); }
static inline void unset_cr0_wp_bit(void) { write_cr0(read_cr0() |  0x1000); }

asmlinkage int hooked_kill(pid_t pid, int sig)
{
    printk(KERN_INFO "[+] LKM: hooked_kill called\n");
    return sys_kill_ptr(pid, sig);
}

static int __init lkm_init(void)
{
    printk("[+] LKM: init\n");

    // System call table address in /boot/System.map-$(uname -r)
    syscall_table = (unsigned long *)0xffffffff81c002a0;

    printk(KERN_EMERG "[+] LKM: syscall_table @ 0x%lx\n",
           (unsigned long)syscall_table);

    sys_kill_ptr = (sys_kill_ptr_t)syscall_table[__NR_kill];
    printk(KERN_EMERG "[+] LKM: syscall_table[__NR_kill] @ 0x%lx\n",
           (unsigned long)sys_kill_ptr);

    set_cr0_wp_bit();

    /* Error */
    syscall_table[__NR_kill] = (unsigned long)hooked_kill;
    /* Error */

    unset_cr0_wp_bit();
    printk(KERN_EMERG "[+] LKM: syscall_table[__NR_kill] hooked @ %lx\n",
           (unsigned long)hooked_kill);

    return 0;
}

static void __exit lkm_exit(void)
{
    set_cr0_wp_bit();

    /* Error */
    syscall_table[__NR_kill] = (unsigned long)sys_kill_ptr;
    /* Error */

    unset_cr0_wp_bit();

    printk("[-] LKM: exit\n");
}

module_init(lkm_init);
module_exit(lkm_exit);

dmesg

[ 4218.114119] [+] LKM: init
[ 4218.115931] [+] LKM: syscall_table @ 0xffffffff81c002a0
[ 4218.117025] [+] LKM: syscall_table[__NR_kill] @ 0xffffffff81092fa0
[ 4218.118087] BUG: unable to handle page fault for address: ffffffff81c00490
[ 4218.119159] #PF: supervisor write access in kernel mode
[ 4218.120267] #PF: error_code(0x0003) - permissions violation
Caio Joca
  • 80
  • 6
  • 1
    I see two problems here: (1) you use `0x1000` instead of `0x10000` when changing `cr0`, so you disable bit 12 instead of bit 16, thus, you don't disable write protection; (2) you try to write a function pointer directly to syscall table, which will fail unpredictably (when you fix the first issue), as you use new kernel - see my answer [here](https://stackoverflow.com/a/55174798/7191047). – Danila Kiver Oct 25 '19 at 15:12
  • Yes, thank you; i was simply blind to the missing 0. I also altered the hook to receive `struct pt_regs *` instead of `pid_t` and `int`, which solved the potential second issue on this kernel version. – Caio Joca Nov 26 '19 at 22:26

0 Answers0