Linux-5.10.13 kprobe源码分析
Posted 全波形反演
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux-5.10.13 kprobe源码分析相关的知识,希望对你有一定的参考价值。
1. 引言
1.1. kprobe API
内核使用kprobe,可以使用register_kprobe()/unregister_kprobe()进行注册/卸载,还可以临时关闭/使能探测点。
int register_kprobe(struct kprobe *p);//注册kprobe探测点
void unregister_kprobe(struct kprobe *p);//卸载kprobe探测点
int register_kprobes(struct kprobe **kps, int num);//注册多个kprobe探测点
void unregister_kprobes(struct kprobe **kps, int num);//卸载多个kprobe探测点
int disable_kprobe(struct kprobe *kp);//暂停指定定kprobe探测点
int enable_kprobe(struct kprobe *kp);//恢复指定kprobe探测点
void dump_kprobe(struct kprobe *kp);//打印指定kprobe探测点的名称、地址、偏移
对于kretprobe同样有一套接口:
int register_kretprobe(struct kretprobe *rp);
void unregister_kretprobe(struct kretprobe *rp);
int register_kretprobes(struct kretprobe **rps, int num);
void unregister_kretprobes(struct kretprobe **rps, int num);
int disable_kretprobe(struct kretprobe *kp);
int enable_kretprobe(struct kretprobe *kp);
在5.10.13代码samples\kprobes\kprobe_example.c中,probe的是kernel_clone函数,在我的环境中3.10.0-1062.el7.x86_64没有这个sym(可以使用cat /proc/kallsyms查看),我使用do_fork替换(do_fork不至于被调用太多,但又不会没有)。
1.2. samples\kprobes\kprobe_example.c
static char symbol[MAX_SYMBOL_LEN] = "do_fork";
module_param_string(symbol, symbol, sizeof(symbol), 0644);
/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {
//定义实例kp并初始化symbol_name为"_do_fork",将探测_do_fork函数。
.symbol_name = symbol,
};
/* kprobe pre_handler: called just before the probed instruction is executed */
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
pr_info("<%s> pre_handler: p->addr = %pF, ip = %lx, flags = 0x%lx\n",
p->symbol_name, p->addr, regs->ip, regs->flags);
/* A dump_stack() here will give a stack backtrace */
return 0;
}
/* kprobe post_handler: called after the probed instruction is executed */
static void handler_post(struct kprobe *p, struct pt_regs *regs,
unsigned long flags)
{
pr_info("<%s> post_handler: p->addr = %pF, flags = 0x%lx\n",
p->symbol_name, p->addr, regs->flags);
}
/*
* fault_handler: this is called if an exception is generated for any
* instruction within the pre- or post-handler, or when Kprobes
* single-steps the probed instruction.
*/
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
pr_info("fault_handler: p->addr = %pF, trap #%dn", p->addr, trapnr);
/* Return 0 because we don't handle the fault. */
return 0;
}
static int __init kprobe_init(void)
{
int ret;
kp.pre_handler = handler_pre;//初始化kp的三个回调函数。
kp.post_handler = handler_post;
kp.fault_handler = handler_fault;
ret = register_kprobe(&kp);//注册kp探测点到内核。
if (ret < 0) {
pr_err("register_kprobe failed, returned %d\n", ret);
return ret;
}
pr_info("Planted kprobe at %pF\n", kp.addr);
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
pr_info("kprobe at %pF unregistered\n", kp.addr);
}
module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");
下面将结合samples\kprobes\kprobe_example.c
实例代码,对kprobe源代码进行分析。
2. 数据结构
2.1. struct kprobe
struct kprobe { /* */
struct hlist_node hlist; /* 被用于kprobe全局hash,索引值为被探测点的地址。*/
/* list of kprobes for multi-handler support */
struct list_head list; /* 用于链接同一被探测点的不同探测kprobe。*/
/*count the number of times this probe was temporarily disarmed
如果 kprobe 嵌套,增加nmissed字段的数值*/
unsigned long nmissed; /* */
/* location of the probe point */
kprobe_opcode_t *addr; /* 被探测点的地址。*/
/* Allow user to indicate symbol name of the probe point */
const char *symbol_name; /* 被探测函数的名称。*/
/* Offset into the symbol */
unsigned int offset; /* 被探测点在函数内部的偏移,用于探测函数内核的指令,如果该值为0表示函数的入口。*/
/* Called before addr is executed. 在被探测指令被执行前回调*/
kprobe_pre_handler_t pre_handler;
/* Called after addr is executed, unless... 在被探测指令执行完毕后回调(注意不是被探测函数)*/
kprobe_post_handler_t post_handler;
/*
* ... called if executing addr causes a fault (eg. page fault).
* Return 1 if it handled fault, otherwise kernel will see it.
*/
kprobe_fault_handler_t fault_handler; /* 在内存访问出错时被调用 */
/* Saved opcode (which has been replaced with breakpoint) */
kprobe_opcode_t opcode; /* 保存的被探测点原始指令。*/
/* copy of the original instruction */
struct arch_specific_insn ainsn; /* 被复制的被探测点的原始指令,用于单步执行,架构强相关。*/
/*
* Indicates various status flags.
* Protected by kprobe_mutex after this kprobe is registered.
*/
u32 flags; /* 状态标记。*/
};
其中回调函数的执行流程为:
2.2. struct kretprobe
struct kretprobe {
struct kprobe kp;
kretprobe_handler_t handler;
kretprobe_handler_t entry_handler;
int maxactive;
int nmissed;
size_t data_size;
struct hlist_head free_instances;
raw_spinlock_t lock;
};
3. API
3.1. register_kprobe
- 注册kprobe
首先调用kprobe_addr
查找函数名。
3.1.1. kprobe_addr
- 获取内核符号地址
调用_kprobe_addr
static kprobe_opcode_t *kprobe_addr(struct kprobe *p)
{
return _kprobe_addr(p->addr, p->symbol_name, p->offset);
}
调用关系为:
register_kprobe
kprobe_addr
_kprobe_addr
kprobe_lookup_name
kallsyms_lookup_name
kallsyms_expand_symbol 1.首先从内核函数中查找
module_kallsyms_lookup_name 2.如果没找到再从模块中查找
函数kallsyms_sym_address
如下,遍历内核符号:
static unsigned long kallsyms_sym_address(int idx)
{
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
return kallsyms_addresses[idx];
/* values are unsigned offsets if --absolute-percpu is not in effect */
if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))
return kallsyms_relative_base + (u32)kallsyms_offsets[idx];
/* ...otherwise, positive offsets are absolute values */
if (kallsyms_offsets[idx] >= 0)
return kallsyms_offsets[idx];
/* ...and negative offsets are relative to kallsyms_relative_base - 1 */
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
}
下面只给出原文目录,详情请点击阅读原文。
3.1.2. check_kprobe_address_safe
- 检测kprobe是否安全
3.1.3. prepare_kprobe
3.1.4. arm_kprobe
- 处理
3.1.5. arch_arm_kprobe
- 用int3替换代码段
3.1.6. __text_poke
- 找到代码段页,用opcode替换
3.2. register_kprobes
- 略
3.3. unregister_kprobe
- 注销kprobe
3.4. unregister_kprobes
- 注销kprobes
4. 参考和相关链接
点击阅读原文阅读完整文章。
以上是关于Linux-5.10.13 kprobe源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Linux内核 eBPF基础:perf基础perf_event_open系统调用内核源码分析
Android 插件化VirtualApp 源码分析 ( 添加应用源码分析 | LaunchpadAdapter 适配器 | 适配器添加元素 | PackageAppData 元素 )(代
Android 逆向Dalvik 函数抽取加壳 ( 类加载流程分析 | ClassLoader#loadClass 函数分析 | BaseDexClassLoader#findClass 分析 )(代
异常及源码分析org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.type.TypeE(代