如何用eBPF阻止文件打开?请明确方案适配uprobe/root probe
Got it, let's walk through exactly how to use eBPF to block access to a specific file when someone tries to open it via the open syscall, plus clear up the compatibility with uprobe/kprobe (I think you might have meant kprobe instead of "root probe"—I'll cover that too!).
To block file access, we need to intercept the open/openat system calls (most modern apps use openat instead of raw open), check the target filename, and modify the syscall's return value to an error code if it matches our target file. We'll use kprobes (kernel function probes) for this since they let us hook into kernel-level syscalls globally.
Quick Prototype with BCC
BCC is a great tool for rapid eBPF development—it handles a lot of kernel compatibility boilerplate for us. Here's a working script:
from bcc import BPF import argparse parser = argparse.ArgumentParser() parser.add_argument("target_file", help="Full path or filename to block access to") args = parser.parse_args() bpf_program = """ #include <uapi/linux/ptrace.h> #include <linux/fs.h> // Hash map to store our target filename from user space BPF_HASH(targets, char *, u32); int block_open_call(struct pt_regs *ctx, const char __user *filename, int flags) { char fname_buf[256]; u32 *is_target; // Safely copy the filename from user space to kernel space if (bpf_probe_read_user_str(fname_buf, sizeof(fname_buf), filename) < 0) { return 0; } // Check if this filename is in our target list is_target = targets.lookup(&fname_buf); if (is_target) { // Overwrite the syscall's return value with -EACCES (permission denied) // Note: This is x86_64-specific—adjust registers for ARM/RISC-V ctx->ax = -EACCES; // Return early to stop the original syscall from executing return 0; } return 0; } """ # Initialize BPF and load our program b = BPF(text=bpf_program) # Hook into both sys_open and sys_openat (covers most use cases) b.attach_kprobe(event="sys_open", fn_name="block_open_call") b.attach_kprobe(event="sys_openat", fn_name="block_open_call") # Push our target filename into the BPF hash map b["targets"][args.target_file.encode()] = 1 print(f"🔒 Blocking access to {args.target_file}... Press Ctrl+C to exit.") # Keep the script running to maintain the probe b.trace_print()
How This Works
- We attach kprobes to the entry points of the
sys_openandsys_openatkernel functions. - The BPF program copies the filename from user space (since kernel can't directly access user memory safely) and checks if it's in our target list.
- If it matches, we overwrite the syscall's return register (
axon x86_64) with-EACCES, so the calling process gets a "permission denied" error instead of opening the file.
Let's break down which probe types fit this use case:
1. Kprobe (Kernel Probe)
- Full compatibility: This is exactly what we used in the example. Kprobes hook into kernel functions, so they intercept every process that calls the
open/openatsyscall—perfect for global file blocking. - Pros: Covers all processes, no need to target specific apps.
- Caveats: Kernel function names can vary slightly between versions (e.g.,
__x64_sys_openon newer kernels), but BCC automatically handles this mapping for you.
2. Uprobe (User-Space Probe)
- Partial compatibility: Uprobes hook into user-space functions (like the
openfunction in libc). They only intercept calls from specific processes or libraries, not all system-wide access. - When to use: If you only want to block a single app from opening the file (e.g., block
catfrom reading/etc/secret), uprobe works. But it's not suitable for global blocking.
"Root Probe" Clarification
I think you might have meant kprobe (since "root probe" isn't a standard eBPF term). If you're referring to kernel-level probes that require root privileges, yes—both kprobes and uprobes need root (or specific capabilities like CAP_BPF/CAP_PERFMON on kernel 5.8+) to attach.
- Permissions: Run the script as root—eBPF kernel operations require elevated privileges.
- Architecture: The example uses
ctx->ax(x86_64's syscall return register). For ARM/RISC-V, usectx->regs[0]instead. - Filename Matching: The script does exact matches. For fuzzy matching (e.g., block all files in
/secret/), usebpf_strstr(requires kernel 5.10+) to check for substrings. - Kernel Version: Works on Linux 4.15+ (minimum for eBPF support), but newer kernels have better eBPF features.
内容的提问来源于stack exchange,提问作者Liroshka

