Skip to content

Commit 49dbbf4

Browse files
author
lipeng hao
committed
monitor execve command
1 parent f524124 commit 49dbbf4

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
# 进程执行命令监控示例
3+
4+
APPS = exec_monitor
5+
include ../Makefile.common
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2+
// 使用 eBPF 监控进程执行的命令
3+
// Hook sched_process_exec tracepoint 捕获 execve 系统调用
4+
5+
#include "vmlinux.h"
6+
#include <bpf/bpf_helpers.h>
7+
#include <bpf/bpf_tracing.h>
8+
#include <bpf/bpf_core_read.h>
9+
10+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
11+
12+
#define MAX_ARGS_SIZE 256
13+
#define MAX_FILENAME_SIZE 256
14+
#define MAX_COMM_SIZE 16
15+
16+
// 执行事件结构体
17+
struct exec_event {
18+
__u32 pid; // 进程 ID
19+
__u32 ppid; // 父进程 ID
20+
__u32 uid; // 用户 ID
21+
char comm[MAX_COMM_SIZE]; // 进程名
22+
char filename[MAX_FILENAME_SIZE]; // 执行的程序路径
23+
char args[MAX_ARGS_SIZE]; // 命令行参数
24+
};
25+
26+
// Ring Buffer:传递事件到用户空间
27+
struct {
28+
__uint(type, BPF_MAP_TYPE_RINGBUF);
29+
__uint(max_entries, 256 * 1024);
30+
} events SEC(".maps");
31+
32+
// 目标 PID 过滤(0 表示监控所有进程)
33+
struct {
34+
__uint(type, BPF_MAP_TYPE_ARRAY);
35+
__uint(max_entries, 1);
36+
__type(key, __u32);
37+
__type(value, __u32);
38+
} target_pid_map SEC(".maps");
39+
40+
// 检查当前进程是否是目标进程的后代(最多向上查找 10 层)
41+
static __always_inline bool is_descendant_of_target(__u32 target_pid)
42+
{
43+
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
44+
45+
// 检查自己是否是目标进程
46+
__u32 pid = bpf_get_current_pid_tgid() >> 32;
47+
if (pid == target_pid)
48+
return true;
49+
50+
// 向上遍历进程树
51+
#pragma unroll
52+
for (int i = 0; i < 10; i++) {
53+
__u32 ppid = BPF_CORE_READ(task, real_parent, tgid);
54+
55+
// 找到目标进程
56+
if (ppid == target_pid)
57+
return true;
58+
59+
// 到达 init 进程,停止遍历
60+
if (ppid == 0 || ppid == 1)
61+
return false;
62+
63+
// 继续向上遍历
64+
task = BPF_CORE_READ(task, real_parent);
65+
if (!task)
66+
return false;
67+
}
68+
return false;
69+
}
70+
71+
SEC("tp/sched/sched_process_exec")
72+
int trace_exec(struct trace_event_raw_sched_process_exec *ctx)
73+
{
74+
struct task_struct *task;
75+
struct exec_event *e;
76+
__u32 pid, ppid, uid;
77+
__u32 key = 0;
78+
79+
// 获取进程信息
80+
pid = bpf_get_current_pid_tgid() >> 32;
81+
uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
82+
task = (struct task_struct *)bpf_get_current_task();
83+
ppid = BPF_CORE_READ(task, real_parent, tgid);
84+
85+
// 检查目标 PID 过滤
86+
__u32 *target = bpf_map_lookup_elem(&target_pid_map, &key);
87+
if (target && *target != 0) {
88+
if (!is_descendant_of_target(*target))
89+
return 0;
90+
}
91+
92+
// 分配事件
93+
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
94+
if (!e)
95+
return 0;
96+
97+
// 填充基本事件数据
98+
e->pid = pid;
99+
e->ppid = ppid;
100+
e->uid = uid;
101+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
102+
103+
// 读取命令路径 - 使用 tracepoint 提供的 filename
104+
unsigned int fname_off = ctx->__data_loc_filename & 0xFFFF;
105+
bpf_probe_read_str(e->filename, sizeof(e->filename), (void *)ctx + fname_off);
106+
107+
// 读取完整命令行参数(从 mm->arg_start)
108+
struct mm_struct *mm = BPF_CORE_READ(task, mm);
109+
if (mm) {
110+
unsigned long arg_start = BPF_CORE_READ(mm, arg_start);
111+
unsigned long arg_end = BPF_CORE_READ(mm, arg_end);
112+
unsigned long arg_len = arg_end - arg_start;
113+
114+
// 限制到缓冲区大小
115+
if (arg_len > MAX_ARGS_SIZE - 1)
116+
arg_len = MAX_ARGS_SIZE - 1;
117+
118+
if (arg_len > 0) {
119+
__builtin_memset(e->args, 0, MAX_ARGS_SIZE);
120+
121+
// 读取整个参数区域
122+
long ret = bpf_probe_read_user(e->args, arg_len, (void *)arg_start);
123+
if (ret == 0) {
124+
// 将 null 字节替换为空格以提高可读性
125+
#pragma unroll
126+
for (int i = 0; i < MAX_ARGS_SIZE - 1; i++) {
127+
if (i >= arg_len - 1)
128+
break;
129+
if (e->args[i] == '\0')
130+
e->args[i] = ' ';
131+
}
132+
e->args[arg_len] = '\0';
133+
}
134+
}
135+
} else {
136+
e->args[0] = '\0';
137+
}
138+
139+
bpf_ringbuf_submit(e, 0);
140+
return 0;
141+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2+
// 进程执行命令监控 - 用户空间程序
3+
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <signal.h>
8+
#include <unistd.h>
9+
#include <time.h>
10+
#include <getopt.h>
11+
#include <bpf/libbpf.h>
12+
#include "exec_monitor.skel.h"
13+
14+
#define MAX_ARGS_SIZE 256
15+
#define MAX_FILENAME_SIZE 256
16+
#define MAX_COMM_SIZE 16
17+
18+
// 与内核程��一致的结构体定义
19+
struct exec_event {
20+
__u32 pid;
21+
__u32 ppid;
22+
__u32 uid;
23+
char comm[MAX_COMM_SIZE];
24+
char filename[MAX_FILENAME_SIZE];
25+
char args[MAX_ARGS_SIZE];
26+
};
27+
28+
static volatile sig_atomic_t exiting = 0;
29+
static int exec_count = 0;
30+
31+
static void sig_handler(int sig)
32+
{
33+
exiting = 1;
34+
}
35+
36+
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
37+
{
38+
if (level == LIBBPF_DEBUG)
39+
return 0;
40+
return vfprintf(stderr, format, args);
41+
}
42+
43+
// 获取当前时间戳
44+
static void get_timestamp(char *buf, size_t len)
45+
{
46+
time_t now = time(NULL);
47+
struct tm *tm_info = localtime(&now);
48+
strftime(buf, len, "%H:%M:%S", tm_info);
49+
}
50+
51+
// 处理执行事件
52+
static int handle_event(void *ctx, void *data, size_t data_sz)
53+
{
54+
const struct exec_event *e = data;
55+
char timestamp[32];
56+
const char *display_cmd;
57+
58+
get_timestamp(timestamp, sizeof(timestamp));
59+
exec_count++;
60+
61+
// 检查是否是 sh -c 或 bash -c 命令,提取实际命令
62+
if (strstr(e->filename, "/sh") || strstr(e->filename, "/bash")) {
63+
const char *cmd_start = strstr(e->args, "-c ");
64+
if (cmd_start) {
65+
cmd_start += 3; // 跳过 "-c "
66+
display_cmd = cmd_start;
67+
} else {
68+
display_cmd = e->args;
69+
}
70+
} else {
71+
display_cmd = e->args;
72+
}
73+
74+
printf("[%s] PID:%-6u PPID:%-6u UID:%-5u | %s\n",
75+
timestamp, e->pid, e->ppid, e->uid, display_cmd);
76+
77+
return 0;
78+
}
79+
80+
static void usage(const char *prog)
81+
{
82+
printf("Usage: %s [OPTIONS]\n", prog);
83+
printf("\n");
84+
printf("监控进程执行的命令 (execve)\n");
85+
printf("\n");
86+
printf("Options:\n");
87+
printf(" -p PID 只监控指定 PID 及其子进程\n");
88+
printf(" -h 显示帮助信息\n");
89+
printf("\n");
90+
printf("Examples:\n");
91+
printf(" %s # 监控所有进程\n", prog);
92+
printf(" %s -p 1234 # 只监控 PID 1234 及其子进程\n", prog);
93+
}
94+
95+
int main(int argc, char **argv)
96+
{
97+
struct exec_monitor_bpf *skel;
98+
struct ring_buffer *rb = NULL;
99+
int err;
100+
__u32 target_pid = 0;
101+
int opt;
102+
103+
// 解析命令行参数
104+
while ((opt = getopt(argc, argv, "p:h")) != -1) {
105+
switch (opt) {
106+
case 'p':
107+
target_pid = atoi(optarg);
108+
break;
109+
case 'h':
110+
default:
111+
usage(argv[0]);
112+
return opt == 'h' ? 0 : 1;
113+
}
114+
}
115+
116+
signal(SIGINT, sig_handler);
117+
signal(SIGTERM, sig_handler);
118+
libbpf_set_print(libbpf_print_fn);
119+
120+
// 打开并加载 BPF 程序
121+
skel = exec_monitor_bpf__open_and_load();
122+
if (!skel) {
123+
fprintf(stderr, "加载 BPF 程序失败\n");
124+
return 1;
125+
}
126+
127+
// 设置目标 PID 过滤
128+
if (target_pid > 0) {
129+
__u32 key = 0;
130+
err = bpf_map__update_elem(skel->maps.target_pid_map,
131+
&key, sizeof(key),
132+
&target_pid, sizeof(target_pid), 0);
133+
if (err) {
134+
fprintf(stderr, "设置目标 PID 失败: %d\n", err);
135+
goto cleanup;
136+
}
137+
}
138+
139+
// 附加 BPF 程序
140+
err = exec_monitor_bpf__attach(skel);
141+
if (err) {
142+
fprintf(stderr, "附加 BPF 程序失败: %d\n", err);
143+
goto cleanup;
144+
}
145+
146+
// 创建 Ring Buffer
147+
rb = ring_buffer__new(bpf_map__fd(skel->maps.events), handle_event, NULL, NULL);
148+
if (!rb) {
149+
fprintf(stderr, "创建 ring buffer 失败\n");
150+
err = -1;
151+
goto cleanup;
152+
}
153+
154+
printf("========================================\n");
155+
printf("进程执行命令监控 (sched_process_exec)\n");
156+
printf("========================================\n");
157+
if (target_pid > 0) {
158+
printf("目标 PID: %u (包含子进程)\n", target_pid);
159+
} else {
160+
printf("目标 PID: 所有进程\n");
161+
}
162+
printf("========================================\n");
163+
printf("监控中... (Ctrl+C 退出)\n\n");
164+
165+
// 主循环
166+
while (!exiting) {
167+
err = ring_buffer__poll(rb, 100);
168+
if (err < 0 && err != -EINTR) {
169+
fprintf(stderr, "轮询错误: %d\n", err);
170+
break;
171+
}
172+
}
173+
174+
printf("\n========================================\n");
175+
printf("监控结束,共捕获 %d 条命令\n", exec_count);
176+
printf("========================================\n");
177+
178+
cleanup:
179+
ring_buffer__free(rb);
180+
exec_monitor_bpf__destroy(skel);
181+
return err < 0 ? -err : 0;
182+
}

0 commit comments

Comments
 (0)