linux kprobes

kprobes



kprobes Kretprobes是linux系统的一个动态调试机制,
使用它可以向内核添加探针(Probe),在代码执行前或执行后触发一个回调函数。
这个机制通常用于调试内核代码,跟踪应用程序执行或收集性能统计信息。
通过使用kprobe,开发人员可以在不影响系统运行逻辑的情况下,对操作系统进行深入的分析和调试。

reference:
linux/Documentation/kprobes.txt


Linux内核调试技术——kprobe使用与实现(一)
Linux内核调试技术——kprobe使用与实现(二)






kernel config:
General architecture-dependent options --->
[*] Kprobes




sample:
linux/samples/kprobes



#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kprobes.h>
#include <linux/sched.h>

static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	struct task_struct *cur = current;

	int sig = (int)regs->regs[0];
	struct task_struct *task = (struct task_struct *)regs->regs[2];

	/*pr_info("pre_handler, p->pc:0x%p, pc = 0x%lx\n", p->addr, regs->pc);*/

	/* if (sig == 9 || sig == 14 || sig == 15) */
	pr_info("signal:%d, cur_task:%s -> task:%s\n", 
		sig, cur->comm, task->comm);

	return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
	/* pr_info("post_handler, p->pc:0x%p, flags:0x%lx\n", p->addr, flags); */
}

static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
	pr_info("fault_handler, p->pc:0x%p, trap:%dn", p->addr, trapnr);

	return 0;
}

static struct kprobe kp = {
	.symbol_name    = "do_send_sig_info",
	.pre_handler   = handler_pre,
	.post_handler  = handler_post,
	.fault_handler = handler_fault,
};

static int __init kprobe_init(void)
{
	int ret;

	pr_info("%s()\n", __func__);

	ret = register_kprobe(&kp);
	if (ret < 0) {
		pr_err("register_kprobe failed: %d\n", ret);
		return ret;
	}

	pr_info("%s() done\n", __func__);

	return 0;
}

static void __exit kprobe_exit(void)
{
	unregister_kprobe(&kp);

	pr_info("%s()\n", __func__);
}

module_init(kprobe_init);
module_exit(kprobe_exit);

MODULE_AUTHOR("wangyangkai");
MODULE_DESCRIPTION("arm64 kprobe example");
MODULE_LICENSE("GPL");


log:

~$ 
~$ killall top
[  123.348849] signal:15, cur task:killall -> task:top
~$ 


#define pr_fmt(fmt) "kt %s() " fmt, __func__

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>

static char symbol[KSYM_NAME_LEN] = "do_send_sig_info";
module_param_string(symbol, symbol, KSYM_NAME_LEN, 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 __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	struct task_struct *cur = current;

	pr_info("taks:%s <%s> p->addr:0x%p, pc:0x%lx, pstate:0x%lx\n",
		cur->comm,
		p->symbol_name, 
		p->addr, (long)regs->pc, (long)regs->pstate);

	dump_stack();

	return 0;
}

/* kprobe post_handler: called after the probed instruction is executed */
static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags)
{
	/*pr_info("<%s> p->addr = 0x%p, pstate = 0x%lx\n",
		p->symbol_name, p->addr, (long)regs->pstate);*/
}

static int __init kprobe_init(void)
{
	int ret;
	kp.pre_handler = handler_pre;
	kp.post_handler = handler_post;

	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_AUTHOR("wangyangkai");
MODULE_DESCRIPTION("arm64 kprobe example");
MODULE_LICENSE("GPL");


#define pr_fmt(fmt) "kt %s() " fmt, __func__

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/atomic.h>

#define MAX_SYMBOL_LEN	(KSYM_NAME_LEN)

static char symbol_1[MAX_SYMBOL_LEN];
static char symbol_2[MAX_SYMBOL_LEN];
static struct kprobe kp1;
static struct kprobe kp2;
static struct kobject *kprobe_kobj;
static atomic64_t call_count1 = ATOMIC64_INIT(0);
static atomic64_t call_count2 = ATOMIC64_INIT(0);
static bool dump_stack_enable = false;

static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs);
static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags);

static int register_new_kprobe(struct kprobe *kprobe, const char *sym)
{
	int ret;

	memset(kprobe, 0, sizeof(*kprobe));
	kprobe->symbol_name = sym;
	kprobe->pre_handler = handler_pre;
	kprobe->post_handler = handler_post;

	ret = register_kprobe(kprobe);
	if (ret < 0) {
		pr_err("register_kprobe failed for %s, returned %d\n", sym, ret);
		return ret;
	}
	pr_info("Planted kprobe at %p, symbol:%s\n", kprobe->addr, sym);
	return 0;
}

/* kprobe pre_handler: called just before the probed instruction is executed */
static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	struct task_struct *cur = current;
	s64 count;
	atomic64_t *counter;

	/* Select the appropriate counter based on which kprobe triggered */
	if (p->symbol_name == symbol_1)
		counter = &call_count1;
	else if (p->symbol_name == symbol_2)
		counter = &call_count2;
	else
		counter = &call_count1;	/* fallback */

	count = atomic64_inc_return(counter);

	pr_info("taks:%s <%s> p->addr:0x%p, pc:0x%lx, pstate:0x%lx, call_count:%lld\n",
		cur->comm,
		p->symbol_name,
		p->addr, (long)regs->pc, (long)regs->pstate, count);

	if (dump_stack_enable)
		dump_stack();

	return 0;
}

/* kprobe post_handler: called after the probed instruction is executed */
static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags)
{
	/*pr_info("<%s> p->addr = 0x%p, pstate = 0x%lx\n",
		p->symbol_name, p->addr, (long)regs->pstate);*/
}

static ssize_t symbol_1_show(struct kobject *kobj, struct kobj_attribute *attr,
			     char *buf)
{
	return sprintf(buf, "%s\n", symbol_1);
}

static ssize_t symbol_1_store(struct kobject *kobj, struct kobj_attribute *attr,
			      const char *buf, size_t count)
{
	int ret;
	char new_sym[MAX_SYMBOL_LEN];

	/* Parse the input */
	if (sscanf(buf, "%127s", new_sym) != 1)
		return -EINVAL;

	/* Handle "null" to stop tracing */
	if (strcmp(new_sym, "null") == 0) {
		if (symbol_1[0] != '\0') {
			unregister_kprobe(&kp1);
			pr_info("kprobe1 at %p unregistered\n", kp1.addr);
			symbol_1[0] = '\0';
		}
		pr_info("kprobe1 tracing disabled\n");
		return count;
	}

	/* Unregister old kprobe if symbol was set */
	if (symbol_1[0] != '\0') {
		unregister_kprobe(&kp1);
		pr_info("kprobe1 at %p unregistered\n", kp1.addr);
	}

	/* Update the symbol name before register */
	strncpy(symbol_1, new_sym, MAX_SYMBOL_LEN - 1);
	symbol_1[MAX_SYMBOL_LEN - 1] = '\0';

	/* Reset call count for new symbol */
	atomic64_set(&call_count1, 0);

	/* Register new kprobe */
	ret = register_new_kprobe(&kp1, symbol_1);
	if (ret < 0) {
		symbol_1[0] = '\0';
		return ret;
	}

	return count;
}

static ssize_t symbol_2_show(struct kobject *kobj, struct kobj_attribute *attr,
			     char *buf)
{
	return sprintf(buf, "%s\n", symbol_2);
}

static ssize_t symbol_2_store(struct kobject *kobj, struct kobj_attribute *attr,
			      const char *buf, size_t count)
{
	int ret;
	char new_sym[MAX_SYMBOL_LEN];

	/* Parse the input */
	if (sscanf(buf, "%127s", new_sym) != 1)
		return -EINVAL;

	/* Handle "null" to stop tracing */
	if (strcmp(new_sym, "null") == 0) {
		if (symbol_2[0] != '\0') {
			unregister_kprobe(&kp2);
			pr_info("kprobe2 at %p unregistered\n", kp2.addr);
			symbol_2[0] = '\0';
		}
		pr_info("kprobe2 tracing disabled\n");
		return count;
	}

	/* Unregister old kprobe if symbol was set */
	if (symbol_2[0] != '\0') {
		unregister_kprobe(&kp2);
		pr_info("kprobe2 at %p unregistered\n", kp2.addr);
	}

	/* Update the symbol name before register */
	strncpy(symbol_2, new_sym, MAX_SYMBOL_LEN - 1);
	symbol_2[MAX_SYMBOL_LEN - 1] = '\0';

	/* Reset call count for new symbol */
	atomic64_set(&call_count2, 0);

	/* Register new kprobe */
	ret = register_new_kprobe(&kp2, symbol_2);
	if (ret < 0) {
		symbol_2[0] = '\0';
		return ret;
	}

	return count;
}

static ssize_t dump_stack_show(struct kobject *kobj, struct kobj_attribute *attr,
				char *buf)
{
	return sprintf(buf, "%d\n", dump_stack_enable ? 1 : 0);
}

static ssize_t dump_stack_store(struct kobject *kobj, struct kobj_attribute *attr,
				 const char *buf, size_t count)
{
	int val;

	if (kstrtoint(buf, 10, &val) != 0)
		return -EINVAL;

	if (val == 1)
		dump_stack_enable = true;
	else if (val == 0)
		dump_stack_enable = false;
	else
		return -EINVAL;

	return count;
}

static struct kobj_attribute symbol_1_attr =
	__ATTR(symbol_1, 0644, symbol_1_show, symbol_1_store);

static struct kobj_attribute symbol_2_attr =
	__ATTR(symbol_2, 0644, symbol_2_show, symbol_2_store);

static struct kobj_attribute dump_stack_attr =
	__ATTR(dump_stack, 0644, dump_stack_show, dump_stack_store);

static struct attribute *kprobe_attrs[] = {
	&symbol_1_attr.attr,
	&symbol_2_attr.attr,
	&dump_stack_attr.attr,
	NULL,
};

static struct attribute_group kprobe_attr_group = {
	.attrs = kprobe_attrs,
};

static int __init kprobe_init(void)
{
	int ret;

	/* Initialize symbols as empty */
	symbol_1[0] = '\0';
	symbol_2[0] = '\0';

	/* Create kobject */
	kprobe_kobj = kobject_create_and_add("kprobe_trace", kernel_kobj);
	if (!kprobe_kobj) {
		pr_err("Failed to create kobject\n");
		return -ENOMEM;
	}

	/* Create sysfs files */
	ret = sysfs_create_group(kprobe_kobj, &kprobe_attr_group);
	if (ret) {
		pr_err("Failed to create sysfs group: %d\n", ret);
		kobject_put(kprobe_kobj);
		return ret;
	}

	pr_info("kprobe_trace module loaded, no symbol configured yet\n");
	return 0;
}

static void __exit kprobe_exit(void)
{
	/* Unregister kprobes if symbols were configured */
	if (symbol_1[0] != '\0') {
		unregister_kprobe(&kp1);
		pr_info("kprobe1 at %p unregistered\n", kp1.addr);
	}
	if (symbol_2[0] != '\0') {
		unregister_kprobe(&kp2);
		pr_info("kprobe2 at %p unregistered\n", kp2.addr);
	}

	sysfs_remove_group(kprobe_kobj, &kprobe_attr_group);
	kobject_put(kprobe_kobj);
}

module_init(kprobe_init)
module_exit(kprobe_exit)

MODULE_AUTHOR("wangyangkai");
MODULE_DESCRIPTION("arm64 kprobe example");
MODULE_LICENSE("GPL");

root@bsp:/# echo __free_pages > /sys/kernel/kprobe_trace/symbol_1 
root@bsp:/# echo __alloc_pages > /sys/kernel/kprobe_trace/symbol_2
root@bsp:/# echo 1 > /sys/kernel/kprobe_trace/dump_stack 



written by wangyangkai on 2021.05.10


posted @ 2022-12-05 11:19  王阳开  阅读(134)  评论(0)    收藏  举报