From e05f447f879a80d9de576c40da7577898e9a6b9e Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Sat, 5 May 2018 22:31:02 +0100 Subject: [PATCH] kprobes --- README.adoc | 22 +++++- kernel_module/README.adoc | 2 + kernel_module/kprobe_example.c | 127 +++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 kernel_module/kprobe_example.c diff --git a/README.adoc b/README.adoc index 908b8ed..7981d0f 100644 --- a/README.adoc +++ b/README.adoc @@ -2682,7 +2682,7 @@ TODO: why does this produce no output? * https://serverfault.com/questions/199654/does-anyone-know-a-simple-way-to-monitor-root-process-spawn * https://unix.stackexchange.com/questions/260162/how-to-track-newly-created-processes -==== CONFIG_PROC_EVENTS aarch64 +===== CONFIG_PROC_EVENTS aarch64 0111ca406bdfa6fd65a2605d353583b4c4051781 was failing with: @@ -2831,6 +2831,26 @@ TODO: what do `+` and `!` mean? Each `enable` under the `events/` tree enables a certain set of functions, the higher the `enable` more functions are enabled. +==== Kprobes + +Inject arbitrary code at a given address in a trap instruction. Oh the good old kernel. :-) + +.... +./build -c 'CONFIG_KPROBES=y' +./run -F 'insmod /kprobe_example.ko && sleep 4 & sleep 4 &' +.... + +Outcome: every fork spits out some extra printks of type: + +.... +<6>[ 2.011117] <_do_fork> pre_handler: p->addr = 0x00000000e1360063, ip = ffffffff810531d1, flags = 0x246 +<6>[ 2.011622] <_do_fork> post_handler: p->addr = 0x00000000e1360063, flags = 0x246 +<6>[ 2.021860] <_do_fork> pre_handler: p->addr = 0x00000000e1360063, ip = ffffffff810531d1, flags = 0x246 +<6>[ 2.022331] <_do_fork> post_handler: p->addr = 0x00000000e1360063, flags = 0x246 +.... + +Docs: https://github.com/torvalds/linux/blob/v4.16/Documentation/kprobes.txt + ==== Count boot instructions * https://www.quora.com/How-many-instructions-does-a-typical-Linux-kernel-boot-take diff --git a/kernel_module/README.adoc b/kernel_module/README.adoc index e901cba..c0239bd 100644 --- a/kernel_module/README.adoc +++ b/kernel_module/README.adoc @@ -47,6 +47,8 @@ .. link:kstrto.c[] . Hardening .. link:strlen_overflow.c[] +. Tracing +.. link:kprobe_example.c[] . Arch .. x86 ... link:ring0.c[] diff --git a/kernel_module/kprobe_example.c b/kernel_module/kprobe_example.c new file mode 100644 index 0000000..ef2405d --- /dev/null +++ b/kernel_module/kprobe_example.c @@ -0,0 +1,127 @@ +/* Upstream: https://github.com/torvalds/linux/blob/v4.16/samples/kprobes/kprobe_example.c */ + +/* + * NOTE: This example is works on x86 and powerpc. + * Here's a sample kernel module showing the use of kprobes to dump a + * stack trace and selected registers when _do_fork() is called. + * + * For more information on theory of operation of kprobes, see + * Documentation/kprobes.txt + * + * You will see the trace data in /var/log/messages and on the console + * whenever _do_fork() is invoked to create a new process. + */ + +#include +#include +#include + +#define MAX_SYMBOL_LEN 64 +static char symbol[MAX_SYMBOL_LEN] = "_do_fork"; +module_param_string(symbol, symbol, sizeof(symbol), 0644); + +/* For each probe you need to allocate a kprobe structure */ +static struct kprobe kp = { + .symbol_name = symbol, +}; + +/* kprobe pre_handler: called just before the probed instruction is executed */ +static int handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#ifdef CONFIG_X86 + pr_info("<%s> pre_handler: p->addr = 0x%p, ip = %lx, flags = 0x%lx\n", + p->symbol_name, p->addr, regs->ip, regs->flags); +#endif +#ifdef CONFIG_PPC + pr_info("<%s> pre_handler: p->addr = 0x%p, nip = 0x%lx, msr = 0x%lx\n", + p->symbol_name, p->addr, regs->nip, regs->msr); +#endif +#ifdef CONFIG_MIPS + pr_info("<%s> pre_handler: p->addr = 0x%p, epc = 0x%lx, status = 0x%lx\n", + p->symbol_name, p->addr, regs->cp0_epc, regs->cp0_status); +#endif +#ifdef CONFIG_TILEGX + pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx, ex1 = 0x%lx\n", + p->symbol_name, p->addr, regs->pc, regs->ex1); +#endif +#ifdef CONFIG_ARM64 + pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx," + " pstate = 0x%lx\n", + p->symbol_name, p->addr, (long)regs->pc, (long)regs->pstate); +#endif +#ifdef CONFIG_S390 + pr_info("<%s> pre_handler: p->addr, 0x%p, ip = 0x%lx, flags = 0x%lx\n", + p->symbol_name, p->addr, regs->psw.addr, regs->flags); +#endif + + /* A dump_stack() here will give a stack backtrace */ + return 0; +} + +/* kprobe post_handler: called after the probed instruction is executed */ +static void handler_post(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ +#ifdef CONFIG_X86 + pr_info("<%s> post_handler: p->addr = 0x%p, flags = 0x%lx\n", + p->symbol_name, p->addr, regs->flags); +#endif +#ifdef CONFIG_PPC + pr_info("<%s> post_handler: p->addr = 0x%p, msr = 0x%lx\n", + p->symbol_name, p->addr, regs->msr); +#endif +#ifdef CONFIG_MIPS + pr_info("<%s> post_handler: p->addr = 0x%p, status = 0x%lx\n", + p->symbol_name, p->addr, regs->cp0_status); +#endif +#ifdef CONFIG_TILEGX + pr_info("<%s> post_handler: p->addr = 0x%p, ex1 = 0x%lx\n", + p->symbol_name, p->addr, regs->ex1); +#endif +#ifdef CONFIG_ARM64 + pr_info("<%s> post_handler: p->addr = 0x%p, pstate = 0x%lx\n", + p->symbol_name, p->addr, (long)regs->pstate); +#endif +#ifdef CONFIG_S390 + pr_info("<%s> pre_handler: p->addr, 0x%p, flags = 0x%lx\n", + p->symbol_name, p->addr, regs->flags); +#endif +} + +/* + * fault_handler: this is called if an exception is generated for any + * instruction within the pre- or post-handler, or when Kprobes + * single-steps the probed instruction. + */ +static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr) +{ + pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr); + /* Return 0 because we don't handle the fault. */ + return 0; +} + +static int __init kprobe_init(void) +{ + int ret; + kp.pre_handler = handler_pre; + kp.post_handler = handler_post; + kp.fault_handler = handler_fault; + + ret = register_kprobe(&kp); + if (ret < 0) { + pr_err("register_kprobe failed, returned %d\n", ret); + return ret; + } + pr_info("Planted kprobe at %p\n", kp.addr); + return 0; +} + +static void __exit kprobe_exit(void) +{ + unregister_kprobe(&kp); + pr_info("kprobe at %p unregistered\n", kp.addr); +} + +module_init(kprobe_init) +module_exit(kprobe_exit) +MODULE_LICENSE("GPL");