kprobe功能的代码实现

1、可以借助/sys/kernel/debug/tracing目录下的文件,linux提供了kprobes功能,抓取内核函数中的入参和返回值。

kprobes,强大的调试工具_sydyh43的博客-CSDN博客

当某些场景不适合用命令行的方式,就需要考虑使用代码的方式实现相应的功能。借助内核kprobe的功能,编译一个内核驱动ko文件。给目标函数执行前后完成打桩,在打桩函数中获取函数的入参值和返回值,从而实现对某个功能的跟踪,如是否打开了某一个文件。

2、其中kprobe打桩功能实现如下。

kprobe功能的代码实现_第1张图片

当系统执行到目标函数时,触发int3异常,完成执行目标函数前pre_handler的调用;然后执行目标函数;当目标函数执行结束最后一刻,触发异常,完成执行目标函数后post_handler的调用。最后恢复正常上下文。

3、具体的代码实现,以open系统函数为例,对应内核态的函数是do_sys_open

#include 
#include 
#include 
#include 

#define FN_LEN      128

static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    int len = 0;
    char kname[FN_LEN] = {0x00};
    char *filename = NULL;

    memset(kname, 0x00, FN_LEN);
#ifdef CONFIG_X86
    filename = (char *)(regs->si);
#endif
    len = strncpy_from_user(kname, filename, FN_LEN);
	if (unlikely(len < 0)) {
		return -1;
	}
    if (strstr(kname, "abcx.log")) {
        printk("pro_name=%s, kname=%s\n", current->comm, kname);
    }

    return 0;
}
 
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
	int fd = regs_return_value(regs);
 
    if (!strcmp(current->comm, "t_open")) {
        printk("fd=%d\n", fd);
    }

    return 0;
}

static struct kretprobe krp = {
    .entry_handler	= entry_handler,
	.handler		= ret_handler,
    .maxactive		= 20,
    .kp.symbol_name = "do_sys_open",
};

static int kretprobe_init(void)
{
	int ret;

	ret = register_kretprobe(&krp);
	if (ret < 0) {
		printk("register_kretprobe failed, returned %d\n", ret);

		return -1;
	}

	printk("Planted return probe at %s: %p\n", krp.kp.symbol_name, krp.kp.addr);

    return 0;
}

static void kretprobe_exit(void)
{
	unregister_kretprobe(&krp);

	printk("kretprobe at %p unregistered\n", krp.kp.addr);

    return;
}

module_init(kretprobe_init);
module_exit(kretprobe_exit);
MODULE_LICENSE("GPL");

 3.1、do_sys_open函数的定义如下

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)

3.2、regs->si就是函数的第二个入参,即filename。不同CPU架构,用于函数入参和返回值的寄存器不一样。此处是x86的架构。

3.3、通过do_sys_open函数定义可知,filename的指针地址是一个用户态的地址,此处是内核态空间,因此不能直接访问,需要借助函数strncpy_from_user实现数据的拷贝。

4、把上面的代码编译成驱动ko文件,insmod到内核,就可以实现用户态open函数调用的跟踪。当用户态打开abcx.log,就可以查看到底是哪个进程在对abcx.log文件进行操作。

你可能感兴趣的:(内核调试,linux)