Kprobe介绍
Kprobe是一种内核调测手段,它可以动态地跟踪内核的行为、收集debug信息和性能信息。可以跟踪内核几乎所有的代码地址(almost:不允许跟踪的名单blacklist在/sys/kernel/debug/kprobes/blacklist),并且当断点被击中后会响应处理函数。
Kprobe有三个子功能:
Kprobes:几乎可以插入内核的任何指令;
Jprobes:可以插入内核的某个函数;
Kretprobes(return probes):可以在函数返回时触发;
这三个子功能,可以让用户在内核指令、内核进入函数和从函数退出时进行断点处理。
框架支持
Kprobes and return probes are implemented on the following
architectures:
- i386 (Supports jump optimization)
- x86_64 (AMD-64, EM64T) (Supports jump optimization)
- ppc64
- ia64 (Does not support probes on instruction slot1.)
- sparc64 (Return probes not yet implemented.)
- arm
- ppc
- mips
- s390
控制接口说明
/sys/kernel/debug/kprobes/list: 列出内核中已经设置kprobe断点的函数
/sys/kernel/debug/kprobes/enabled: kprobe开启/关闭开关
/sys/kernel/debug/kprobes/blacklist: kprobe黑名单(无法设置断点函数)
/proc/sys/debug/kprobes-optimization: Turn kprobes optimization ON/OFF.
Kprobe函数接口及使用
1.Jprobe使用
#include <linux/kprobes.h>
int register_jprobe(struct jprobe *jp)
void unregister_jprobe(struct jprobe *jp);
1) register_jprobe()是jprobe的注册函数,参数jp->kp.addr指向需要调测的函数。
2) 当内核调用函数时断点被触发,执行jp->entry指向的处理函数。
3) 其中jp->kp.addr指向的函数名(以内核do_fork()函数为例),可以按以下方式确认内核是否存在:
grep do_fork /usr/src/linux/System.map
nm vmlinuz |grep do_fork
cat /proc/kallsyms |grep do_fork
kallsyms_lookup_name(“do_fork”);
4) jprobe断点的取消注册,调用unregister_jprobe()。
在linux内核源码中提供了jprobe的用例
samples/kprobes/jprobe_example.c:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
// 断点触发后的处理函数,这里打印stack信息,然后调用jprobe_return()返回断点触发位置
static long jdo_fork(unsigned long clone_flags, unsigned long stack_start,
unsigned long stack_size, int __user *parent_tidptr,
int __user *child_tidptr)
{
pr_info("jprobe: clone_flags = 0x%lx, stack_start = 0x%lx "
"stack_size = 0x%lx\n", clone_flags, stack_start, stack_size);
/* Always end with a call to jprobe_return(). */
jprobe_return();
return 0;
}
// jprobe结构体初始化,其中.entry指定处理函数,.kp指定打断点的函数名称
static struct jprobe my_jprobe = {
.entry = jdo_fork,
.kp = {
.symbol_name = "do_fork",
},
};
// 模块init函数,inmode插入模块从这里开始执行
static int __init jprobe_init(void)
{
int ret;
//jprobe注册函数
ret = register_jprobe(&my_jprobe);
if (ret < 0) {
printk(KERN_INFO "register_jprobe failed, returned %d\n", ret);
return -1;
}
printk(KERN_INFO "Planted jprobe at %p, handler addr %p\n",
my_jprobe.kp.addr, my_jprobe.entry);
return 0;
}
// 模块exit函数,rmmod卸载模块执行这个函数
static void __exit jprobe_exit(void)
{
//取消jprobe断点
unregister_jprobe(&my_jprobe);
printk(KERN_INFO "jprobe at %p unregistered\n", my_jprobe.kp.addr);
}
module_init(jprobe_init)
module_exit(jprobe_exit)
MODULE_LICENSE("GPL");
2.Kprobe使用