Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
SPiCa — eBPF-based Linux rootkit detector using multi-channel cross-view analysis (sched_switch, NMI, /proc) to detect DKOM, tracepoint tampering, and process hiding with hardware-level integrity verification. | Kitploit
Tools/GitHubGitHub/0xkirisame/spica
Defensive ToolsIndicator of Compromise (IOC) ManagementForensicsMalware AnalysisBinary AnalysisIntrusion DetectionPapers & ResearchLearning & EducationIncident ResponseAnomaly Detection
GitHub0xkirisame/spica
1046292 months agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →

SPiCa

eBPF-based Linux rootkit detector using multi-channel cross-view analysis (sched_switch, NMI, /proc) to detect DKOM, tracepoint tampering, and process hiding with hardware-level integrity verification.

View Repository
Share

SPiCa

System Process Integrity & Cross-view Analysis

SPiCa

"I'm going to sing, so shine bright, SPiCa..."

SPiCa is an eBPF-based Linux rootkit detector written in Rust. The name comes from the Hatsune Miku song SPiCa and the star it references — Spica (Alpha Virginis), the brightest point in Virgo. What looks like a single star to the naked eye is actually a spectroscopic binary: two stars in mutual orbit, indistinguishable as separate objects without measuring their spectra. SPiCa applies the same principle to kernel observation: multiple independent channels measure the same kernel state from physically distinct mechanisms, and a rootkit that suppresses one is exposed by the others.

Disclaimer: Significant portions of this codebase were generated or refactored with GLM assistance. Rigorous testing and iterative design were applied, but review the code for security and performance before production use.


Table of Contents

  1. Threat Model
  2. Architecture Overview
  3. The sched_switch Observation Channel
  4. The NMI Integrity Channel
  5. Detection Logic
  6. Verifier-Bounded Address Secrecy
  7. LSM Map-Access Gate
  8. Key Management & Obfuscation
  9. PCR-Bound TPM Sealing (Design)
  10. Defense in Depth
  11. The BTF Bug Incident
  12. Known Limitations & Attack Surface
  13. Build & Run
  14. Roadmap
  15. Glossary

1. Threat Model

The constrained adversary: the eBPF rootkit

SPiCa is designed to defeat the eBPF-constrained adversary — an attacker with elevated privileges (CAP_BPF or CAP_SYS_ADMIN) who loads a privileged eBPF program into the kernel. This adversary is fundamentally weaker than an LKM rootkit because the BPF verifier imposes hard constraints:

ConstraintEffect on adversary
Bounded loops (~1M instructions per program)Cannot scan all of kernel memory
Pointer safety (direct dereference requires provably valid, bounded target)Cannot follow arbitrary pointers
Restricted helpers (per-program-type allowlist)Cannot call kmalloc, vmalloc, or any kernel allocation function
No arbitrary kernel write (bpf_probe_write_kernel heavily restricted)Cannot modify task_struct, tracepoint funcs arrays, or BPF program bytecode
Map access scoped to loaded referencesCannot access maps belonging to other programs (unless enumerated by ID — see §7)

With LKMs blocked by the LSM gate after boot, this constrained adversary is the realistic remaining threat. SPiCa's anti-evasion machinery is calibrated for this threat — every defense is honest about what it covers and what it doesn't.

Non-goals

  • Nation-state kernel exploits — memory corruption yielding arbitrary kernel write without init_module. SPiCa raises the floor by blocking the easier LKM vector but does not ceiling the adversary.
  • Hostile LKMs loaded before SPiCa started — defense in depth (Secure Boot, module signing, IMA) is required for the boot window.
  • Verifier exploits — if the BPF verifier is unsound (historical CVEs: CVE-2020-27194, CVE-2022-23222, CVE-2023-2163), the adversary escapes the constrained model into arbitrary kernel code execution. This is a separate threat class. SPiCa's defenses hold under a correct verifier.

SPiCa is the last-resort layer in a defense-in-depth stack, not a substitute for the layers above.


2. Architecture Overview

SPiCa runs four eBPF programs attached to kernel hooks, plus a userspace detection engine that cross-correlates their output against the system's view of itself (/proc).

Three observation channels, each suppressible only by a mechanism of escalating cost

ChannelMechanismWhat suppresses itCost of suppression
/procprocfs directory enumerationDKOM (unlink process from task list)Low — well-understood rootkit technique
sched_switcheBPF tracepoint on the schedulerDetach program from funcs array; filter contextMedium — requires kernel write to funcs array
NMIHardware perf counter interrupt → eBPF perf eventHook the IDT/NMI handlerNear-certain kernel panic — hardware interrupt, not maskable

The key architectural property: a rootkit cannot suppress all three channels simultaneously without the suppression itself becoming detectable or destabilizing. Suppressing NMI requires patching the IDT (Interrupt Descriptor Table), which panics on most kernels. This is the "live bomb" — the attacker's only path to total blindness is one that likely crashes the system.

Detection model — three sentences

sched sees it, /proc doesn't              → DKOM
NMI finds sched's heartbeat stopped       → TAMPER
sched goes globally silent, /proc != empty → SILENT

Each detection class is a differential verdict: a discrepancy between two or more channels. The detection engine is a pure function over the registry + /proc snapshot + channel timestamps — no I/O, no side effects, fully unit-testable.

The NMI redesign: from observation to integrity

In the original design, NMI was a second process-observation channel that sampled the CPU and reported which task was running. This was redundant: sched_switch already observes scheduling, and NMI sampled the same data via a different mechanism. The redundancy cost ~1000+ ring-buffer events/sec/CPU of process data that 99.999% of the time confirmed "yes, the scheduler is doing what the scheduler does."

In the redesigned architecture, NMI is repurposed from process observation to tracepoint integrity verification. It no longer reports which process is on the CPU. Instead, it verifies that sched_switch is actually running by reading a shared .bss heartbeat. This:

  1. Eliminates ~99% of NMI ring-buffer traffic (near-zero steady-state events)
  2. Directly detects tracepoint detachment, suppression, and BTF/attach failures (the original BTF bug — see §11)
  3. Runs from a hardware interrupt, outside the tracepoint dispatch path — immune to bpf_override_return, kprobe interception, and funcs-array manipulation
  4. Reads .bss globals via direct memory access, not BPF helpers — immune to fmod_ret on helper functions

3. The sched_switch Observation Channel

An eBPF program attached to the sched_switch tracepoint fires every time the kernel schedules a process onto a CPU. It reads the incoming task's PID and comm directly from the tracepoint arguments using traditional (non-BTF) fixed-offset reads:

ctx.read_at::<u32>(56)    → next_pid
ctx.read_at::<[u8;16]>(40) → next_comm

Deliberately not BTF/CO-RE. The tracepoint argument layout is stable across kernel versions (it's part of the tracepoint ABI). Using hardcoded offsets avoids the kernel-version fragility of BTF-resolved struct navigation. This is a deliberate design choice documented in §11.

On every invocation, the program:

  1. Reads next_pid and next_comm from the tracepoint context
  2. Filters out the idle task (PID 0)
  3. XOR-obfuscates the ProcessInfo struct with BASE_KEY
  4. Submits to the sc_sched ring buffer
  5. Writes bpf_ktime_get_ns() to the .bss global SCHED_HEARTBEAT — the heartbeat that the NMI integrity checker monitors
Download Tool