[2023]基于ebpf的进程监控实现python + CPP两个例子 huoji EDR,liunx,ebpf 2023-05-09 875 次浏览 0 次点赞 python写的demo,用ebpf监控进程创建和退出.内核做不太行,因为内核做目前新Ubuntu不导出excute、fork的东西了(草) 我写了两个demo,一个是python的用于体验ebpf,另外一个是C++的,用于我的"安全鸭"项目 ebpf唯一的问题是栈太小了,导致某些信息需要异步查询,比如进程路径和父进程路径,不知道有没有大哥给出点好的方案 这是python的简单实现: ```cpp from bcc import BPF from time import sleep # eBPF program to trace process creation and exit program = """ #include #include struct event_data_t { int type; u32 pid; u32 ppid; } __attribute__((packed)); BPF_PERF_OUTPUT(events); int trace_process_start(struct pt_regs *ctx, struct filename *filename) { struct event_data_t event_data; u32 pid = bpf_get_current_pid_tgid(); u32 ppid = bpf_get_current_pid_tgid() >> 32; event_data.type = 0; event_data.pid = pid; event_data.ppid = ppid; events.perf_submit(ctx, &event_data, sizeof(event_data)); return 0; } int trace_process_exit(struct pt_regs *ctx) { struct event_data_t event_data; u32 pid = bpf_get_current_pid_tgid(); u32 ppid = bpf_get_current_pid_tgid() >> 32; event_data.type = 1; event_data.pid = pid; event_data.ppid = ppid; events.perf_submit(ctx, &event_data, sizeof(event_data)); return 0; } """ # initialize BPF and attach tracepoints b = BPF(text=program) b.attach_kprobe(event=b.get_syscall_fnname("execve"), fn_name="trace_process_start") b.attach_kprobe(event=b.get_syscall_fnname("exit_group"), fn_name="trace_process_exit") import psutil def get_process_path(pid): try: proc = psutil.Process(pid) return proc.exe() except (psutil.NoSuchProcess, psutil.AccessDenied): return "unknown" # process event data def print_event(cpu, data, size): event = b["events"].event(data) path = get_process_path(event.pid) print(f"type: {event.type} pid: {event.pid}, ppid: {event.ppid} path: {path}") b["events"].open_perf_buffer(print_event) while True: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() ``` 这个是C++的实现: ```cpp #include #include #include #include #include #include #include #include const std::string EBPF_SOURCE_PATH = "core.ebpf.c"; volatile bool interrupted = false; void signal_handler(int signal) { interrupted = true; } std::pair get_process_path(int pid) { try { char path[4096] = {0}; std::string symlink_path = "/proc/" + std::to_string(pid) + "/exe"; ssize_t len = readlink(symlink_path.c_str(), path, sizeof(path)); if (len != -1) { return {true, std::string(path)}; } } catch (...) { } return {false, "unknown"}; } std::string get_process_name(pid_t pid) { std::string process_name; std::ifstream comm_file("/proc/" + std::to_string(pid) + "/comm"); if (comm_file.is_open()) { std::getline(comm_file, process_name); comm_file.close(); } else { process_name = "unknown"; } return process_name; } std::string get_process_cmdline(pid_t pid) { std::string cmdline; std::ifstream cmdline_file("/proc/" + std::to_string(pid) + "/cmdline"); if (cmdline_file.is_open()) { std::getline(cmdline_file, cmdline, '\0'); cmdline_file.close(); } else { cmdline = "unknown"; } return cmdline; } void print_event(void *context, void *data, int data_size) { struct event_data_t { int type; uint32_t pid; uint32_t ppid; }; auto event = static_cast(data); auto [process_exists, path] = get_process_path(event->pid); if (event->type == 0 && process_exists == false) { // 进程启动如果路径为空说明取不到了 // 进程结束路径一定是取不到的 return; } auto process_name = get_process_name(event->pid); auto cmdline = get_process_cmdline(event->pid); auto event_type = event->type == 0 ? "start" : "exit"; printf("type: %s pid: %d ppid: %d path: %s name: %s cmdline: %s\n", event_type, event->pid, event->ppid, path.c_str(), process_name.c_str(), cmdline.c_str()); } int main() { ebpf::BPF bpf; std::ifstream ebpf_file(EBPF_SOURCE_PATH, std::ios::binary); // 检查文件是否存在 if (ebpf_file.fail()) { std::cerr << "Failed to open " << EBPF_SOURCE_PATH << std::endl; return 1; } std::vector ebpf_program((std::istreambuf_iterator(ebpf_file)), std::istreambuf_iterator()); auto init_res = bpf.init(std::string(ebpf_program.data(), ebpf_program.size())); if (init_res.code() != 0) { std::cerr << "Failed to initialize BPF program: " << init_res.msg() << std::endl; return 1; } std::string execve_fnname = bpf.get_syscall_fnname("execve"); std::string exit_group_fnname = bpf.get_syscall_fnname("exit_group"); auto attach_res1 = bpf.attach_kprobe(execve_fnname, "trace_process_start"); auto attach_res2 = bpf.attach_kprobe(exit_group_fnname, "trace_process_exit"); if (attach_res1.code() != 0 || attach_res2.code() != 0) { std::cerr << "Failed to attach kprobes: " << attach_res1.msg() << " " << attach_res2.msg() << std::endl; return 1; } signal(SIGINT, signal_handler); auto ret = bpf.open_perf_buffer("events", print_event, nullptr, nullptr, 256); if (ret.code() != 0) { fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str()); return EXIT_FAILURE; } auto table = bpf.get_table("events"); auto perf_buffer = bpf.get_perf_buffer("events"); while (!interrupted) { perf_buffer->poll(1000); } return 0; } ``` 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
还不快抢沙发