From 3a67878cffb51f36a4bca67420557cf4e68a6188 Mon Sep 17 00:00:00 2001 From: tanjunchen Date: Wed, 28 Feb 2024 00:30:58 +0800 Subject: [PATCH] fix bug for 24-hide (#111) --- .gitignore | 2 + README.md | 12 +++++ README.zh.md | 12 +++++ src/24-hide/README.md | 6 +-- src/24-hide/README_en.md | 6 +-- src/24-hide/pidhide.bpf.c | 76 ++++++++++++++++++----------- src/24-hide/pidhide.c | 11 ++--- src/24-hide/{common.h => pidhide.h} | 14 +++++- 8 files changed, 94 insertions(+), 45 deletions(-) rename src/24-hide/{common.h => pidhide.h} (53%) diff --git a/.gitignore b/.gitignore index f8d81b90..ed350fdc 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,5 @@ src/12-profile/profile ecli-server ecc ecli + +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index bf7fddc2..8c12074a 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,18 @@ TIME COMM TID LAT(us) ![docker](imgs/docker.png) +## build + +The example of local compilation is shown as follows: + +```shell +$ git clone https://github.com/eunomia-bpf/bpf-developer-tutorial.git +$ cd bpf-developer-tutorial +$ git submodule update --init --recursive # Synchronize submodule +$ cd src/24-hide +$ make +``` + ## Why do we need tutorials based on libbpf and BPF CO-RE? > In history, when it comes to developing a BPF application, one could choose the BCC framework to load the BPF program into the kernel when implementing various BPF programs for Tracepoints. BCC provides a built-in Clang compiler that can compile BPF code at runtime and customize it into a program that conforms to a specific host kernel. This is the only way to develop maintainable BPF applications under the constantly changing internal kernel environment. The portability of BPF and the introduction of CO-RE are detailed in the article "BPF Portability and CO-RE", explaining why BCC was the only viable option before and why libbpf is now considered a better choice. Last year, Libbpf saw significant improvements in functionality and complexity, eliminating many differences with BCC (especially for Tracepoints applications) and adding many new and powerful features that BCC does not support (such as global variables and BPF skeletons) diff --git a/README.zh.md b/README.zh.md index ab2d6ffa..6cd62e7c 100644 --- a/README.zh.md +++ b/README.zh.md @@ -147,6 +147,18 @@ TIME COMM TID LAT(us) ![docker](imgs/docker.png) +## 本地编译 + +本地编译示例如下所示: + +```shell +$ git clone https://github.com/eunomia-bpf/bpf-developer-tutorial.git +$ cd bpf-developer-tutorial +$ git submodule update --init --recursive # 同步 submodule 子模块 +$ cd src/24-hide +$ make +``` + ## 为什么需要基于 libbpf 和 BPF CO-RE 的教程? > 历史上,当需要开发一个BPF应用时可以选择BCC 框架,在实现各种用于Tracepoints的BPF程序时需要将BPF程序加载到内核中。BCC提供了内置的Clang编译器,可以在运行时编译BPF代码,并将其定制为符合特定主机内核的程序。这是在不断变化的内核内部下开发可维护的BPF应用程序的唯一方法。在BPF的可移植性和CO-RE一文中详细介绍了为什么会这样,以及为什么BCC是之前唯一的可行方式,此外还解释了为什么 libbpf 是目前比较好的选择。去年,Libbpf的功能和复杂性得到了重大提升,消除了与BCC之间的很多差异(特别是对Tracepoints应用来说),并增加了很多BCC不支持的新的且强大的特性(如全局变量和BPF skeletons)。 diff --git a/src/24-hide/README.md b/src/24-hide/README.md index b9c06470..06b4c3ff 100644 --- a/src/24-hide/README.md +++ b/src/24-hide/README.md @@ -98,7 +98,7 @@ const volatile int target_ppid = 0; // of the PID to hide. This becomes the name // of the folder in /proc/ const volatile int pid_to_hide_len = 0; -const volatile char pid_to_hide[max_pid_len]; +const volatile char pid_to_hide[MAX_PID_LEN]; // struct linux_dirent64 { // u64 d_ino; /* 64-bit inode number */ @@ -177,7 +177,7 @@ int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) struct linux_dirent64 *dirp = 0; int pid = pid_tgid >> 32; short unsigned int d_reclen = 0; - char filename[max_pid_len]; + char filename[MAX_PID_LEN]; unsigned int bpos = 0; unsigned int *pBPOS = bpf_map_lookup_elem(&map_bytes_read, &pid_tgid); @@ -256,7 +256,7 @@ int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen); // Debug print - char filename[max_pid_len]; + char filename[MAX_PID_LEN]; bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp_previous->d_name); filename[pid_to_hide_len-1] = 0x00; bpf_printk("[PID_HIDE] filename previous %s\n", filename); diff --git a/src/24-hide/README_en.md b/src/24-hide/README_en.md index f0d6289c..50ed6d74 100644 --- a/src/24-hide/README_en.md +++ b/src/24-hide/README_en.md @@ -98,7 +98,7 @@ const volatile int target_ppid = 0; // of the PID to hide. This becomes the name // of the folder in /proc/ const volatile int pid_to_hide_len = 0; -const volatile char pid_to_hide[max_pid_len]; +const volatile char pid_to_hide[MAX_PID_LEN]; // struct linux_dirent64 { // u64 d_ino; /* 64-bit inode number */ @@ -177,7 +177,7 @@ int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) struct linux_dirent64 *dirp = 0; int pid = pid_tgid >> 32; short unsigned int d_reclen = 0; - char filename[max_pid_len]; + char filename[MAX_PID_LEN]; unsigned int bpos = 0; unsigned int *pBPOS = bpf_map_lookup_elem(&map_bytes_read, &pid_tgid); @@ -256,7 +256,7 @@ int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen); // Debug print - char filename[max_pid_len]; + char filename[MAX_PID_LEN]; bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp_previous->d_name); filename[pid_to_hide_len-1] = 0x00; bpf_printk("[PID_HIDE] filename previous %s\n", filename); diff --git a/src/24-hide/pidhide.bpf.c b/src/24-hide/pidhide.bpf.c index 47f88958..70fe1ec8 100644 --- a/src/24-hide/pidhide.bpf.c +++ b/src/24-hide/pidhide.bpf.c @@ -3,18 +3,20 @@ #include #include #include -#include "common.h" +#include "pidhide.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; // Ringbuffer Map to pass messages from kernel to user -struct { +struct +{ __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps"); // Map to fold the dents buffer addresses -struct { +struct +{ __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 8192); __type(key, size_t); @@ -23,7 +25,8 @@ struct { // Map used to enable searching through the // data in a loop -struct { +struct +{ __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 8192); __type(key, size_t); @@ -31,7 +34,8 @@ struct { } map_bytes_read SEC(".maps"); // Map with address of actual -struct { +struct +{ __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 8192); __type(key, size_t); @@ -39,7 +43,8 @@ struct { } map_to_patch SEC(".maps"); // Map to hold program tail calls -struct { +struct +{ __uint(type, BPF_MAP_TYPE_PROG_ARRAY); __uint(max_entries, 5); __type(key, __u32); @@ -53,14 +58,14 @@ const volatile int target_ppid = 0; // of the PID to hide. This becomes the name // of the folder in /proc/ const volatile int pid_to_hide_len = 0; -const volatile char pid_to_hide[max_pid_len]; +const volatile char pid_to_hide[MAX_PID_LEN]; // struct linux_dirent64 { // u64 d_ino; /* 64-bit inode number */ // u64 d_off; /* 64-bit offset to next structure */ // unsigned short d_reclen; /* Size of this dirent */ // unsigned char d_type; /* File type */ -// char d_name[]; /* Filename (null-terminated) */ }; +// char d_name[]; /* Filename (null-terminated) */ }; // int getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count); SEC("tp/syscalls/sys_enter_getdents64") int handle_getdents_enter(struct trace_event_raw_sys_enter *ctx) @@ -68,10 +73,12 @@ int handle_getdents_enter(struct trace_event_raw_sys_enter *ctx) size_t pid_tgid = bpf_get_current_pid_tgid(); // Check if we're a process thread of interest // if target_ppid is 0 then we target all pids - if (target_ppid != 0) { + if (target_ppid != 0) + { struct task_struct *task = (struct task_struct *)bpf_get_current_task(); int ppid = BPF_CORE_READ(task, real_parent, tgid); - if (ppid != target_ppid) { + if (ppid != target_ppid) + { return 0; } } @@ -92,13 +99,15 @@ int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) size_t pid_tgid = bpf_get_current_pid_tgid(); int total_bytes_read = ctx->ret; // if bytes_read is 0, everything's been read - if (total_bytes_read <= 0) { + if (total_bytes_read <= 0) + { return 0; } // Check we stored the address of the buffer from the syscall entry - long unsigned int* pbuff_addr = bpf_map_lookup_elem(&map_buffs, &pid_tgid); - if (pbuff_addr == 0) { + long unsigned int *pbuff_addr = bpf_map_lookup_elem(&map_buffs, &pid_tgid); + if (pbuff_addr == 0) + { return 0; } @@ -111,29 +120,35 @@ int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) struct linux_dirent64 *dirp = 0; int pid = pid_tgid >> 32; short unsigned int d_reclen = 0; - char filename[max_pid_len]; + char filename[MAX_PID_LEN]; unsigned int bpos = 0; unsigned int *pBPOS = bpf_map_lookup_elem(&map_bytes_read, &pid_tgid); - if (pBPOS != 0) { + if (pBPOS != 0) + { bpos = *pBPOS; } - for (int i = 0; i < 200; i ++) { - if (bpos >= total_bytes_read) { + for (int i = 0; i < 200; i++) + { + if (bpos >= total_bytes_read) + { break; } - dirp = (struct linux_dirent64 *)(buff_addr+bpos); + dirp = (struct linux_dirent64 *)(buff_addr + bpos); bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen); bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp->d_name); int j = 0; - for (j = 0; j < pid_to_hide_len; j++) { - if (filename[j] != pid_to_hide[j]) { + for (j = 0; j < pid_to_hide_len; j++) + { + if (filename[j] != pid_to_hide[j]) + { break; } } - if (j == pid_to_hide_len) { + if (j == pid_to_hide_len) + { // *********** // We've found the folder!!! // Jump to handle_getdents_patch so we can remove it! @@ -148,7 +163,8 @@ int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) // If we didn't find it, but there's still more to read, // jump back the start of this function and keep looking - if (bpos < total_bytes_read) { + if (bpos < total_bytes_read) + { bpf_map_update_elem(&map_bytes_read, &pid_tgid, &bpos, BPF_ANY); bpf_tail_call(ctx, &map_prog_array, PROG_01); } @@ -163,8 +179,9 @@ int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) { // Only patch if we've already checked and found our pid's folder to hide size_t pid_tgid = bpf_get_current_pid_tgid(); - long unsigned int* pbuff_addr = bpf_map_lookup_elem(&map_to_patch, &pid_tgid); - if (pbuff_addr == 0) { + long unsigned int *pbuff_addr = bpf_map_lookup_elem(&map_to_patch, &pid_tgid); + if (pbuff_addr == 0) + { return 0; } @@ -176,17 +193,17 @@ int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) short unsigned int d_reclen_previous = 0; bpf_probe_read_user(&d_reclen_previous, sizeof(d_reclen_previous), &dirp_previous->d_reclen); - struct linux_dirent64 *dirp = (struct linux_dirent64 *)(buff_addr+d_reclen_previous); + struct linux_dirent64 *dirp = (struct linux_dirent64 *)(buff_addr + d_reclen_previous); short unsigned int d_reclen = 0; bpf_probe_read_user(&d_reclen, sizeof(d_reclen), &dirp->d_reclen); // Debug print - char filename[max_pid_len]; + char filename[MAX_PID_LEN]; bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp_previous->d_name); - filename[pid_to_hide_len-1] = 0x00; + filename[pid_to_hide_len - 1] = 0x00; bpf_printk("[PID_HIDE] filename previous %s\n", filename); bpf_probe_read_user_str(&filename, pid_to_hide_len, dirp->d_name); - filename[pid_to_hide_len-1] = 0x00; + filename[pid_to_hide_len - 1] = 0x00; bpf_printk("[PID_HIDE] filename next one %s\n", filename); // Attempt to overwrite @@ -196,7 +213,8 @@ int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) // Send an event struct event *e; e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (e) { + if (e) + { e->success = (ret == 0); e->pid = (pid_tgid >> 32); bpf_get_current_comm(&e->comm, sizeof(e->comm)); diff --git a/src/24-hide/pidhide.c b/src/24-hide/pidhide.c index 021d51be..4da32ed4 100644 --- a/src/24-hide/pidhide.c +++ b/src/24-hide/pidhide.c @@ -13,14 +13,7 @@ #include #include "pidhide.skel.h" -#include "common.h" - -// These are used by a number of -// different programs to sync eBPF Tail Call -// login between user space and kernel -#define PROG_00 0 -#define PROG_01 1 -#define PROG_02 2 +#include "pidhide.h" // Setup Argument stuff static struct env @@ -44,6 +37,7 @@ static const struct argp_option opts[] = { {"target-ppid", 't', "TARGET-PPID", 0, "Optional Parent PID, will only affect its children."}, {}, }; + static error_t parse_arg(int key, char *arg, struct argp_state *state) { switch (key) @@ -74,6 +68,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) } return 0; } + static const struct argp argp = { .options = opts, .parser = parse_arg, diff --git a/src/24-hide/common.h b/src/24-hide/pidhide.h similarity index 53% rename from src/24-hide/common.h rename to src/24-hide/pidhide.h index ac4be7fd..4c7aae8b 100644 --- a/src/24-hide/common.h +++ b/src/24-hide/pidhide.h @@ -5,10 +5,20 @@ // Simple message structure to get events from eBPF Programs // in the kernel to user spcae #define TASK_COMM_LEN 16 -struct event { +#define MAX_PID_LEN 16 + +// These are used by a number of +// different programs to sync eBPF Tail Call +// login between user space and kernel +#define PROG_00 0 +#define PROG_01 1 +#define PROG_02 2 + +struct event +{ int pid; char comm[TASK_COMM_LEN]; bool success; }; -#endif // BAD_BPF_COMMON_H +#endif // BAD_BPF_COMMON_H