Skip to content

Commit 525c119

Browse files
committed
init: trace openat and unlinkat syscalls
0 parents  commit 525c119

File tree

5 files changed

+199
-0
lines changed

5 files changed

+199
-0
lines changed

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Argus
2+
Argus is an eBPF-based File Integrity Monitoring (FIM) tool that provides real-time detection of unauthorized file modifications by leveraging eBPF to efficiently track file system events at the kernel level.
3+
4+
## Prerequisites:
5+
- Linux kernel version 5.7 or later
6+
- LLVM 11 or later
7+
- libbpf headers
8+
- Linux kernel headers
9+
- Go compiler version supported by ebpf-go's Go module
10+
- [ebpf-go library](https://github.com/cilium/ebpf)
11+
12+
13+
## How to run:
14+
```
15+
go generate
16+
go build (An executable named filemon will be generated if the build succeeds)
17+
sudo ./filemon
18+
```
19+
20+
## Features:
21+
- Monitors file operations by tracing syscalls using eBPF
22+
- Currently traces openat and unlinkat syscalls
23+
- Reports PID, process name, operation type, and filename for each event

bpf/file_monitor.c

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include <linux/bpf.h>
2+
#include <bpf/bpf_helpers.h>
3+
#include <bpf/bpf_core_read.h>
4+
#include <bpf/bpf_tracing.h>
5+
#include <sys/syscall.h>
6+
7+
char __license[] SEC("license") = "GPL";
8+
9+
// Defined according to /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/format
10+
struct openat_ctx {
11+
unsigned long pad;
12+
__u64 __unsused_syscall_header;
13+
int syscall_nr;
14+
int dfd;
15+
const char *filename;
16+
int flags;
17+
__u16 mode;
18+
};
19+
20+
// Defined according to /sys/kernel/debug/tracing/events/syscalls/sys_enter_unlinkat/format
21+
struct unlinkat_ctx {
22+
unsigned long pad;
23+
__u64 __unused_syscall_header;
24+
int syscall_nr;
25+
int dfd;
26+
const char *pathname;
27+
int flag;
28+
};
29+
30+
struct event {
31+
__u32 pid;
32+
char command[16];
33+
char filename[256];
34+
char op[10];
35+
};
36+
37+
struct {
38+
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
39+
__uint(key_size, sizeof(int));
40+
__uint(value_size, sizeof(int));
41+
__uint(max_entries, 1024);
42+
} events SEC(".maps");
43+
44+
SEC("tracepoint/syscalls/sys_enter_openat")
45+
int trace_openat(struct openat_ctx *ctx) {
46+
struct event event = {};
47+
48+
event.pid = bpf_get_current_pid_tgid() >> 32;
49+
bpf_get_current_comm(&event.command, sizeof(event.command));
50+
51+
const char* filename = ctx->filename;
52+
bpf_probe_read_user_str(event.filename, sizeof(event.filename), filename);
53+
54+
__builtin_memcpy(event.op, "OPEN", 5);
55+
56+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event,
57+
sizeof(event));
58+
return 0;
59+
}
60+
61+
SEC("tracepoint/syscalls/sys_enter_unlinkat")
62+
int trace_unlinkat(struct unlinkat_ctx *ctx) {
63+
struct event event = {};
64+
65+
event.pid = bpf_get_current_pid_tgid() >> 32;
66+
bpf_get_current_comm(&event.command, sizeof(event.command));
67+
68+
const char* pathname = ctx->pathname;
69+
bpf_probe_read_user_str(event.filename, sizeof(event.filename), pathname);
70+
71+
__builtin_memcpy(event.op, "DELETE", 7);
72+
73+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event,
74+
sizeof(event));
75+
return 0;
76+
}

go.mod

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/suchit07-git/filemon
2+
3+
go 1.24.1
4+
5+
require (
6+
github.com/cilium/ebpf v0.17.3 // indirect
7+
golang.org/x/sys v0.30.0 // indirect
8+
)

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/cilium/ebpf v0.17.3 h1:FnP4r16PWYSE4ux6zN+//jMcW4nMVRvuTLVTvCjyyjg=
2+
github.com/cilium/ebpf v0.17.3/go.mod h1:G5EDHij8yiLzaqn0WjyfJHvRa+3aDlReIaLVRMvOyJk=
3+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
4+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

main.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"errors"
7+
"fmt"
8+
"log"
9+
"os"
10+
"os/signal"
11+
"syscall"
12+
13+
"github.com/cilium/ebpf/link"
14+
"github.com/cilium/ebpf/perf"
15+
)
16+
17+
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf bpf/file_monitor.c
18+
19+
type Event struct {
20+
Pid uint32
21+
Command [16]byte
22+
Filename [256]byte
23+
Op [10]byte
24+
}
25+
26+
func main() {
27+
objs := bpfObjects{}
28+
if err := loadBpfObjects(&objs, nil); err != nil {
29+
log.Fatalf("loading objects: %v", err)
30+
}
31+
defer objs.Close()
32+
33+
openatTracepoint, err := link.Tracepoint("syscalls", "sys_enter_openat", objs.TraceOpenat, nil)
34+
if err != nil {
35+
log.Fatalf("opening tracepoint: %s", err)
36+
}
37+
defer openatTracepoint.Close()
38+
39+
unlinkatTracepoint, err := link.Tracepoint("syscalls", "sys_enter_unlinkat", objs.TraceUnlinkat, nil)
40+
if err != nil {
41+
log.Fatalf("opening tracepoint: %s", err)
42+
}
43+
defer unlinkatTracepoint.Close()
44+
45+
rd, err := perf.NewReader(objs.Events, os.Getpagesize())
46+
if err != nil {
47+
log.Fatalf("creating perf event reader: %s", err)
48+
}
49+
defer rd.Close()
50+
51+
sig := make(chan os.Signal, 1)
52+
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
53+
54+
fmt.Println("Monitoring file operartions...")
55+
56+
go func() {
57+
for {
58+
record, err := rd.Read()
59+
if err != nil {
60+
if errors.Is(err, perf.ErrClosed) {
61+
return
62+
}
63+
log.Printf("reading from perf event reader: %s", err)
64+
continue
65+
}
66+
67+
if record.LostSamples != 0 {
68+
log.Printf("lost %d samples", record.LostSamples)
69+
continue
70+
}
71+
72+
var event Event
73+
if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {
74+
log.Printf("parsing perf event: %s", err)
75+
continue
76+
}
77+
78+
command := string(bytes.TrimRight(event.Command[:], "\x00"))
79+
filename := string(bytes.TrimRight(event.Filename[:], "\x00"))
80+
operation := string(bytes.TrimRight(event.Op[:], "\x00"))
81+
82+
log.Printf("PID: %d, Process: %s, Operation: %s, File: %s\n", event.Pid, command, operation, filename)
83+
}
84+
}()
85+
86+
<-sig
87+
fmt.Println("\nExiting...")
88+
}

0 commit comments

Comments
 (0)