目录
代码分析
BPF程序部分
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2021 Sartura */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name)
{
pid_t pid;
const char *filename;
pid = bpf_get_current_pid_tgid() >> 32;
filename = BPF_CORE_READ(name, name);
bpf_printk("KPROBE ENTRY pid = %d, filename = %s\n", pid, filename);
return 0;
}
SEC("kretprobe/do_unlinkat")
int BPF_KRETPROBE(do_unlinkat_exit, long ret)
{
pid_t pid;
pid = bpf_get_current_pid_tgid() >> 32;
bpf_printk("KPROBE EXIT: pid = %d, ret = %ld\n", pid, ret);
return 0;
}
功能说明
用来监控在 Linux 中的文件删除操作的,具体来说是 unlinkat 系统调用
do_unlinkat 和 do_unlinkat_exit 不是系统调用,而是与 unlinkat 系统调用相关的内核函数和 BPF 程序中的探针。
- do_unlinkat 是一个内核函数,它实现了 unlinkat 系统调用的功能。unlinkat 系统调用用于删除一个文件或者目录,其原型如下
int unlinkat(int dirfd, const char *pathname, int flags);
其中 dirfd 是一个文件描述符,它引用了一个目录;pathname 是要删除的文件或者目录的路径;flags 可以是 0,也可以是 AT_REMOVEDIR,如果是 AT_REMOVEDIR,那么 pathname 就会被当作目录处理。
do_unlinkat 接收的参数与此类似,但是 pathname 参数被替换为了一个 struct filename * 类型的参数,这是因为在内核中,文件名经常以 struct filename 结构体的形式进行传递。
- do_unlinkat_exit 是 BPF 程序中定义的一个探针,它是一个 kretprobe。kretprobe 是内核返回探针,它会在某个内核函数执行完毕后触发。在这个 BPF 程序中,do_unlinkat_exit 会在 do_unlinkat 函数执行完毕后触发。
do_unlinkat_exit 探针的功能是获取 do_unlinkat 的返回值,并打印一条包含了执行这个函数的进程的 PID 和返回值的消息。这对于追踪 unlinkat 系统调用的行为非常有用,因为你可以看到每次这个系统调用执行时的 PID 和返回值。
BPF_CORE_READ
BPF_CORE_READ(src, field) 是一个 eBPF 提供的宏,用于从内核空间读取数据。这个宏的工作方式类似于 C 语言的 . 运算符,但是它可以安全地用于读取内核数据,而不会导致 eBPF 程序因为尝试访问无效的内核内存地址而被终止。
BPF_CORE_READ(name, name) 这行代码的意图是从 struct filename *name 结构体中读取 name 字段的值,然后将这个值赋给 filename 变量。在这里,struct filename *name 是内核函数 do_unlinkat 的一个参数,struct filename 结构体的 name 字段通常用于存储文件名。
用户程序部分
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2021 Sartura
* Based on minimal.c by Facebook */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "kprobe.skel.h"
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
return vfprintf(stderr, format, args);
}
static volatile sig_atomic_t stop;
static void sig_int(int signo)
{
stop = 1;
}
int main(int argc, char **argv)
{
struct kprobe_bpf *skel;
int err;
/* Set up libbpf errors and debug info callback */
libbpf_set_print(libbpf_print_fn);
/* Open load and verify BPF application */
skel = kprobe_bpf__open_and_load();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
/* Attach tracepoint handler */
err = kprobe_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}
if (signal(SIGINT, sig_int) == SIG_ERR) {
fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
goto cleanup;
}
printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
"to see output of the BPF programs.\n");
while (!stop) {
fprintf(stderr, ".");
sleep(1);
}
cleanup:
kprobe_bpf__destroy(skel);
return -err;
}
功能说明
加载一个 BPF 程序,将其附加到内核,然后等待用户的中断信号。
执行效果
URL Classifier-6018 [004] d..31 77753.806532: bpf_trace_printk: KPROBE ENTRY pid = 5723, filename = /home/zy/snap/firefox/common/.cache/mozilla/firefox/faxkuo36.default/safebrowsing-updating/social-tracking-protection-twitter-digest256.sbstore
URL Classifier-6018 [004] d..31 77753.806536: bpf_trace_printk: KPROBE EXIT: pid = 5723, ret = 0
ThreadPoolForeg-50602 [006] d..31 77754.683597: bpf_trace_printk: KPROBE ENTRY pid = 6861, filename = /home/zy/.config/clash_win/Cache/Cache_Data/7c2d1cffc51cc88b_0
ThreadPoolForeg-50602 [006] d..31 77754.683616: bpf_trace_printk: KPROBE EXIT: pid = 6861, ret = 0
ThreadPoolForeg-50602 [006] d..31 77754.683625: bpf_trace_printk: KPROBE ENTRY pid = 6861, filename = /home/zy/.config/clash_win/Cache/Cache_Data/a70ac0571625a32a_0
ThreadPoolForeg-50602 [006] d..31 77754.683637: bpf_trace_printk: KPROBE EXIT: pid = 6861, ret = 0
ThreadPoolForeg-50618 [016] d..31 77754.683904: bpf_trace_printk: KPROBE ENTRY pid = 6861, filename = /home/zy/.config/clash_win/Cache/Cache_Data/352fee22ce52ccf1_0
ThreadPoolForeg-50618 [016] d..31 77754.683934: bpf_trace_printk: KPROBE EXIT: pid = 6861, ret = 0
Chrome_IOThread-6809 [012] d..31 77754.684552: bpf_trace_printk: KPROBE ENTRY pid = 6798, filename = /dev/shm/.org.chromium.Chromium.rrvRWo
Chrome_IOThread-6809 [012] d..31 77754.684555: bpf_trace_printk: KPROBE EXIT: pid = 6798, ret = 0
Chrome_IOThread-6809 [014] d..31 77754.685207: bpf_trace_printk: KPROBE ENTRY pid = 6798, filename = /dev/shm/.org.chromium.Chromium.n57azy
Chrome_IOThread-6809 [014] d..31 77754.685209: bpf_trace_printk: KPROBE EXIT: pid = 6798, ret = 0
Chrome_IOThread-6809 [014] d..31 77754.685665: bpf_trace_printk: KPROBE ENTRY pid = 6798, filename = /dev/shm/.org.chromium.Chromium.lHj06A
Chrome_IOThread-6809 [014] d..31 77754.685666: bpf_trace_printk: KPROBE EXIT: pid = 6798, ret = 0
文章评论