There was a need to write a kernel module, with a hook for exec. I found a way with sys_calls_table
and lsm
. As I understand it, sys_calls_table
is more of a hack than a correct solution, and I did not find normal examples for lsm
.
How to correctly intercept a system call in modern kernel versions? I will be very happy with examples.
There is no correct way to do this.
LSM (Linux Security Modules) doesn't support system calls interception, with LSMs you need the implemented some of the functions listed at lsm_hooks_defs.h.
There are two alternatives ways to intercept system calls which I'm aware of:
Hook the sys_call_table
which can be obtained, and overwrite the pointers with your new function:
unsigned long *sys_call_table_ptr = kallsyms_lookup_name("sys_call_table");
unsigned long cr0 = read_cr0();
write_cr0(cr0 & ~x86_CR0_WP);
sys_call_table_ptr[__NR_getpid] = new_getpid;
write_cr0(cr0);
Using kprobe: Syscall functions name expands with the prefix __do_sys_
(see __SYSCALL_DEFINEx).
For example, kprobe on __do_sys_finit_module
(or any other syscall you want) as follow:
static struct kprobe kp = {
.symbol_name = "__do_sys_finit_module",
};
static int handler_pre(struct kprobe *p, struct pt_regs *regs) {
// do your logic
// obtain function arguments using register (calling convetion)
}
static int __init kprobe_init(void)
{
kp.pre_handler = handler_pre;
ret = register_kprobe(&kp);
if (ret < 0) {
printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
return ret;
}
printk(KERN_INFO "Planted kprobe at %p\n", kp.addr);
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr);
}