Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I override a system call table entry with my own function?

I want to update the system call table to use my custom open function. I wrote the following code:

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

MODULE_LICENSE("GPL");
char *sym_name = "sys_call_table";

typedef asmlinkage long (*sys_call_ptr_t)(const struct pt_regs *);
static sys_call_ptr_t *sys_call_table;
typedef asmlinkage long (*custom_open) (const char __user *filename, int flags, umode_t mode);

custom_open old_open;

static asmlinkage long my_open(const char __user *filename, int flags, umode_t mode)
{
    pr_info("%s\n",__func__);
        return old_open(filename, flags, mode);
}

static int __init hello_init(void)
{
    sys_call_table = (sys_call_ptr_t *)kallsyms_lookup_name(sym_name);
    old_open = (custom_open)sys_call_table[__NR_open];
    sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;

    return 0;
}

static void __exit hello_exit(void)
{
    sys_call_table[__NR_open] = (sys_call_ptr_t)old_open;    
}

module_init(hello_init);
module_exit(hello_exit);

When I load the module, I get the following error:

[69736.192438] BUG: unable to handle page fault for address: ffffffff98e001d0
[69736.192441] #PF: supervisor write access in kernel mode
[69736.192442] #PF: error_code(0x0003) - permissions violation
[69736.192443] PGD 10460e067 P4D 10460e067 PUD 10460f063 PMD 80000001040000e1 
[69736.192461] Oops: 0003 [#1] SMP PTI
[69736.192463] CPU: 0 PID: 45249 Comm: insmod Tainted: G           OE     5.2.8 #6

Can I update the system call table? How can I resolve such an error?

like image 464
md.jamal Avatar asked Jan 26 '23 09:01

md.jamal


1 Answers

You're almost there. In Intel x86 CPUs, the Control Register CR0 has a special bit (called Write Protect bit), to control whether the CPU can write to read-only pages while running in privilege level 0 (kernel code does run at privilege level 0).

Since the syscall table is inside a page that is read-only, and by default the "Write Protect" bit is set, you are prevented from writing to it. So if you attempt to do:

sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;

you'll crash everything.

In order to hijack the syscall correctly you will need to disable write protection by setting the "Write Protect" bit of the CR0 register to 0 before overwriting the table entry, and re-enable it after you're done. The two macros read_cr0() and write_cr0() are there exactly for manipulating said register.

Here's the correct code:

// Temporarily disable write protection
write_cr0(read_cr0() & (~0x10000));

// Overwrite the syscall table entry
sys_call_table[__NR_open] = /* whatever */;

// Re-enable write protection
write_cr0(read_cr0() | 0x10000);

The mask 0x10000 is used above since the "Write Protect" bit is the 17th least significant bit of the register.

Note that the above steps need to be done both in the init and exit functions of your module.

like image 129
Marco Bonelli Avatar answered Jan 27 '23 23:01

Marco Bonelli