This is the official repository for Liminalis, a Linux eBPF Rootkit Framework. Building on our track record of sharing offensive research with the community - Abyss (Windows UEFI Bootkit) and Benthic (Windows Kernel-Mode Rootkit) to Antarctic (first Public Linux UEFI Bootkit) and Gillyweed (Linux LKM Rootkit) - Liminalis explores the next frontier of kernel-level stealth by weaponizing eBPF, a technology originally designed for observability and networking, turning it into a platform for file protection, process suppression, and network filtering without ever loading a traditional kernel module.
Liminalis is a modular eBPF-based rootkit framework that loads three independent kernel programs from a single BPF object and manages them through a unified userland loader. The name comes from the Latin limen, threshold, reflecting the position of eBPF itself: a technology that lives at the threshold between userland and the kernel, instrumented but not fully inside either world.
Unlike traditional LKM rootkits that require loading a kernel module via insmod, Liminalis operates entirely through the eBPF subsystem. The kernel verifies and JIT-compiles all three programs in a single load operation, and the entire framework is torn down automatically when the loader process exits. All blocklists live in BPF hash maps that persist in the kernel for the lifetime of the loader and can be updated at runtime using bpftool without restarting or reloading anything.
The project follows a two-veil architecture that separates userland control from kernel-space instrumentation, following the same modular pattern as the Gillyweed LKM rootkit.
Loader is the userland component. It opens, loads, and attaches the compiled BPF object, populates the blocklist maps from text files provided as arguments, and keeps the programs alive until the operator sends Ctrl-C. The loader is built around a set of function modules: "Helpers" provides shared utility routines, "BlockFiles" manages the file blocklist and attaches the openat tracepoint, "BlockProcesses" manages the process blocklist and attaches the execve tracepoint, and "BlockConnections" manages the IP blocklist and attaches the XDP hook.
eBPFProgram is the kernel-space component. It contains a single BPF compilation unit (Program.bpf.c) that includes all three program modules and their shared map definitions. Each module is implemented in its own source file under "Programs/": Program01BlockFiles.bpf.c hooks the openat tracepoint, Program02BlockProcesses.bpf.c hooks the execve tracepoint, and Program03BlockConnections.bpf.c hooks XDP at the network driver level.
Liminalis leverages the eBPF subsystem to execute verified, JIT-compiled programs inside the kernel without loading a traditional module. The loading pipeline compiles all three programs into a single BPF ELF object using clang, which the loader then opens with bpf_object__open() and loads with bpf_object__load(). At that point the kernel verifier validates all three programs and the JIT compiler translates them to native machine code in a single operation.
Each module is then attached to its hook point independently. BlockFiles and BlockProcesses attach to kernel tracepoints (sys_enter_openat and sys_enter_execve respectively), which fire every time a process invokes those system calls. BlockConnections attaches to the XDP hook on the specified network interface, which fires for every incoming packet before the kernel even allocates an sk_buff structure.
When a hooked event fires, the corresponding BPF program reads the relevant context (file path, executable path, or source IP), looks it up in its BPF hash map, and either allows the operation to continue or takes action - bpf_send_signal(SIGKILL) for file and process blocking, or returning XDP_DROP for packet filtering. The entire framework is cleaned up automatically when the loader exits: the kernel detaches all programs, removes all maps, and leaves no trace of the instrumentation.
Liminalis deploys three independent modules, each targeting a different layer of the system.
The BlockFiles module hooks the sys_enter_openat tracepoint, which fires every time a process calls openat() to open a file. When the tracepoint fires, the BPF program reads the file path from userspace using bpf_probe_read_user_str(), extracts the basename by scanning for the last "/" separator, and checks it against the map_block_files hash map. If the basename matches a blocklisted entry, the program calls bpf_send_signal(SIGKILL) to kill the calling process before a file descriptor is ever returned. The file remains on disk, but any process that attempts to open it is terminated instantly - protecting attacker-controlled files from forensic tools and incident responders.
The BlockProcesses module hooks the sys_enter_execve tracepoint, which fires every time a process calls execve() to launch a new executable. The BPF program reads the target executable path from userspace and checks it against the map_block_proc hash map. If the path matches a blocklisted entry, the program calls bpf_send_signal(SIGKILL) to kill the process before execve completes. This prevents monitoring and enumeration tools like ps, top, and htop from ever executing on the compromised system.
The BlockConnections module operates at the XDP hook in the network driver, the earliest possible point in the packet processing path. When an inbound packet arrives at the network interface, the BPF program parses the Ethernet and IPv4 headers, extracts the source IP address, and checks it against the map_block_connections hash map. If the source IP matches a blocklisted entry, the program returns XDP_DROP, discarding the packet at the driver level before the kernel networking stack ever sees it - before sk_buff allocation, before netfilter, before anything. Traffic from blocked IPs simply ceases to exist from the perspective of the system.
