系统调用、eBPF、C语言、底层编程基础。
eBPF(扩展的伯克利数据包过滤器)是一项允许用户在内核中运行自定义程序的技术。BPF或cBPF(经典BPF)是eBPF的前身,它提供了一种简单高效的方法来基于预定义规则过滤数据包。与内核模块相比,eBPF程序提供了更高的安全性、可移植性和可维护性。现有多种高级方法可用于处理eBPF程序,如Cilium的Go语言库、bpftrace、libbpf等。
注意
: 本文要求读者对eBPF
有基本了解。如果你不熟悉它,ebpf.io
上的这篇文章是很好的参考资料。你应该已经熟悉著名的工具 strace
。我们将使用eBPF开发类似的工具。例如:
|
|
以下是该文本的地道中文翻译:
在开始编写我们的工具之前,我们需要熟悉一些关键概念。
跟踪点(Tracepoints)
:这些是放置在 Linux 内核代码各个部分的检测点。它们提供了一种方法,可以在不修改内核源代码的情况下,钩入内核中的特定事件或代码路径。可用于跟踪的事件可以在 /sys/kernel/debug/tracing/events
中找到。SEC
宏:它在目标 ELF 文件中创建一个新的段,段名与跟踪点的名称相同。例如,SEC(tracepoint/raw_syscalls/sys_enter)
创建了一个具有这个名称的新段。可以使用 readelf 命令查看这些段。
|
|
映射(Maps)
:这些是可以从 eBPF 程序和用户空间运行的应用程序中访问的共享数据结构。由于 Linux 内核中存在大量的系统调用,我们不会编写一个全面的工具来跟踪所有系统调用。相反,我们将专注于跟踪几个常见的系统调用。为了实现这一目标,我们将编写两类程序:eBPF 程序和加载器(用于将 BPF 对象加载到内核并将其附加进来)。
让我们首先创建一些数据结构来进行初始设置:
|
|
加载器将读取用户通过命令行参数提供的待追踪 ELF 文件的路径。然后,加载器会创建一个子进程,并使用 execve
来运行命令行参数中指定的程序。
父进程将处理加载和附加 eBPF 程序所需的所有设置。它还执行一项关键任务:通过 BPF 哈希映射将子进程的 ID 发送给 eBPF 程序。
|
|
要追踪系统调用,我们需要编写由 tracepoint/raw_syscalls/sys_enter
和 tracepoint/raw_syscalls/sys_exit
跟踪点触发的 eBPF 程序。这些跟踪点提供了对系统调用号和参数的访问。对于给定的系统调用,tracepoint/raw_syscalls/sys_enter
跟踪点总是在 tracepoint/raw_syscalls/sys_exit
跟踪点之前触发。我们可以使用前者获取系统调用参数,使用后者获取返回值。
此外,我们将使用 eBPF 映射在用户空间程序和我们的 eBPF 程序之间共享信息。具体来说,我们将使用两种类型的 eBPF 映射:哈希映射和环形缓冲区。
|
|
确定了映射关系之后,我们就可以动手写代码了。首先,让我们来编写针对追踪点 tracepoint/raw_syscalls/sys_enter 的程序代码。
|
|
同理,我们也能编写用于读取返回值并将其传递给用户态空间的程序代码。
|
|
现在,让我们来完善加载器程序中父进程的功能部分。但在进行之前,我们需要理解几个关键函数的工作原理。
1、bpf_object__open
: 通过打开由传递路径指向的 BPF ELF 对象文件并在内存中加载它,创建一个 bpf_object
结构体实例。
|
|
2、bpf_object__load
: 将 BPF 对象加载到内核中。
|
|
3、bpf_object__find_program_by_name
: 返回指向有效 BPF 程序的指针。
|
|
4、bpf_program__attach
: 根据自动检测的程序类型、附加类型和适用的额外参数,将 BPF 程序附加到内核。
|
|
5、bpf_map__update_elem
: 允许在与提供的键对应的 BPF 映射中插入或更新值。
|
|
6、bpf_object__find_map_fd_by_name
: 给定一个 BPF 映射名称,返回该映射的文件描述符。
|
|
7、ring_buffer__new
: 返回指向环形缓冲区的指针。
|
|
第二个参数必须是一个可用于处理从环形缓冲区接收的数据的回调函数。
|
|
它会打印系统调用的名称和参数。
8、ring_buffer__consume
: 此函数处理环形缓冲区中可用的事件。
|
|
现在我们有了编写加载器所需的一切要素。
|
|
以下便是 eBPF 程序的部分。所有的 C 语言源码最终会被编译整合成单一的对象文件。
|
|
编译之前,我们不妨先构建一个测试程序,以便后续使用我们的工具对其进行追踪分析。
|
|
可以利用下面提供的 Makefile 来完成所有相关组件的编译工作。
|
|
整个代码可以在以下的GitHub仓库中找到:
https://github.com/0xSh4dy/bee_tracer
参考链接: