原文:https://blog.csdn.net/u012489236/article/details/127954817
官网:https://www.kernel.org/doc/html/latest/trace/uprobetracer.html
kprobe 可以实现动态内核的注入,基于中断的方法在任意指令中插入追踪代码,并且通过 pre_handler/post_handler去接收回调。另一个 kprobe 的同族是 kretprobe,只不过是针对函数级别的内核监控,根据用户注册时提供的 entry_handler 和 ret_handler 来分别在函数进入时和返回前进行回调。
uprobe ,顾名思义,相对于内核函数/地址的监控,主要用于用户态函数/地址的监控。听起来是不是有点神奇,内核怎么监控用户态函数的调用呢?本章的内容包括:
如何使用uprobe
内核是如何通过uprobe监控用户态的调用,其原理是如何的
1 如何使用uprobe
站在用户视角,我们先看个简单的例子,假设有这么个一个用户程序:
// test.c #include <stdio.h> void foo() { printf("hello, uprobe!\n"); } int main() { foo(); return 0; }
编译好之后,查看某个符号的地址,然后告诉内核我要监控这个地址的调用:
#gcc test.c -o test #readelf -s test | grep foo 56: 000000000000115a 19 FUNC GLOBAL DEFAULT 14 foo #echo 1 > /sys/kernel/debug/tracing/events/uprobes/p_test_0x115a/enable #echo 1 > /sys/kernel/debug/tracing/events/uprobes/p_test_0x115a/enable #echo 1 > /sys/kernel/debug/tracing/tracing_on
然后运行用户程序并检查内核的监控返回:
$ ./test && ./test hello, uprobe! hello, uprobe!
对于内核如何使用uprobe,请参考内核文档 https://www.kernel.org/doc/html/latest/trace/uprobetracer.html,其使用基本跟kprobe类似
2 实现原理
上面的接口是基于 tracefs,即读写文件的方式去与内核交互实现 uprobe 监控。
其中写入 uprobe_events
时会经过一系列内核调用,最终会调用到create_or_delete_trace_uprobe
对于__trace_uprobe_create跟使用kprobe类似,也是大家使用的三板斧
- **alloc_trace_uprobe:**分配 uprobe 结构体
**register_trace_uprobe:**注册 uprobe:
regiseter_uprobe_event: 将 probe 添加到全局列表中,并创建对应的 uprobe debugfs 目录,即上文示例中的 p_test_0x115a
对于uprobe其实整个流程跟kprobe基本类似,我们重点关注于uprobe_register的函数做了些什么
当已经注册了 uprobe 的 ELF 程序被执行时,可执行文件会被 mmap 映射到进程的地址空间,同时内核会将该进程虚拟地址空间中对应的 uprobe 地址替换成断点指令。
与 kprobe 类似,我们可以在触发 uprobe 时候根据对应寄存器去提取当前执行的上下文信息,比如函数的调用参数等。同时 uprobe 也有类似的同族: uretprobe。
通过设置uprobe,我们来看看通过gdb跟踪后,反汇编对应的文件,对于x86平台是不是修改成int3指令,确实是修改成int3指令
指定位置上的指令,头部修改为软件中断指令(同时原指令存档他处):
- 当执行到该位置时,触发软件中断,陷入内核
- 在内核,执行以 注入的 Handler
- 单步执行原指令
- 修正寄存器和栈,回到原有指令流
3 uprobe内核模块验证
我们以ubuntu为试验环境,使用uprobe一般都是编写内核驱动,在模块中定义uprobe_consumer ,然后调用uprobe的API(uprobe_register)来进行注册uprobe
#include <linux/module.h> #include <linux/ptrace.h> #include <linux/uprobes.h> #include <linux/namei.h> #include <linux/moduleparam.h> MODULE_AUTHOR("john doe"); MODULE_LICENSE("GPL v2"); static char *filename; module_param(filename, charp, S_IRUGO); static long offset; module_param(offset, long, S_IRUGO); static int handler_pre(struct uprobe_consumer *self, struct pt_regs *regs){ pr_info("handler: arg0 = %d arg1 =%d \n", (int)regs->di, (int)regs->si); return 0; } static int handler_ret(struct uprobe_consumer *self, unsigned long func, struct pt_regs *regs){ pr_info("ret_handler ret = %d \n", (int)regs->ax); return 0; } static struct uprobe_consumer uc = { .handler = handler_pre, .ret_handler = handler_ret, }; static struct inode *inode; static int __init uprobe_init(void) { struct path path; int ret; ret = kern_path(filename, LOOKUP_FOLLOW, &path); if (ret < 0) { pr_err("kern_path failed, returned %d\n", ret); return ret; } inode = igrab(path.dentry->d_inode); path_put(&path); ret = uprobe_register(inode, offset, &uc); if (ret < 0) { pr_err("register_uprobe failed, returned %d\n", ret); return ret; } return 0; } static void __exit uprobe_exit(void) { uprobe_unregister(inode, offset, &uc); } module_init(uprobe_init); module_exit(uprobe_exit);View Code
此外,没有像 Kprobes 那样提供 uretprobe_register,如果 ret_handler 设置为值,则设置uretprobe,生成Makefile
obj-m := hello-uprobe-world.o KDIR := /lib/modules/$(shell uname -r)/build VERBOSE = 0 all: $(MAKE) -C $(KDIR) M=$(PWD) KBUILD_VERBOSE=$(VERBOSE) CONFIG_DEBUG_INFO=y modules clean: rm -f *.o *.ko *.mod.c Module.symvers modules.order
编译生成ko文件:
准备要跟踪的程序:
#include <stdio.h> int add(int a, int b) { return a + b; } int main(void) { add(1, 2); }
安装ko文件,并执行该文件,我们跟踪add函数,其offset可以通过获取
4 uprobe_event验证
uprobe_events 是一种无需创建内核模块即可使用 Uprobe 的机制。 您可以在与 Ftrace 相同的界面中动态创建探测
root@rlk:/sys/kernel/tracing# echo 'p:sample_uprobe /root/Make/main:0x114a %di %si' > /sys/kernel/debug/tracing/uprobe_events root@rlk:/sys/kernel/tracing# echo 'r:sample_uretprobe /root/Make/main:0x114a %ax' >> /sys/kernel/debug/tracing/uprobe_events root@rlk:/sys/kernel/tracing# echo 1 > /sys/kernel/tracing/events/uprobes/sample_uprobe/enable root@rlk:/sys/kernel/tracing# echo 1 > /sys/kernel/tracing/events/uprobes/sample_uretprobe/enable
查看结果:
5 perf_event_open
perf_event_open系统调用将 BPF 程序附加到 uprobe 事件。 直接编写 BPF 程序可能比较痛苦,因此需要通过 bpftrace 来使用它。
bpftrace -e ‘uretprobe:/root/work/uprobe/main:add {printf(“%d\n”, retval); exit(); }’
检查perf_event_open的使用方式
6 参考文档
Linux 内核监控在 Android 攻防中的应用:https://evilpan.com/2022/01/03/kernel-tracing/#uprobe
https://www.kernel.org/doc/html/latest/trace/uprobetracer.html
标签:kernel,ftrace,tracing,ret,handler,内核,原理,uprobe From: https://www.cnblogs.com/shiqi17/p/18168095