探针点
探针点是一个调试语句,有助于探索软件的实行特性(即,实行流程以及当探针语句实行时软件数据构造的状态)。printk是探针语句的最大略形式,也是黑客用于内核攻击的根本工具之一。
由于它须要重新编译源代码,以是printk插入是静态的探测方法。内核代码中主要位置上还有许多其他静态跟踪点可以动态启用或禁用。 Linux内核有一些框架可以帮助程序员探测内核或用户空间运用程序,而无需重新编译源代码。Kprobe是在内核代码中插入探针点的动态方法之一,并且uprobe在用户运用程序中实行此操作。
利用uprobe跟踪用户空间
可以通过利用thesysfs接口或perf工具将uprobe跟踪点插入用户空间代码。
利用sysfs接口插入uprobe
考虑以下大略测试代码,没有打印语句,我们想在某个指令中插入探针:
[source,c].test.c
#include <stdio.h>#include <stdlib.h>#include <unistd.h>
编译代码并找到要探测的指令地址:
# gcc -o test test.c# objdump -d test
假设我们在ARM64平台上有以下目标代码:
0000000000400620 <func_1>: 400620:90000080 adrpx0, 410000 <__FRAME_END__+0xf6f8>
并且我们想在偏移量0x620和0x644之间插入探针。实行以下命令:
# echo 'p:func_2_entry test:0x620' > /sys/kernel/debug/tracing/uprobe_events# echo 'p:func_1_entry test:0x644' >> /sys/kernel/debug/tracing/uprobe_events# echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable# ./test&
在上面的第一个和第二个echo语句中,p见告我们这是一个大略的测试。(探测器可以是大略的或返回的。)func_n_entry是我们在跟踪输出中看到的名称,名称是可选字段,如果没有供应,我们该当期待像p_test_0x644这样的名字。test 是我们要插入探针的可实行二进制文件。如果test 不在当前目录中,则须要指定path_to_test / test。
0x620或0x640是从程序启动开始的指令偏移量。请把稳>>在第二个echo语句中,由于我们要再添加一个探针。以是,当我们在前两个命令中插入探针点之后,我们启用uprobe跟踪,当我们写入events/ uprobes / enable时,它将启用所有的uprobe事宜。程序员还可以通过写入在该事宜目录中创建的特定事宜文件来启用单个事宜。一旦探针点被插入和启用,每当实行探测指令时,我们可以看到一个跟踪条款。
读取跟踪文件以查看输出:
# cat /sys/kernel/debug/tracing/trace# tracer: nop## entries-in-buffer/entries-written: 8/8 #P:8## _-----=> irqs-off# / _----=> need-resched# | / _---=> hardirq/softirq# || / _--=> preempt-depth# ||| / delay# TASK-PID CPU# |||| TIMESTAMP FUNCTION# | | | |||| | |
我们可以看到哪个CPU完成了什么任务,什么时候实行了探测指令。
返回探针也可以插入指令。当返回该指令的函数时,将记录一个条款:
# echo 0 > /sys/kernel/debug/tracing/events/uprobes/enable# echo 'r:func_2_exit test:0x620' >> /sys/kernel/debug/tracing/uprobe_events# echo 'r:func_1_exit test:0x644' >> /sys/kernel/debug/tracing/uprobe_events# echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable
这里我们利用r而不是p,所有其他参数是相同的。请把稳,如果要插入新的探测点,须要禁用uprobe事宜:
test-3009 [002] .... 4813.852674: func_1_entry: (0x400644)
上面的日志表明,func_1返回到地址0x4006b0,韶光戳为4813.852691。
# echo 0 > /sys/kernel/debug/tracing/events/uprobes/enable# echo 'p:func_2_entry test:0x630' > /sys/kernel/debug/tracing/uprobe_events count=%x1# echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable# echo > /sys/kernel/debug/tracing/trace# ./test&
当实行偏移量0x630的指令时,将打印ARM64 x1寄存器的值作为count =。
输出如下所示:
test-3095 [003] .... 7918.629728: func_2_entry: (0x400630) count=0x1
利用perf插入uprobe
找到须要插入探针的指令或功能的偏移量很麻烦,而且须要知道分配给局部变量的CPU寄存器的名称更为繁芜。 perf是一个有用的工具,用于帮助勾引探针插入源代码中。
除了perf,还有一些其他工具,如SystemTap,DTrace和LTTng,可用于内核和用户空间跟踪;然而,perf与内核合营完美,以是它受到内核程序员的青睐。
# gcc -g -o test test.c# perf probe -x ./test func_2_entry=func_2# perf probe -x ./test func_2_exit=func_2%return# perf probe -x ./test test_15=test.c:15# perf probe -x ./test test_25=test.c:25 number# perf record -e probe_test:func_2_entry -e probe_test:func_2_exit -e probe_test:test_15 -e probe_test:test_25 ./test
如上所示,程序员可以将探针点直接插入函数start和return,源文件的特定行号等。可以获取打印的局部变量,并拥有许多其他选项,例如调用函数的所有实例。 perf探针用于创建探针点事宜,那么在实行./testexecutable时,可以利用perf记录来探测这些事宜。当创建一个perf探测点时,可以利用其他录音选项,例如perf stat,可以拥有许多后期剖析选项,如perf脚本或perf报告。
利用perf脚本,上面的例子输出如下:
# perf script
利用kprobe跟踪内核空间
与uprobe一样,可以利用sysfs接口或perf工具将kprobe跟踪点插入到内核代码中。
利用sysfs接口插入kprobe
程序员可以在/proc/kallsyms中的大多数符号中插入kprobe;其他符号已被列入内核的黑名单。还有一些与kprobe插入不兼容的符号,比如kprobe_events文件中的kprobe插入将导致写入缺点。 也可以在符号根本的某个偏移处插入探针,像uprobe一样,可以利用kretprobe跟踪函数的返回,局部变量的值也可以打印在跟踪输出中。
以下是如何做:
; disable all events, just to insure that we see only kprobe output in trace.# echo 0 > /sys/kernel/debug/tracing/events/enable; disable kprobe events until probe points are inseted.# echo 0 > /sys/kernel/debug/tracing/events/kprobes/enable; clear out all the events from kprobe_events, to insure that we see output for; only those for which we have enabled
[root@pratyush ~]# more /sys/kernel/debug/tracing/trace# tracer: nop## entries-in-buffer/entries-written: 9037/9037 #P:8## _-----=> irqs-off# / _----=> need-resched# | / _---=> hardirq/softirq# || / _--=> preempt-depth# ||| / delay# TASK-PID CPU# |||| TIMESTAMP FUNCTION# | | | |||| | |
利用perf插入kprobe
与uprobe一样,程序员可以利用perf在内核代码中插入一个kprobe,可以直接将探针点插入到函数start和return中,源文件的特定行号等。程序员可以向-k选项供应vmlinux,也可以为-s选项供应内核源代码路径:
# perf probe -k vmlinux kfree_entry=kfree# perf probe -k vmlinux kfree_exit=kfree%return# perf probe -s ./ kfree_mid=mm/slub.c:3408 x# perf record -e probe:kfree_entry -e probe:kfree_exit -e probe:kfree_mid sleep 10
利用perf脚本,以上示例的输出:
# perf script
你还有什么更好的方法吗?欢迎在评论区留言!