概述
kprobe是linux内核提供了一个钩子回调机制,能够让我们轻松的加入回调函数在指定的函数之前调用。方便我们在不更改调试模块代码的情况下,加入回调函数以供调试使用。
实验环境
Ubuntu16.04
准备工作
查找想要hook的kernel内核函数。
sudo vim /boot/System.map-$(uname -r)
代码实例
这里我们决定对内核函数_do_fork添加回调钩子,也就是说每当_do_fork被调用时,我们注册的回调接口,也会被调用。
kprobe.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/thread_info.h>
//定义要Hook的函数,本例中_do_fork
static struct kprobe kp =
{
.symbol_name = "_do_fork",
};
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
printk(KERN_ERR "hook in handler_pre");
return 0;
}
static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
printk(KERN_ERR "hook in handler_post");
}
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
printk(KERN_ERR "hook in handler_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)
{
printk(KERN_INFO "register_kprobe failed, returned %dn", ret);
return ret;
}
printk(KERN_INFO "Planted kprobe at %pn", kp.addr);
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
printk(KERN_INFO "kprobe at %p unregisteredn", kp.addr);
}
module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");
编写Makefile
ifneq ($(KERNELRELEASE),)
obj-m := kprobe.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
~
~
编译安装模块
make
sudo ./kprobe.ko
执行
dmesg | tail
可以看到我们的钩子函数被调用了。
[18186.961947] kprobe: loading out-of-tree module taints kernel.
[18186.961975] kprobe: module verification failed: signature and/or required key missing - tainting kernel
[18186.965653] Planted kprobe at ffffffff8fe86080
[18188.794619] hook in handler_pre
[18188.794622] hook in handler_post
[18188.808006] hook in handler_pre
[18188.808010] hook in handler_post
[18189.567033] hook in handler_pre
[18189.567042] hook in handler_post
[18194.470944] hook in handler_pre
[18194.470953] hook in handler_post
[18195.214975] hook in handler_pre
[18195.214984] hook in handler_post
[18195.790376] hook in handler_pre
[18195.790384] hook in handler_post
[18196.270902] hook in handler_pre
配合dump_stack() 使用
配合dump_stack(),我们可以轻松获得_do_fork调用时的堆栈情况。
修改handler_post函数,添加dump_stack();
static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
printk(KERN_ERR "hook in handler_post");
dump_stack();
}
执行
dmesg | tail
可以看到堆栈里函数的调用次序,了解是谁调用了_do_fork。
[19565.981675] hook in handler_post
[19574.126920] hook in handler_pre
[19574.126934] CPU: 2 PID: 15488 Comm: bash Tainted: G OE 4.13.0-36-generic #40~16.04.1-Ubuntu
[19574.126939] Hardware name: SHINELON Computer P65xRP/P65xRP, BIOS 1.05.03 08/25/2016
[19574.126940] Call Trace:
[19574.126953] dump_stack+0x63/0x8b
[19574.126962] handler_post+0xe/0x1c [kprobe]
[19574.126970] kprobe_ftrace_handler+0x111/0x120
[19574.126976] ? _do_fork+0x5/0x3f0
[19574.126983] ftrace_ops_assist_func+0x74/0x110
[19574.126989] ? security_file_alloc+0x44/0x80
[19574.126995] 0xffffffffc023e0d5
[19574.127000] ? sys_vfork+0x30/0x30
[19574.127005] ? _do_fork+0x5/0x3f0
[19574.127009] _do_fork+0x5/0x3f0
[19574.127014] SyS_clone+0x19/0x20
[19574.127018] ? _do_fork+0x5/0x3f0
[19574.127022] ? SyS_clone+0x19/0x20
[19574.127028] do_syscall_64+0x61/0xd0
[19574.127035] entry_SYSCALL64_slow_path+0x25/0x25
[19574.127039] RIP: 0033:0x7eff7b8de41a
[19574.127042] RSP: 002b:00007fff516e25d0 EFLAGS: 00000246 ORIG_RAX: 0000000000000038
[19574.127047] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007eff7b8de41a
[19574.127050] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000001200011
[19574.127053] RBP: 00007fff516e2600 R08: 0000000000000000 R09: 00007eff7c214700
[19574.127055] R10: 00007eff7c2149d0 R11: 0000000000000246 R12: 0000000000003c80
[19574.127058] R13: 00000000025f9b68 R14: 0000000000000000 R15: 00000000025f8628
另外还有更多灵活的用法等待发掘。
最后
以上就是大方烤鸡为你收集整理的kernel 调试技术之kprobe的全部内容,希望文章能够帮你解决kernel 调试技术之kprobe所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复