// SPDX-License-Identifier: GPL-3.0-or-later #define SEC(name) __attribute__((section(name), used)) #define __uint(name, value) int (*name)[value] typedef unsigned int u32; typedef unsigned short u16; typedef unsigned long long u64; #define BPF_MAP_TYPE_HASH 1 #define BPF_MAP_TYPE_PERF_EVENT_ARRAY 4 #define BPF_F_CURRENT_CPU 0xffffffffULL struct trace_event_raw_sys_enter { u64 _common; long id; unsigned long args[6]; }; struct trace_event_raw_sys_exit { u64 _common; long id; long ret; }; struct task_struct { int tgid; struct task_struct *real_parent; } __attribute__((preserve_access_index)); struct syscall_event { u32 pid; u32 ppid; u32 uid; u32 syscall_id; char comm[16]; u64 args[6]; char text[80]; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, sizeof(u64)); __uint(value_size, sizeof(u32)); __uint(max_entries, 64); } monitored_syscalls SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, 16); __uint(value_size, sizeof(u32)); __uint(max_entries, 64); } monitored_host_comms SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, sizeof(u64)); __uint(value_size, sizeof(struct syscall_event)); __uint(max_entries, 4096); } pending_returns SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(u32)); __uint(value_size, sizeof(u32)); } syscall_events SEC(".maps"); static u64 (*bpf_get_current_pid_tgid)(void) = (void *)14; static u64 (*bpf_get_current_uid_gid)(void) = (void *)15; static long (*bpf_get_current_comm)(void *buf, u32 size) = (void *)16; static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *)1; static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, u64 flags) = (void *)2; static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *)3; static long (*bpf_probe_read_user_str)(void *dst, u32 size, const void *unsafe_ptr) = (void *)114; static long (*bpf_probe_read_user)(void *dst, u32 size, const void *unsafe_ptr) = (void *)112; static long (*bpf_probe_read_kernel)(void *dst, u32 size, const void *unsafe_ptr) = (void *)113; static long (*bpf_perf_event_output)(void *ctx, void *map, u64 flags, void *data, u64 size) = (void *)25; static u64 (*bpf_get_current_task)(void) = (void *)35; #define bpf_core_read(dst, size, src) \ bpf_probe_read_kernel((dst), (size), \ (const void *)__builtin_preserve_access_index(src)) SEC("tracepoint/raw_syscalls/sys_enter") int trace_sys_enter(struct trace_event_raw_sys_enter *ctx) { u64 syscall_number = ctx->id; u32 *syscall_id = bpf_map_lookup_elem(&monitored_syscalls, &syscall_number); struct syscall_event event = {}; struct task_struct *task; struct task_struct *parent = 0; if (!syscall_id) return 0; task = (struct task_struct *)bpf_get_current_task(); event.pid = bpf_get_current_pid_tgid() >> 32; event.uid = bpf_get_current_uid_gid(); event.syscall_id = *syscall_id; bpf_core_read(&parent, sizeof(parent), &task->real_parent); if (parent) bpf_core_read(&event.ppid, sizeof(event.ppid), &parent->tgid); bpf_get_current_comm(event.comm, sizeof(event.comm)); // Canonical ID 21 covers write/writev/pwrite variants. Keep the hot path // in-kernel and emit only for process names that can satisfy the // persistence-write host rule. if (*syscall_id >= 21 && *syscall_id <= 26 && !bpf_map_lookup_elem(&monitored_host_comms, event.comm)) return 0; // Match the Python BCC transport's semantic argument layout rather than // exposing pointer-only raw ABI slots in alert keys and details. if (*syscall_id == 3) { // memfd_create(name, flags) event.args[0] = ctx->args[1]; bpf_probe_read_user_str(event.text, sizeof(event.text), (const void *)ctx->args[0]); } else if (*syscall_id == 7 || *syscall_id == 8) { // process_vm_{readv,writev} event.args[0] = ctx->args[0]; // target pid event.args[1] = ctx->args[2]; // local iovec count event.args[2] = ctx->args[4]; // remote iovec count event.args[3] = ctx->args[5]; // flags } else if (*syscall_id == 14) { // chmod(path, mode) event.args[0] = ctx->args[1]; event.args[2] = (u64)-100; // AT_FDCWD bpf_probe_read_user_str(event.text, sizeof(event.text), (const void *)ctx->args[0]); } else if (*syscall_id == 15) { // fchmodat(dirfd, path, mode) event.args[0] = ctx->args[2]; event.args[2] = ctx->args[0]; bpf_probe_read_user_str(event.text, sizeof(event.text), (const void *)ctx->args[1]); } else if (*syscall_id == 16 || *syscall_id == 17) { // chown/lchown(path, uid, gid) event.args[0] = ctx->args[1]; event.args[1] = ctx->args[2]; event.args[2] = (u64)-100; // AT_FDCWD bpf_probe_read_user_str(event.text, sizeof(event.text), (const void *)ctx->args[0]); } else if (*syscall_id == 18) { // fchownat(dirfd, path, uid, gid) event.args[0] = ctx->args[2]; event.args[1] = ctx->args[3]; event.args[2] = ctx->args[0]; bpf_probe_read_user_str(event.text, sizeof(event.text), (const void *)ctx->args[1]); } else if (*syscall_id == 19) { // capset(header, data[2]) u32 effective_low = 0; u32 effective_high = 0; const char *data = (const char *)ctx->args[1]; bpf_probe_read_user(&effective_low, sizeof(effective_low), data); bpf_probe_read_user(&effective_high, sizeof(effective_high), data + 12); event.args[0] = effective_low | ((u64)effective_high << 32); } else if (*syscall_id == 22 || *syscall_id == 23) { // connect/bind(fd, sockaddr, len) const char *address = (const char *)ctx->args[1]; u16 family = 0; u16 port = 0; u32 ipv4 = 0; bpf_probe_read_user(&family, sizeof(family), address); bpf_probe_read_user(&port, sizeof(port), address + 2); event.args[0] = family; event.args[3] = ctx->args[0]; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ event.args[1] = __builtin_bswap16(port); #else event.args[1] = port; #endif if (family == 2) { bpf_probe_read_user(&ipv4, sizeof(ipv4), address + 4); event.args[2] = ipv4; } else if (family == 10) { bpf_probe_read_user(event.text, 16, address + 8); } } else if (*syscall_id >= 24 && *syscall_id <= 26) { // listen/accept/accept4 event.args[3] = ctx->args[0]; } else { #pragma unroll for (int i = 0; i < 6; i++) event.args[i] = ctx->args[i]; } if (*syscall_id >= 21 && *syscall_id <= 26) { u64 pid_tgid = bpf_get_current_pid_tgid(); bpf_map_update_elem(&pending_returns, &pid_tgid, &event, 0); return 0; } bpf_perf_event_output(ctx, &syscall_events, BPF_F_CURRENT_CPU, &event, sizeof(event)); return 0; } SEC("tracepoint/raw_syscalls/sys_exit") int trace_sys_exit(struct trace_event_raw_sys_exit *ctx) { u64 pid_tgid = bpf_get_current_pid_tgid(); struct syscall_event *event = bpf_map_lookup_elem(&pending_returns, &pid_tgid); if (!event) return 0; if (event->syscall_id == 25 || event->syscall_id == 26) event->args[3] = ctx->ret; // Every write-family syscall returns its byte count, so the shared // canonical event is confirmed only when at least one byte was written. if ((event->syscall_id == 21 && ctx->ret > 0) || (event->syscall_id >= 22 && event->syscall_id <= 24 && ctx->ret == 0) || ((event->syscall_id == 25 || event->syscall_id == 26) && ctx->ret >= 0)) bpf_perf_event_output(ctx, &syscall_events, BPF_F_CURRENT_CPU, event, sizeof(*event)); bpf_map_delete_elem(&pending_returns, &pid_tgid); return 0; } char LICENSE[] SEC("license") = "GPL";