From fd01861c623d492c055450e63c363c6b29f531d9 Mon Sep 17 00:00:00 2001 From: Fancy Zhang Date: Fri, 30 Dec 2022 15:25:45 +0800 Subject: [PATCH] move to libbpf 1.x --- .vscode/settings.json | 4 +- CMakeLists.txt | 6 +- execsnoop-libbpf/CMakeLists.txt | 19 +++++ execsnoop-libbpf/execsnoop.bpf.c | 92 ++++++++++++++++++++++++ execsnoop-libbpf/execsnoop.c | 71 +++++++++++++++++++ execsnoop-libbpf/execsnoop_share.cpp | 96 ++++++++++++++++++++++++++ execsnoop-libbpf/execsnoop_share.h | 20 ++++++ execsnoop-libbpf/readme.md | 18 +++++ src/CMakeLists.txt | 4 +- src/{common.h.cmake => common.cmake.h} | 0 10 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 execsnoop-libbpf/CMakeLists.txt create mode 100644 execsnoop-libbpf/execsnoop.bpf.c create mode 100644 execsnoop-libbpf/execsnoop.c create mode 100644 execsnoop-libbpf/execsnoop_share.cpp create mode 100644 execsnoop-libbpf/execsnoop_share.h create mode 100644 execsnoop-libbpf/readme.md rename src/{common.h.cmake => common.cmake.h} (100%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7fcbddc..ee6578f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,8 @@ "cmake.configureOnOpen": true, "cmake.configureArgs": [ "-Dbuild_static=OFF", - "-Dbuild_execsnoop_dl=OFF" + "-Dbuild_execsnoop_dl=ON", + "-Dbuild_tools=ON", + "-Dbuild_test=ON" ] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 83d49f8..d231903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.14) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -project(cgproxy VERSION 0.19) +project(cgproxy VERSION 1.00) include(GNUInstallDirs) -add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter -Wl,--no-undefined) +add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter -Wno-overlength-strings) # for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -18,7 +18,7 @@ option(build_tools OFF) option(build_test "for develop" OFF) add_subdirectory(src) -add_subdirectory(execsnoop-kernel) +add_subdirectory(execsnoop-libbpf) add_subdirectory(pack) if (build_tools) add_subdirectory(tools) diff --git a/execsnoop-libbpf/CMakeLists.txt b/execsnoop-libbpf/CMakeLists.txt new file mode 100644 index 0000000..b0a050e --- /dev/null +++ b/execsnoop-libbpf/CMakeLists.txt @@ -0,0 +1,19 @@ +find_library(LIBBPF bpf REQUIRED) + +# generate execsnoop.skel.h +add_custom_command(OUTPUT execsnoop.skel.h + COMMAND bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h + COMMAND clang -O2 -g -target bpf -c execsnoop.bpf.c -o execsnoop.bpf.o + COMMAND bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h + DEPENDS execsnoop.bpf.c + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +if (build_execsnoop_dl) + add_library(execsnoop MODULE execsnoop_share.cpp execsnoop.skel.h) + target_link_libraries(execsnoop PRIVATE ${LIBBPF}) + install(TARGETS execsnoop DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/cgproxy/) +else() + add_library(execsnoop execsnoop_share.cpp execsnoop.skel.h) + target_link_libraries(execsnoop PRIVATE ${LIBBPF}) +endif() diff --git a/execsnoop-libbpf/execsnoop.bpf.c b/execsnoop-libbpf/execsnoop.bpf.c new file mode 100644 index 0000000..f8f629c --- /dev/null +++ b/execsnoop-libbpf/execsnoop.bpf.c @@ -0,0 +1,92 @@ +#include "vmlinux.h" +#include +#include +#include + +#define TASK_COMM_LEN 16 +struct event { + char comm[TASK_COMM_LEN]; + pid_t pid; + pid_t tgid; + pid_t ppid; + uid_t uid; +}; + +/* /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/format */ +struct syscalls_enter_execve_args { + __u64 unused; + int syscall_nr; + const char filename_ptr; + const char *const * argv; + const char *const * envp; +}; + +/* /sys/kernel/debug/tracing/events/syscalls/sys_exit_execve/format */ +struct syscalls_exit_execve_args { + __u64 unused; + int syscall_nr; + long ret; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, pid_t); + __type(value, struct event); +} records SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(max_entries, 128); + __type(key, u32); + __type(value, u32); +} perf_events SEC(".maps"); + +SEC("tracepoint/syscalls/sys_enter_execve") +int syscall_enter_execve(struct syscalls_enter_execve_args *ctx){ + pid_t pid, tgid; uid_t uid; + struct event *event; + struct task_struct *task, *task_p; + + u64 id = bpf_get_current_pid_tgid(); + pid = (pid_t)id; + tgid = id >> 32; + uid = (u32)bpf_get_current_uid_gid(); + + struct event empty_event={}; + if (bpf_map_update_elem(&records, &pid, &empty_event, BPF_NOEXIST)!=0) return 0; + event = bpf_map_lookup_elem(&records, &pid); + if (!event) return 0; + + event->pid = pid; + event->tgid = tgid; + event->uid = uid; + + /* ppid is not reliable here, normal in arch, but become 0 in ubuntu 20.04 */ + task = (struct task_struct*)bpf_get_current_task(); + bpf_probe_read(&task_p, sizeof(struct task_struct*),&task->real_parent); + bpf_probe_read(&event->ppid,sizeof(pid_t),&task_p->tgid); + + return 0; +} + +SEC("tracepoint/syscalls/sys_exit_execve") +int syscall_exit_execve(struct syscalls_exit_execve_args *ctx){ + pid_t pid; + struct event *event; + + pid = (pid_t)bpf_get_current_pid_tgid(); + event = bpf_map_lookup_elem(&records,&pid); + if (!event) return 0; + if (ctx->ret < 0) goto cleanup; + + /* get comm */ + bpf_get_current_comm(&event->comm,sizeof(event->comm)); + bpf_perf_event_output(ctx, &perf_events, BPF_F_CURRENT_CPU, event, sizeof(struct event)); +cleanup: + bpf_map_delete_elem(&records, &pid); + return 0; +} + +char _license[] SEC("license") = "GPL"; +u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/execsnoop-libbpf/execsnoop.c b/execsnoop-libbpf/execsnoop.c new file mode 100644 index 0000000..6d59367 --- /dev/null +++ b/execsnoop-libbpf/execsnoop.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include "execsnoop.skel.h" + +#define TASK_COMM_LEN 16 +struct event { + char comm[TASK_COMM_LEN]; + pid_t pid; + pid_t tgid; + pid_t ppid; + uid_t uid; +}; + +#define PERF_BUFFER_PAGES 64 + +static void handle_event(void *ctx, int cpu, void *data, __u32 size) { + auto e = static_cast(data); + printf("comm: %s, pid: %d, tgid: %d, ppid: %d, uid: %d\n",e->comm,e->pid,e->tgid,e->ppid,e->uid); +} + +void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) { + fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); +} + +int bump_memlock_rlimit(void) { + struct rlimit rlim_new = { RLIM_INFINITY, RLIM_INFINITY }; + return setrlimit(RLIMIT_MEMLOCK, &rlim_new); +} + +int main(int argc, char **argv) { + struct perf_buffer_opts pb_opts = {}; + struct perf_buffer *pb; + int err; + + err = bump_memlock_rlimit(); + if (err) { + fprintf(stderr, "failed to increase rlimit: %d\n", err); + return 1; + } + + struct execsnoop_bpf *obj=execsnoop_bpf__open_and_load(); + if (!obj) { + fprintf(stderr, "failed to open and/or load BPF object\n"); + return 1; + } + + err = execsnoop_bpf__attach(obj); + if (err) { + fprintf(stderr, "failed to attach BPF programs\n"); + return err; + } + + #if LIBBPF_MAJOR_VERSION == 0 + pb_opts.sample_cb = handle_event; + pb_opts.lost_cb = handle_lost_events; + pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts); + #else + pb_opts.sz = sizeof(pb_opts); + pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, handle_event, handle_lost_events, nullptr, &pb_opts); + #endif + err = libbpf_get_error(pb); + if (err) { + printf("failed to setup perf_buffer: %d\n", err); + return 1; + } + + while ((err = perf_buffer__poll(pb, -1)) >= 0) {} + kill(0, SIGINT); + return err; +} diff --git a/execsnoop-libbpf/execsnoop_share.cpp b/execsnoop-libbpf/execsnoop_share.cpp new file mode 100644 index 0000000..12d8a91 --- /dev/null +++ b/execsnoop-libbpf/execsnoop_share.cpp @@ -0,0 +1,96 @@ +#include "execsnoop_share.h" + +#include +#include +#include +#include +#include "execsnoop.skel.h" + +namespace CGPROXY::EXECSNOOP { + +#define PERF_BUFFER_PAGES 64 +#define TASK_COMM_LEN 16 +struct event { + char comm[TASK_COMM_LEN]; + pid_t pid; + pid_t tgid; + pid_t ppid; + uid_t uid; +}; + +function callback = NULL; +promise status; + +static void handle_event(void *ctx, int cpu, void *data, __u32 size) { + auto e = static_cast(data); + if (callback) callback(e->pid); +} + +void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) { + fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); +} + +int bump_memlock_rlimit(void) { + struct rlimit rlim_new = { RLIM_INFINITY, RLIM_INFINITY }; + return setrlimit(RLIMIT_MEMLOCK, &rlim_new); +} + +int execsnoop() { + struct perf_buffer_opts pb_opts = {}; + struct perf_buffer *pb; + int err; + bool notified=false; + + err = bump_memlock_rlimit(); + if (err) { + fprintf(stderr, "failed to increase rlimit: %d\n", err); + return 1; + } + + struct execsnoop_bpf *obj=execsnoop_bpf__open_and_load(); + if (!obj) { + fprintf(stderr, "failed to open and/or load BPF object\n"); + return 1; + } + + err = execsnoop_bpf__attach(obj); + if (err) { + fprintf(stderr, "failed to attach BPF programs\n"); + return err; + } + +main_loop: + #if LIBBPF_MAJOR_VERSION == 0 + pb_opts.sample_cb = handle_event; + pb_opts.lost_cb = handle_lost_events; + pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts); + #else + pb_opts.sz = sizeof(pb_opts); + pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, handle_event, handle_lost_events, nullptr, &pb_opts); + #endif + err = libbpf_get_error(pb); + if (err) { + printf("failed to setup perf_buffer: %d\n", err); + return 1; + } + + // notify + if (!notified) {status.set_value(); notified=true;} + + while ((err = perf_buffer__poll(pb, -1)) >= 0) {} + perf_buffer__free(pb); + /* handle Interrupted system call when sleep */ + if (err == -EINTR) goto main_loop; + + perror("perf_buffer__poll"); + kill(0, SIGINT); + return err; +} + +void startThread(function c, promise _status) { + status = move(_status); + callback = c; + execsnoop(); +} + +} diff --git a/execsnoop-libbpf/execsnoop_share.h b/execsnoop-libbpf/execsnoop_share.h new file mode 100644 index 0000000..c465aae --- /dev/null +++ b/execsnoop-libbpf/execsnoop_share.h @@ -0,0 +1,20 @@ +#ifndef EXECSNOOP_SHARE_HPP +#define EXECSNOOP_SHARE_HPP 1 + +#include +#include +#include +using namespace std; + +namespace CGPROXY::EXECSNOOP { + +extern "C" void startThread(function c, promise _status); + +#ifdef BUIlD_EXECSNOOP_DL +// only for dlsym() +using startThread_t=decltype(startThread); +startThread_t *_startThread; +#endif + +} // namespace CGPROXY::EXECSNOOP +#endif diff --git a/execsnoop-libbpf/readme.md b/execsnoop-libbpf/readme.md new file mode 100644 index 0000000..f4c0f1c --- /dev/null +++ b/execsnoop-libbpf/readme.md @@ -0,0 +1,18 @@ +generate `vmlinux.h` + +```shell +bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h +``` + +compiled into BPF ELF file + +```shell +clang -O2 -g -target bpf -c execsnoop.bpf.c -o execsnoop.bpf.o +``` + +generate BPF skeleton .skel.h + +```shell +bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h +``` + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc69880..f1008a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,10 @@ find_package(nlohmann_json REQUIRED) include_directories(${PROJECT_SOURCE_DIR}) -include_directories(${PROJECT_SOURCE_DIR}/execsnoop-kernel/) +include_directories(${PROJECT_SOURCE_DIR}/execsnoop-libbpf/) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -configure_file(common.h.cmake common.h) +configure_file(common.cmake.h ${CMAKE_CURRENT_SOURCE_DIR}/common.h) if (build_execsnoop_dl) add_definitions(-DBUIlD_EXECSNOOP_DL) diff --git a/src/common.h.cmake b/src/common.cmake.h similarity index 100% rename from src/common.h.cmake rename to src/common.cmake.h