I'm installing a kprobe on sys_kill and I want to monitor PIDs and signal numbers.
While I can install the kprobe and get some data in dmesg, the data looks wrong and I cannot make sense of it.
Below is the code that I have:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/sched.h>
static struct kprobe kp;
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
pid_t pid = (pid_t)regs->di;
int sig = (int)regs->si;
printk(KERN_INFO "kprobe pre_handler: kill syscall - PID: %d, Signal: %d\n", pid, sig);
return 0;
}
static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
pid_t pid = (pid_t)regs->di;
int sig = (int)regs->si;
printk(KERN_INFO "kprobe post_handler: kill syscall - PID: %d, Signal: %d\n", pid, sig);
}
static int __init kprobe_init(void)
{
kp.symbol_name = "__x64_sys_kill";
kp.pre_handler = handler_pre;
kp.post_handler = handler_post;
int ret = register_kprobe(&kp);
if (ret < 0) {
printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
return ret;
}
printk(KERN_INFO "kprobe registered for __x64_sys_kill\n");
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
printk(KERN_INFO "kprobe unregistered\n");
}
module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");
It gives me the following output:
kernel: [ 2605.007657] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007658] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007660] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007661] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007662] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007663] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007666] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007667] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007668] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007669] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007671] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007672] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007674] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007675] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007676] kprobe pre_handler: kill syscall - PID: 6029144, Signal: 62
kernel: [ 2605.007677] kprobe post_handler: kill syscall - PID: 6029144, Signal: 62
I tried different approaches:
ax
value to make sure it is the kill system call by checking ax
agains __NR_kill
, but it never got past the if
condition. long syscall_nr = regs->orig_ax;
// Check if it is the kill syscall (syscall number 62 on x86_64)
if (syscall_nr == __NR_kill) {
// NEVER GOT HERE
}
}
So I thought I ask for help here. I do not do this as part of a project, my goal is to learn kernel programming and Linux kernel internals.
Syscall wrappers (__x64_sys_xxx
) created by the standard SYSCALL_DEFINEn(...)
macros only take a single argument: a pointer to a struct pt_regs
holding the user registers. See also this other answer of mine where I explain this.
Your kprobe handlers get the kernel pt_regs
, but you want the user pt_regs
so you need to dereference regs->di
to get ahold of it:
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *user_regs = (struct pt_regs *)regs->di;
pid_t pid = (pid_t)user_regs->di;
int sig = (int)user_regs->si;
printk(KERN_INFO "kprobe pre_handler: kill syscall - PID: %d, Signal: %d\n", pid, sig);
return 0;
}