基于eBPF技术的Rootkit检测与系统内核取证实战指南
1. 项目概述当系统安全遭遇“隐形刺客”在系统安全领域Rootkit一直是最具威胁性的存在之一。它不像勒索软件那样大张旗鼓地加密文件也不像挖矿木马那样疯狂消耗资源。Rootkit更像一个技艺高超的“隐形刺客”一旦潜入系统内核便能篡改系统调用、隐藏恶意进程和文件、劫持网络连接将自己从操作系统的视野中彻底抹去。传统的基于签名或行为分析的杀毒软件面对这种深度内核挂钩的对手常常力不从心因为攻击者已经控制了“裁判”本身。那么当怀疑系统已被Rootkit感染我们该如何进行有效的取证分析揪出这个“隐形刺客”呢这正是“利用eBPF检测Rootkit项目取证”要解决的核心问题。eBPF扩展伯克利包过滤器技术近年来从网络领域“出圈”成为了系统可观测性和安全领域的明星。它允许我们以安全、高效的方式在内核中运行自定义的沙盒程序无需修改内核源码或加载内核模块。这为我们提供了一个从“上帝视角”观察内核行为的绝佳工具——即使Rootkit试图隐藏自己只要它还在活动就必然会在内核中留下痕迹而eBPF正是捕捉这些痕迹的“显微镜”。这个项目适合所有关心系统底层安全的安全工程师、SRE站点可靠性工程师以及对Linux内核机制有浓厚兴趣的开发者。无论你是想构建主动防御体系还是想在安全事件发生后进行深度根因分析掌握eBPF取证技能都将让你拥有透视系统内部的能力。接下来我将从一个实战者的角度拆解如何构建一套基于eBPF的Rootkit检测与取证方案。2. 核心思路为什么eBPF是Rootkit取证的理想选择在深入实操之前我们必须先理解为什么eBPF在这个场景下具有不可替代的优势。传统的Rootkit检测工具如chkrootkit、rkhunter主要采用基于特征码的扫描和文件完整性校验。而更高级的内核模块检测工具如lsmod配合unhide等其原理也依赖于内核自身提供的、未被篡改的接口来列举信息。一旦Rootkit通过sys_call_table挂钩或直接修改内核数据结构如进程链表task_struct这些工具看到的就是一幅被精心伪造过的“假象”。eBPF的突破性在于其执行位置和安全性。它的程序由内核中的eBPF虚拟机执行其验证器确保了程序不会导致内核崩溃或陷入死循环。这意味着我们可以将探针Probe附着到几乎任何内核函数或事件上例如系统调用入口/出口监控open、execve、kill、connect等关键调用对比用户空间传入的参数和内核实际处理的对象。内核跟踪点Tracepoints这是内核静态定义的钩子点如sched_process_exec进程执行、sched_process_exit进程退出。内核函数探针kprobes可以动态附着到绝大多数内核函数的入口kprobe和出口kretprobe灵活性极高。网络数据包处理XDP, TC用于检测Rootkit进行的网络嗅探或连接隐藏。取证的核心思路对比传统思路询问系统“请列出所有进程”。Rootkit可以篡改答案eBPF思路直接在内核中“蹲守”进程创建和调度的事件源头记录下每一个事件然后问自己“我记录到的进程和系统现在告诉我的对得上吗”。这种“绕过被污染接口直击事件源头”的能力使得eBPF取证具有极高的可靠性和对抗性。我们的项目将围绕这一核心思路展开部署eBPF探针收集原始事件日志与系统状态进行交叉验证从而发现异常。2.1 方案选型与工具链在具体实施前需要选择合适的工具链。目前主流的eBPF开发有以下几种方式直接使用BCCBPF Compiler Collection这是最快速的上手方式。BCC提供了大量现成的工具如execsnoop、opensnoop和Python前端可以快速编写和部署eBPF程序。优点是开发效率高适合快速原型验证和交互式分析。缺点是运行时依赖Python和LLVM/Clang进行即时编译在生产环境部署稍显笨重且性能开销相对较高。使用libbpf C语言这是当前生产环境推荐的模式。libbpf是内核内置的库支持CO-RECompile Once – Run Everywhere技术。我们先用C语言编写eBPF程序预编译成.o文件然后用户空间程序可以用C、Go、Rust通过libbpf库加载并与之交互。优点是部署简单只有一个二进制文件、性能优异、内存占用小且兼容性更好。缺点是开发门槛稍高需要处理更多的底层细节。基于eBPF的专用安全项目如Tracee、Falco它们已经封装了丰富的安全检测规则包括部分Rootkit检测能力。我们可以基于它们进行二次开发或规则扩展。对于取证项目我的建议是采用“libbpf C/Go”的方案。原因如下独立性编译出的二进制工具可以拷贝到疑似受害机器上直接运行无需安装复杂的编译环境这对取证现场至关重要。低干扰自身开销小对已经可能不稳定的受害系统影响更小。灵活性可以针对特定的Rootkit行为模式编写高度定制化的检测逻辑。本项目将主要基于libbpf范式进行讲解但核心eBPF程序代码逻辑是相通的。注意eBPF需要较新的内核支持通常需要Linux 4.4以上且功能越完整越好建议5.4。在取证时首先要确认受害系统的内核版本。如果内核过于老旧可能需要考虑备用方案。3. 取证探针设计与关键eBPF程序解析一个完整的Rootkit取证方案需要从多个维度部署探针形成交叉验证的网络。下面我们设计几个核心的eBPF程序模块。3.1 模块一进程生命周期监控与隐藏进程发现这是最基础的检测。Rootkit通常会隐藏恶意进程。我们的策略是在内核记录所有进程创建和退出事件然后在用户空间对比/proc文件系统或ps命令的输出。eBPF程序核心逻辑附着到tracepoint/sched/sched_process_exec和tracepoint/sched/sched_process_exit// 定义一个BPF哈希映射Map用于存储活跃进程 struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, pid_t); // 键进程PID __type(value, struct event); // 值进程信息 } active_procs SEC(.maps); // 定义事件结构体通过另一个Map或perf事件缓冲区发送到用户空间 struct event { pid_t pid; pid_t ppid; char comm[TASK_COMM_LEN]; // 进程名 unsigned long long timestamp; int is_exit; // 0 for exec, 1 for exit }; SEC(tracepoint/sched/sched_process_exec) int tracepoint__sched__sched_process_exec(struct trace_event_raw_sched_process_exec *ctx) { struct event e {}; pid_t pid bpf_get_current_pid_tgid() 32; // 获取当前进程PID e.pid pid; e.ppid (pid_t)ctx-parent_pid; e.timestamp bpf_ktime_get_ns(); e.is_exit 0; bpf_get_current_comm(e.comm, sizeof(e.comm)); // 将进程信息存入活跃进程Map bpf_map_update_elem(active_procs, pid, e, BPF_ANY); // 同时也可以发送事件到用户空间供实时分析 // perf_submit(ctx, e, sizeof(e)); return 0; } SEC(tracepoint/sched/sched_process_exit) int tracepoint__sched__sched_process_exit(struct trace_event_raw_sched_process_exit *ctx) { pid_t pid bpf_get_current_pid_tgid() 32; // 从活跃进程Map中删除 bpf_map_delete_elem(active_procs, pid); struct event e {}; e.pid pid; e.timestamp bpf_ktime_get_ns(); e.is_exit 1; bpf_get_current_comm(e.comm, sizeof(e.comm)); // 发送退出事件 // perf_submit(ctx, e, sizeof(e)); return 0; }用户空间取证逻辑加载上述eBPF程序开始收集事件。运行一段时间后读取active_procs这个BPF Map里面存储着eBPF视角下所有“正在运行”的进程。同时通过常规方式如遍历/proc/[pid]目录获取系统当前显示的进程列表。进行对比分析情况A进程隐藏eBPF Map中存在的某个PID在/proc中找不到对应目录。这强烈暗示该进程被Rootkit隐藏了。情况B进程伪装/proc中某个进程的comm命令名或/proc/[pid]/exe可执行文件路径与eBPF在exec事件中记录的信息不符。这可能意味着进程执行后其内存中的名称或被/proc返回的信息被篡改了。实操心得tracepoint比kprobe更稳定因为它是内核的稳定API。对于取证工具稳定性优先因此首选tracepoint。此外Map的大小max_entries需要根据系统规模设置取证时建议设置得大一些避免事件丢失。3.2 模块二系统调用挂钩Syscall Hook检测这是Rootkit的经典技术。通过修改sys_call_table将原本的open、getdents读取目录等系统调用指向恶意函数从而过滤掉恶意文件或进程的信息。检测原理我们可以利用kprobe检查关键系统调用处理函数的代码页属性。正常情况下内核代码段是只读的RX。如果Rootkit为了挂钩而临时修改了页表属性、写入跳转指令后再改回可能会留下痕迹。但更可靠的是一种“行为差异”检测法。eBPF程序核心逻辑以检测getdents钩子为例隐藏文件在用户空间创建一个已知的测试文件比如/tmp/.hidden_test。eBPF程序挂钩sys_getdents和sys_getdents64的入口和出口使用kprobe和kretprobe。当用户空间程序调用ls或readdir遍历/tmp时eBPF记录下该系统调用的参数文件描述符和返回值。在eBPF的kretprobe出口探针中我们可以检查返回的目录条目缓冲区。但更简单的方法是在用户空间对比“通过syscall直接调用getdents得到的结果”和“通过libc的readdir得到的结果”。如果存在差异则表明在libc到内核之间的某个层次很可能是系统调用本身存在过滤。由于在内核中直接解析目录缓冲区比较复杂一个更实用的取证方法是eBPF程序监控open和openat系统调用寻找“看不见的访问”SEC(kprobe/do_sys_openat2) int kprobe__do_sys_openat2(struct pt_regs *ctx) { char filename[256]; bpf_probe_read_user_str(filename, sizeof(filename), (void *)PT_REGS_PARM2(ctx)); // 读取文件名参数 // 如果某个进程尝试打开一个我们已知存在、但常规ls看不到的文件这就是可疑行为 // 我们可以将已知的测试文件名硬编码或通过Map传递进来 if (bpf_strncmp(filename, sizeof(/tmp/.hidden_test), /tmp/.hidden_test) 0) { pid_t pid bpf_get_current_pid_tgid() 32; bpf_printk(PID %d is accessing hidden file: %s\\n, pid, filename); // 记录到取证Map中 } return 0; }取证操作在/tmp目录下创建文件.hidden_test。运行常规ls -la /tmp确认看不到该文件假设Rootkit隐藏了它。运行我们的eBPF取证工具。在另一个终端尝试cat /tmp/.hidden_test。如果eBPF工具捕获到了这次open系统调用而ls却看不到文件那么就证实了getdents系统调用被挂钩过滤。3.3 模块三网络连接隐身检测Rootkit如Diamorphine可能会隐藏网络连接。检测思路与进程隐藏类似。eBPF程序核心逻辑附着到tracepoint/syscalls/sys_enter_connect和tracepoint/syscalls/sys_exit_connect在sys_enter_connect时记录进程PID、目标IP和端口到一个Map中。在sys_exit_connect时如果连接成功返回值0则将这条连接信息记录到另一个“活跃连接Map”中。同时我们可以挂钩tcp_v4_connect等内核函数来更早地捕获连接意图。用户空间取证逻辑从eBPF的“活跃连接Map”中获取所有eBPF观测到的连接。使用netstat -tunap或ss -tunap命令获取系统报告的网络连接。对比两者。任何eBPF能看到而netstat看不到的连接就是被隐藏的连接。进一步可以关联进程ID找出是哪个“隐藏进程”建立了这个“隐藏连接”。3.4 模块四内核模块列表完整性校验Rootkit可能是内核模块LKM。虽然lsmod命令可能被篡改但我们可以通过读取/sys/module/目录或直接解析内核符号表来获取信息。eBPF程序本身不直接用于列举模块因为lsmod的数据源struct module链表可能已被篡改。eBPF在这里的辅助作用是监控模块加载/卸载事件挂钩sys_init_module和sys_delete_module系统调用记录所有模块加载和卸载行为无论其是否试图隐藏。这能提供一个独立的加载历史日志。检测可疑的内核内存修改可以尝试使用kprobe附着到一些关键的内核函数如module_alloc但这对eBPF程序稳定性要求极高在取证环境中需谨慎使用。一个更直接的用户空间取证方法是使用cat /proc/kallsyms | grep module_来查看内核符号并与lsmod输出进行对比寻找不匹配的模块名。eBPF收集的加载历史日志可以作为时间线分析的佐证。4. 项目实战构建一体化取证工具链理论说完我们来动手组装一个具备基本功能的取证工具。我们将使用libbpf-bootstrap框架来快速搭建一个C语言项目它集成了现代libbpf开发的最佳实践。4.1 环境准备与项目初始化系统要求Linux内核 5.4 已安装clang,llvm,libelf,zlib等开发工具。内核头文件通常位于/lib/modules/$(uname -r)/build。# 1. 获取 libbpf-bootstrap 脚手架 git clone https://github.com/libbpf/libbpf-bootstrap.git cd libbpf-bootstrap git submodule update --init # 2. 创建我们的取证项目目录 cd examples/c cp -r minimal/ rootkit-forensic/ cd rootkit-forensic mv minimal.bpf.c rootkit-forensic.bpf.c mv minimal.c rootkit-forensic.c4.2 编写eBPF内核态程序 (rootkit-forensic.bpf.c)我们将整合前文提到的进程监控和文件访问监控。// rootkit-forensic.bpf.c #include vmlinux.h #include bpf/bpf_helpers.h #include bpf/bpf_tracing.h #include bpf/bpf_core_read.h char LICENSE[] SEC(license) Dual BSD/GPL; // 定义存储进程执行事件的结构体 struct proc_event { __u64 timestamp; __u32 pid; __u32 ppid; __u32 uid; __u32 gid; char comm[TASK_COMM_LEN]; char filename[256]; // 对于exec事件记录可执行文件路径 }; // 定义存储文件访问事件的结构体针对隐藏文件测试 struct file_access_event { __u64 timestamp; __u32 pid; char comm[TASK_COMM_LEN]; char filename[256]; }; // 定义Perf Event缓冲区用于向用户空间发送事件 struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(__u32)); __uint(value_size, sizeof(__u32)); } proc_events SEC(.maps); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(__u32)); __uint(value_size, sizeof(__u32)); } file_access_events SEC(.maps); // 一个哈希Map用于临时存储正在执行的进程信息键为PID值为开始时间 struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 8192); __type(key, __u32); __type(value, __u64); } proc_start_ts SEC(.maps); // 挂钩进程执行 SEC(tp/sched/sched_process_exec) int handle_sched_process_exec(struct trace_event_raw_sched_process_exec *ctx) { struct proc_event e {}; __u64 id bpf_get_current_pid_tgid(); __u32 pid id 32; __u32 tid (__u32)id; e.timestamp bpf_ktime_get_ns(); e.pid pid; e.ppid BPF_CORE_READ(ctx, parent_pid); e.uid bpf_get_current_uid_gid(); e.gid bpf_get_current_uid_gid() 32; bpf_get_current_comm(e.comm, sizeof(e.comm)); // 尝试读取可执行文件名从第一个参数argv[0]获取这里简化处理实际更复杂 // 注意在生产环境中获取完整路径需要更复杂的逻辑可能需挂钩do_execveat_common // 这里仅作示例我们主要关注进程创建事件本身 bpf_probe_read_user_str(e.filename, sizeof(e.filename), (void *)ctx-filename); // 发送事件到用户空间 bpf_perf_event_output(ctx, proc_events, BPF_F_CURRENT_CPU, e, sizeof(e)); // 记录进程开始时间用于后续可能的统计如进程存活时间 __u64 start_ts bpf_ktime_get_ns(); bpf_map_update_elem(proc_start_ts, pid, start_ts, BPF_ANY); return 0; } // 挂钩进程退出 (可选用于清理Map) SEC(tp/sched/sched_process_exit) int handle_sched_process_exit(struct trace_event_raw_sched_process_exit *ctx) { __u32 pid bpf_get_current_pid_tgid() 32; bpf_map_delete_elem(proc_start_ts, pid); return 0; } // 挂钩打开文件检测对特定隐藏文件的访问 SEC(kprobe/do_sys_openat2) int handle_do_sys_open(struct pt_regs *ctx) { // 获取文件名参数在do_sys_openat2中第二个参数是const char __user *filename // 注意参数位置随内核版本和架构可能变化此处为示例需适配。 char filename[256]; const char __user *user_filename (const char __user *)PT_REGS_PARM2(ctx); if (bpf_probe_read_user_str(filename, sizeof(filename), user_filename) 0) { // 检查是否访问了我们预设的“诱饵”隐藏文件 // 在实际取证中这个文件名可以通过用户空间程序动态配置到另一个BPF Map中 if (bpf_strncmp(filename, sizeof(/tmp/.forensic_bait), /tmp/.forensic_bait) 0) { struct file_access_event e {}; e.timestamp bpf_ktime_get_ns(); e.pid bpf_get_current_pid_tgid() 32; bpf_get_current_comm(e.comm, sizeof(e.comm)); bpf_probe_read_user_str(e.filename, sizeof(e.filename), user_filename); bpf_perf_event_output(ctx, file_access_events, BPF_F_CURRENT_CPU, e, sizeof(e)); } } return 0; }4.3 编写用户空间加载与控制程序 (rootkit-forensic.c)用户空间程序负责加载eBPF程序、设置诱饵文件、接收事件并进行分析。// rootkit-forensic.c (部分关键代码基于libbpf-bootstrap框架修改) #include stdio.h #include unistd.h #include sys/resource.h #include bpf/libbpf.h #include signal.h #include rootkit-forensic.skel.h #include time.h // 定义从内核态传递过来的事件结构体需与.bpf.c中定义一致 // ... static volatile bool exiting false; static void sig_handler(int sig) { exiting true; } int main(int argc, char **argv) { struct rootkit_forensic_bpf *skel; int err; // 1. 设置诱饵文件 system(touch /tmp/.forensic_bait); system(chmod 600 /tmp/.forensic_bait); printf([*] 诱饵文件 /tmp/.forensic_bait 已设置。\\n); // 2. 打开并加载eBPF程序 libbpf_set_strict_mode(LIBBPF_STRICT_ALL); skel rootkit_forensic_bpf__open_and_load(); // ... 错误检查 // 3. 附加eBPF程序到跟踪点/kprobe err rootkit_forensic_bpf__attach(skel); // ... 错误检查 printf([*] eBPF取证工具已启动。开始监控进程执行和隐藏文件访问...\\n); printf([*] 按 CtrlC 停止。\\n); signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); // 4. 从Perf Event缓冲区中循环读取事件 while (!exiting) { // 这里需要设置perf buffer回调函数并调用perf_buffer__poll // 为了简化示例我们使用一个简化的事件处理循环 // 实际应使用libbpf的perf_buffer__poll机制 sleep(1); // 模拟定期对比eBPF记录的进程和/proc中的进程 printf([*] 执行定期检查... (在实际工具中这里会进行对比分析)\\n); } // 5. 清理工作 printf(\\n[*] 停止取证工具...\\n); rootkit_forensic_bpf__destroy(skel); system(rm -f /tmp/.forensic_bait); printf([*] 清理完成。\\n); return 0; }完整的用户空间程序还需要实现Perf Buffer回调函数用于实时处理从内核发送过来的proc_event和file_access_event。对比分析逻辑定期例如每10秒执行一个函数该函数读取当前/proc下的所有PID。从eBPF程序的proc_start_tsMap中读取所有eBPF认为存活的PID。进行对比输出差异报告。参数化配置允许用户通过命令行参数指定要监控的诱饵文件路径、检查间隔等。4.4 编译与运行# 在项目目录下 make sudo ./rootkit-forensic运行后工具会开始监控。你可以在另一个终端尝试访问诱饵文件cat /tmp/.forensic_bait同时用ls -la /tmp查看是否能看到它。如果工具捕获到了访问事件而ls看不到文件这就是一个明确的异常信号。5. 高级对抗与深度取证技巧基础的隐藏检测可能被更高级的Rootkit绕过。例如Rootkit可能直接操作eBPF映射或拦截eBPF辅助函数。这就需要更深入的技巧。5.1 检测eBPF自身是否被干扰枚举系统eBPF程序使用bpftool prog list和bpftool map list查看系统中所有加载的eBPF程序和映射。检查是否有未知或可疑的程序。Rootkit可能会加载自己的eBPF程序来干扰你的检测。校验关键数据你的取证工具可以定期读取自己的eBPF Map计算哈希值与预期值对比防止Map内容被恶意修改。5.2 利用不可篡改的硬件事件PMU性能监控单元事件eBPF可以挂钩到CPU的PMU硬件事件上如perf_event_open。这些事件由硬件产生内核层面的Rootkit难以伪造或抑制。可以监控如cpu-cycles、instructions等如果发现某个“不存在”的PID消耗了大量CPU资源就是异常。5.3 内存取证结合eBPF是运行时动态分析而内存取证是静态分析。两者结合威力更大。使用eBPF工具快速定位可疑点如隐藏的PID、端口。获取这些可疑PID后立即使用LiME、AVML等工具转储系统内存。在内存镜像中直接解析内核数据结构如task_struct链表、tcp_sock队列寻找与eBPF发现相符的、但被隐藏的实体。这能提供法庭级的证据。5.4 行为图谱与时间线分析不要只关注单点异常。将eBPF收集到的所有事件进程创建、文件访问、网络连接按照时间戳排序构建系统行为图谱。关联分析隐藏进程A创建后是否立即访问了敏感文件B并向外发起连接C时间线在攻击发生的时间点附近eBPF日志显示了哪些异常事件序列这有助于还原攻击链。6. 常见问题、避坑指南与效能考量在实际部署和运行eBPF取证工具时你会遇到各种问题。以下是我从实践中总结的一些要点。6.1 性能与开销eBPF虽高效但不当使用仍会影响系统。事件频率监控sys_enter_openat这样高频的系统调用会对性能产生显著影响可能达到5%-10%或更高。在取证时应精准挂钩只监控最可疑的调用如execve,init_module,connect或使用过滤条件。Map操作bpf_map_update_elem和bpf_map_lookup_elem是相对昂贵的操作。避免在每次事件中都对大型Map进行全量扫描或复杂更新。缓冲区大小Perf Event Ring Buffer的大小需要设置合理。太小会导致事件丢失太大会浪费内存。根据事件速率调整。实操心得在生产环境或紧张的取证环境中可以先运行一个“轻量级侦察”程序只监控进程创建(exec)和模块加载开销通常小于1%。发现可疑线索后再动态加载一个针对性更强的、监控特定PID或文件路径的eBPF程序进行深度追踪。6.2 兼容性与可移植性内核版本差异不同内核版本中函数名、参数位置、数据结构可能变化。使用BPF_CORE_READ宏和CO-RE技术是解决此问题的关键。在编译时使用-D__KERNEL__和正确的内核头文件路径。架构差异x86_64和ARM64的参数传递规则ABI不同。PT_REGS_PARMx宏在libbpf中会处理这些差异但自己写裸kprobe时需要留意。6.3 典型错误与排查验证器错误这是最常见的问题。错误信息通常很晦涩。“invalid stack off”通常是因为访问了超出范围的栈变量。确保数组访问有边界检查bpf_probe_read_*。“R0 invalid mem access”解引用了一个可能为空的指针。在访问用户空间或内核空间指针前必须用bpf_probe_read_*系列函数。“back-edge from insn”程序可能包含循环而验证器无法证明其有界。eBPF程序中的循环必须使用#pragma unroll展开或者使用尾调用tail call来实现有界循环。事件丢失Perf Buffer满了。增加Ring Buffer大小perf_buffer__new的参数或提高用户空间程序的消费速度。无任何输出检查sudo权限是否足够。检查内核是否支持所需的tracepoint或kprobesudo cat /sys/kernel/debug/tracing/available_events。检查eBPF程序是否成功加载和附着sudo bpftool prog list。在eBPF程序中多用bpf_printk输出调试信息用sudo cat /sys/kernel/debug/tracing/trace_pipe查看。6.4 取证现场操作流程建议准备阶段将编译好的静态链接的取证工具二进制文件存放在干净的U盘或通过网络安全传输到目标机器。避免在受害机器上编译。信息收集首先运行uname -r,cat /proc/cmdline等命令记录系统基本信息。然后运行./rootkit-forensic --light启动轻量级监控。初步分析让工具运行几分钟同时尝试触发一些可疑操作如果已有线索。观察控制台输出和生成的日志文件。深度检测如果发现异常PID或行为使用--pid PID参数启动针对该PID的详细监控监控其所有系统调用、文件访问等。证据保存将eBPF工具的所有输出包括Perf事件和对比分析报告重定向到文件。同时如果条件允许立即进行内存转储。清理取证完成后停止工具。工具应自动清理诱饵文件。记录下所有操作的时间戳和命令形成完整的取证报告。利用eBPF进行Rootkit取证是一个从“依赖系统报告”到“亲自观察系统行为”的范式转变。它赋予安全人员一种深入内核、难以被欺骗的洞察力。这项技术的学习曲线虽然有些陡峭但一旦掌握它将成为你安全工具箱中最锋利、最值得信赖的武器之一。从简单的进程隐藏检测开始逐步扩展到网络、文件系统、内核模块等多个维度你就能构建起一套立体的、主动的内核威胁狩猎体系。记住在对抗中视角往往比技术本身更重要而eBPF恰恰提供了那个至关重要的、不被污染的视角。