212 lines
8 KiB
C
212 lines
8 KiB
C
// 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";
|