ebpf基础(一)

Linux内核源码编译

内核编译

可以首先搜索一下当前电脑上可以使用apt-get下载的linux-source版本

1
sudo apt-cache search linux-source

要下载的是5.15.0版本的内核,首先执行sudo apt-get install linux-source-5.15.0来将内核源码给下载到/usr/src/linux-source-5.15.0文件夹下。

之后对当前文件夹执行sudo chmod 777 -R .来赋予当前目录写的权限。就可以对源码包进行解压tar xf linux-source-5.15.0.tar.bz2

然后将cp -R debin ./linux-source-5.15.0复制进去,然后cp /boot/config-$(uname -r) .config

就可以进入linux-source-5.15.0目录下执行make来编译了。

内核工具编译

在源码目录下其实存在很多linux自带的工具,比如perf,可以尝试对其进行编译。进入到/usr/src/linux-source-5.15.0/linux-source-5.15.0/tools/perf目录下直接执行make。

traceFS

监测点详解

1
2
sudo cat /sys/kernel/debug/tracing/available_filter_functions | wc -l
# 记录的是所有的可以被kprobe探测到的挂载点,kprobe就是内核中函数的探测点。
  • available_events文件
    • 在/sys/kernel/debug/tracing目录下存在available_events文件,用来表示该操作系统中可用的挂载点,是在available_filter_functions文件中挑选过的非常稳定的挂载点。

image-20240719101741224

  • events目录

该目录下存放了所有可以被监测的Hook点,并用目录对其进行了分类。比如存在power目录或者syscalls目录,这些目录下都存放的是对应的很多个Hook点。并且在events目录下是存在一个enable文件,该文件是所有监测点的总开关,如果将该文件的内容设置为1的时候,该目录下的所有监测点就会被激活。

image-20240719101828145

但是如果将子文件夹下的enable文件设置为1的话,那么只有该子文件夹下的监测点会被激活。并且如果只想要激活某一个监测点的话,就可以只激活该监测点文件夹下的enable文件。

1
2
3
4
5
6
#比如想要激活raw_syscalls下的所有监测点就要将该目录enable文件设置为1
red256@maomao:~$ sudo ls /sys/kernel/debug/tracing/events/raw_syscalls
enable filter sys_enter sys_exit
#比如想要激活raw_syscalls下的sys_enter监测点就要将该目录enable文件设置为1
red256@maomao:~$ sudo ls /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter
enable filter format hist id inject trigger

在每一个特定的监测点下都会有个format文件,其中的内容是关于该监测点的字段信息,描述了参数、返回值、参数类型等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
red256@maomao:~$ sudo cat /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/format
name: sys_enter
ID: 24
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;

field:long id; offset:8; size:8; signed:1;
field:unsigned long args[6]; offset:16; size:48; signed:0;

print fmt: "NR %ld (%lx, %lx, %lx, %lx, %lx, %lx)", REC->id, REC->args[0], REC->args[1], REC->args[2], REC->args[3], REC->args[4], REC->args[5]

激活uprobes

针对/bin/bash中的readline方法进行uprobe的监测。首先使用readelf来找到readline方法的偏移地址是0x00000000000d2190

1
2
3
4
5
6
7
red256@red256-virtual-machine:~$ readelf -s /bin/bash | grep readline | grep FUNC
348: 00000000000d1dc0 968 FUNC GLOBAL DEFAULT 13 readline_interna[...]
851: 00000000000d0d60 620 FUNC GLOBAL DEFAULT 13 readline_interna[...]
901: 0000000000097364 264 FUNC GLOBAL DEFAULT 13 posix_readline_i[...]
922: 00000000000d5700 208 FUNC GLOBAL DEFAULT 13 readline
1293: 00000000000948d0 24 FUNC GLOBAL DEFAULT 13 initialize_readline
2111: 00000000000d0fd0 776 FUNC GLOBAL DEFAULT 13 readline_interna[...]

之后对文件内容进行更新

1
sudo bash -c 'echo p:readline /bin/bash:0x00000000000d5700 %ip %ax > /sys/kernel/debug/tracing/uprobe_events'

可以查询一下该文件中的内容

1
2
red256@red256-virtual-machine:~$ sudo cat /sys/kernel/debug/tracing/uprobe_events
p:uprobes/readline /bin/bash:0x00000000000d5700 arg1=%ip arg2=%ax

并且当这个文件中存在内容的时候,在/sys/kernel/debug/tracing/events/uprobes目录下就会存在对应的监测点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
red256@red256-virtual-machine:~$ sudo ls /sys/kernel/debug/tracing/events/uprobes
enable filter readline
red256@red256-virtual-machine:~$ sudo ls /sys/kernel/debug/tracing/events/uprobes/readline
enable filter format hist id inject trigger
red256@red256-virtual-machine:~$ sudo ls /sys/kernel/debug/tracing/events/uprobes/readline/format
/sys/kernel/debug/tracing/events/uprobes/readline/format
red256@red256-virtual-machine:~$ sudo cat /sys/kernel/debug/tracing/events/uprobes/readline/format
name: readline
ID: 1634
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;

field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 arg1; offset:16; size:8; signed:0;
field:u64 arg2; offset:24; size:8; signed:0;

print fmt: "(%lx) arg1=0x%Lx arg2=0x%Lx", REC->__probe_ip, REC->arg1, REC->arg2tr

新建好了监测点之后需要启用监测点,执行以下指令

1
2
sudo bash -c 'echo 1 > /sys/kernel/debug/tracing/events/uprobes/readline/enbale'
sudo bash -c 'echo 1 > /sys/kernel/debug/tracing/tracing_on'

可以通过查看/sys/kernel/debug/tracing/trace_pipe文件来查看一下管道输入。每当readline方法被调用的时候这里就会出现一个输出,这里并没有记录真实的字符串,但是记录的是参数的指针等等。

1
2
3
4
5
6
red256@red256-virtual-machine:~$ sudo cat /sys/kernel/debug/tracing/trace_pipe
bash-2171 [000] DNZff 4535.880055: readline: (0x5828a3d8a700) arg1=0x5828a3d8a700 arg2=0x5828a3ddc1bd
bash-2171 [001] DNZff 4577.061549: readline: (0x5828a3d8a700) arg1=0x5828a3d8a700 arg2=0x5828a3ddc1bd
bash-2171 [001] DNZff 4615.412384: readline: (0x5828a3d8a700) arg1=0x5828a3d8a700 arg2=0x5828a3ddc1bd
<...>-4556 [000] DNZff 4621.275109: readline: (0x5dc7ef96c700) arg1=0x5dc7ef96c700 arg2=0x5dc7ef9be1bd
bash-4556 [000] DNZff 4624.673064: readline: (0x5dc7ef96c700) arg1=0x5dc7ef96c700 arg2=0x5dc7ef9be1bd

当不需要的时候需要对监测点进行关闭

1
2
3
4
sudo bash -c 'echo > /sys/kernel/debug/tracing/kprobe_events'
sudo bash -c 'echo > /sys/kernel/debug/tracing/uprobe_events'
sudo bash -c 'echo 0 > /proc/sys/kernel/ftrace_enabled'
sudo bash -c 'echo 0 > /sys/kernel/debug/tracing/tracing_on'

perf工具使用

perf在ebpf流行起来之前,是在linux系统中常用的性能监测工具,那么现在perf涉及到的perf_event_open这个系统调用,被ebpf广泛的使用。

使用sudo apt-get install linux-tools-${uname -r}来安装perf

perf top

使用sudo pref top指令可以实现top指令的效果。会列举现在的程序使用的系统调用。

perf ftrace

perf工具针对ftrace也做了对应的封装,首先是可以执行sudo perf ftrace -F就可以打印所有的可以被探测的函数。实际上是读取了/sys/kernel/debug/tracing/available_filter_functions文件中的内容来打印出来的。

1
2
3
4
red256@red256-virtual-machine:~/Desktop$ sudo perf ftrace -F | wc -l
59699
red256@red256-virtual-machine:~/Desktop$ sudo cat /sys/kernel/debug/tracing/available_filter_functions | wc -l
59699

perf record

当想使用perf来统计2秒中sys_enter_openat系统调用的次数的话,可以使用record指令

1
sudo perf record -e 'syscalls:sys_enter_openat' -aR sleep 2

ebpf指令集

ebpf指令集规范

官方文档:https://docs.kernel.org/bpf/standardization/instruction-set.html