bpf读取长字符串拼接问题
背景
在监控SEC(“tracepoint/syscalls/sys_enter_execve”)时,我们需要获取用户输入的整条命令,例如:
docker run -it -v /your_path:/Agith/build/output --privileged --pid=host --workdir=/Agith/build/prod agith -p XXXX
命令会被分隔保存在sys_enter_execve_args
中的argv
数组中,我们需要完成字符串的拼接并传递到用户态。
以下是sys_enter_execve_args
结构体的定义:
struct sys_enter_execve_args {
unsigned long long unused;
long syscall_nr;
const char* filename;
const char* const* argv;
const char* const* envp;
};
在平时的代码中,这项工作非常容易,但是eBPF有非常严格的审查机制,稍不注意就会加载失败。
由于bpf的map不能直接声明长数组,我们需要先封装数组到结构体,再定义一个map:
struct big_string_t {
char inner_str[MAX_ARG_LENGTH];
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, unsigned int);
__type(value, struct big_string_t);
__uint(max_entries, CPU_NUM * ENTRY_NUM_PER_CPU);
} arg_strings_map SEC(".maps");
在探针程序中实现的过程如下:
argv_map_value = bpf_map_lookup_elem(&arg_strings_map, &trace_ptr);
if (argv_map_value == NULL) return 0;
for (int i = 0; i < MAX_ARG; i++) {
ret = bpf_probe_read(&argv, sizeof(argv), &ctx->argv[i]);
if (argv == 0 || ret < 0) {
break;
}
if (offset >= 0 && offset + STR_BUF_SIZE < MAX_ARG_LENGTH) {
ret = bpf_probe_read_str(&argv_map_value[offset], STR_BUF_SIZE, argv);
offset += ret;
if (offset - 1 > 0 && offset - 1 < MAX_ARG_LENGTH) {
argv_map_value[offset - 1] = ' ';
}
}
}
用户态读取:
struct big_string_t value;
bpf_map_lookup_elem(m_arg_strings_map_fd, index, &value);