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
浙公网安备 33010602011771号