
eBPF-आधारित Linux रूटकिट डिटेक्टर जो मल्टी-चैनल क्रॉस-व्यू विश्लेषण (sched_switch, NMI, /proc) का उपयोग करके DKOM, ट्रेसपॉइंट छेड़छाड़, और प्रक्रिया छिपाने का पता लगाता है, जिसमें हार्डवेयर-स्तरीय अखंडता सत्यापन शामिल है।
सिस्टम प्रोसेस इंटीग्रिटी और क्रॉस-व्यू एनालिसिस
"मैं गाने जा रही हूँ, तो चमको, SPiCa..."
SPiCa Rust में लिखा गया एक eBPF-आधारित Linux रूटकिट डिटेक्टर है। यह नाम Hatsune Miku गीत SPiCa और उसमें संदर्भित तारे — Spica (Alpha Virginis), जो कन्या राशि का सबसे चमकीला बिंदु है — से लिया गया है। नंगी आँखों से जो एक तारा दिखता है, वह वास्तव में एक स्पेक्ट्रोस्कोपिक बाइनरी है: दो तारे पारस्परिक कक्षा में, जो बिना उनके स्पेक्ट्रा मापे अलग-अलग वस्तुओं के रूप में भिन्न नहीं किए जा सकते। SPiCa उसी सिद्धांत को कर्नेल अवलोकन पर लागू करता है: कई स्वतंत्र चैनल भौतिक रूप से भिन्न तंत्रों से एक ही कर्नेल स्थिति को मापते हैं, और एक रूटकिट जो एक को दबाता है, वह दूसरों द्वारा उजागर हो जाता है।
अस्वीकरण: इस कोडबेस के महत्वपूर्ण भाग GLM सहायता से उत्पन्न या रिफैक्टर किए गए थे। कठोर परीक्षण और पुनरावृत्त डिज़ाइन लागू किया गया था, लेकिन उत्पादन उपयोग से पहले सुरक्षा और प्रदर्शन के लिए कोड की समीक्षा करें।
SPiCa को eBPF-सीमित प्रतिद्वंद्वी को हराने के लिए डिज़ाइन किया गया है — एक हमलावर जिसके पास उन्नत विशेषाधिकार (CAP_BPF या CAP_SYS_ADMIN) हैं और वह कर्नेल में एक विशेषाधिकार प्राप्त eBPF प्रोग्राम लोड करता है। यह प्रतिद्वंद्वी LKM रूटकिट की तुलना में मौलिक रूप से कमज़ोर है क्योंकि BPF वेरिफायर कठोर बाधाएं लगाता है:
बूट के बाद LSM गेट द्वारा LKM को अवरुद्ध करने के साथ, यह सीमित प्रतिद्वंद्वी शेष यथार्थवादी खतरा है। SPiCa की एंटी-एवेज़न मशीनरी इस खतरे के लिए कैलिब्रेट की गई है — हर बचाव ईमानदार है कि वह क्या कवर करता है और क्या नहीं।
init_module के बिना मनमाना कर्नेल लेखन देता है। SPiCa आसान LKM वेक्टर को अवरुद्ध करके फर्श उठाता है, लेकिन प्रतिद्वंद्वी को सीमित नहीं करता।SPiCa एक गहराई में रक्षा स्टैक में अंतिम-उपाय परत है, न कि ऊपर की परतों का विकल्प।
SPiCa कर्नेल हुक से जुड़े चार eBPF प्रोग्राम चलाता है, साथ ही एक उपयोगकर्ता-स्थान डिटेक्शन इंजन जो उनके आउटपुट को सिस्टम के स्वयं के दृश्य (/proc) के साथ क्रॉस-सहसंबंधित करता है।
मुख्य आर्किटेक्चरल गुण: एक रूटकिट तीनों चैनलों को एक साथ नहीं दबा सकता, जब तक कि दमन स्वयं पता लगाने योग्य या अस्थिर न हो जाए। NMI को दबाने के लिए IDT (इंटरप्ट डिस्क्रिप्टर टेबल) को पैच करने की आवश्यकता होती है, जो अधिकांश कर्नेल पर पैनिक का कारण बनता है। यह "लाइव बम" है — कुल अंधता के लिए हमलावर का एकमात्र रास्ता वह है जो संभवतः सिस्टम को क्रैश कर देता है।
sched sees it, /proc doesn't → DKOM NMI finds sched's heartbeat stopped → TAMPER sched goes globally silent, /proc != empty → SILENT
प्रत्येक पहचान वर्ग एक अंतर निर्णय है: दो या अधिक चैनलों के बीच विसंगति। पहचान इंजन रजिस्ट्री + /proc स्नैपशॉट + चैनल टाइमस्टैम्प पर एक शुद्ध फ़ंक्शन है — कोई I/O नहीं, कोई दुष्प्रभाव नहीं, पूरी तरह से यूनिट-परीक्षण योग्य।
### The NMI redesign: from observation to integrity
मूल डिज़ाइन में, NMI एक दूसरा प्रक्रिया-अवलोकन चैनल था जो CPU का नमूना लेता था और रिपोर्ट करता था कि कौन सा कार्य चल रहा है। यह अनावश्यक था: sched\_switch पहले से ही शेड्यूलिंग का अवलोकन करता है, और NMI एक अलग तंत्र के माध्यम से समान डेटा का नमूना लेता था। अनावश्यकता की लागत ~1000+ रिंग-बफ़र ईवेंट/सेकंड/CPU प्रक्रिया डेटा की थी जो 99.999% समय पुष्टि करता था "हाँ, शेड्यूलर वही कर रहा है जो शेड्यूलर करता है।"
पुनर्डिज़ाइन की गई वास्तुकला में, **NMI को प्रक्रिया अवलोकन से ट्रेसपॉइंट अखंडता सत्यापन के लिए पुन: उपयोग किया गया है।** यह अब रिपोर्ट नहीं करता कि CPU पर कौन सी प्रक्रिया है। इसके बजाय, यह एक साझा `.bss` हार्टबीट पढ़कर सत्यापित करता है कि `sched\_switch` वास्तव में चल रहा है। यह:
1. NMI रिंग-बफ़र ट्रैफ़िक का ~99% समाप्त करता है (लगभग शून्य स्थिर-स्थिति ईवेंट)
2. सीधे ट्रेसपॉइंट अलगाव, दमन, और BTF/अटैच विफलताओं का पता लगाता है (मूल BTF बग — [§11](#11-the-btf-bug-incident) देखें)
3. एक हार्डवेयर इंटरप्ट से चलता है, ट्रेसपॉइंट डिस्पैच पथ के बाहर — `bpf_override_return`, kprobe इंटरसेप्शन, और funcs-array हेरफेर से प्रतिरक्षित
4. `.bss` ग्लोबल्स को BPF हेल्पर्स के बजाय प्रत्यक्ष मेमोरी एक्सेस के माध्यम से पढ़ता है — हेल्पर फ़ंक्शंस पर `fmod_ret` से प्रतिरक्षित
---
## 3. The sched\_switch Observation Channel
`sched_switch` ट्रेसपॉइंट से जुड़ा एक eBPF प्रोग्राम हर बार फायर करता है जब कर्नेल CPU पर एक प्रक्रिया शेड्यूल करता है। यह पारंपरिक (गैर-BTF) निश्चित-ऑफ़सेट रीड का उपयोग करके ट्रेसपॉइंट तर्कों से सीधे आने वाले कार्य के PID और comm को पढ़ता है:```
ctx.read_at::<u32>(56) → next_pid
ctx.read_at::<[u8;16]>(40) → next_comm
जानबूझकर BTF/CO-RE नहीं। ट्रेसपॉइंट आर्गुमेंट लेआउट कर्नेल संस्करणों में स्थिर है (यह ट्रेसपॉइंट ABI का हिस्सा है)। हार्डकोडेड ऑफ़सेट का उपयोग BTF-रिज़ॉल्व्ड स्ट्रक्ट नेविगेशन की कर्नेल-संस्करण भंगुरता से बचाता है। यह एक जानबूझकर डिज़ाइन विकल्प है जिसे §11 में दस्तावेज़ित किया गया है।
प्रत्येक आह्वान पर, प्रोग्राम:
next_pid और next_comm पढ़ता हैProcessInfo स्ट्रक्ट को BASE_KEY से XOR-ऑबस्क्यूरेट करता हैsc_sched रिंग बफ़र में सबमिट करता है.bss ग्लोबल SCHED_HEARTBEAT में bpf_ktime_get_ns() लिखता है — वह हार्टबीट जिसकी NMI इंटीग्रिटी चेकर निगरानी करता हैप्रोग्राम इस संदर्भ में जानबूझकर bpf_get_current_pid_tgid() से बचता है। sched_switch समय पर, "current" बाहर जाने वाला कार्य है, अंदर आने वाला नहीं। ट्रेसपॉइंट आर्गुमेंट सही (अंदर आने वाली) प्रक्रिया पहचान प्रदान करते हैं।
डिटेक्शन इंजन एकल मोनोटोनिक समय आधार का उपयोग करता है: SPiCa की प्रक्रिया स्टार्टअप से नैनोसेकंड (run_detection() प्रविष्टि से Instant::now() के बाद)। जब कोई sched ईवेंट आता है, तो ईवेंट हैंडलर कॉलर-प्रदत्त now (प्रक्रिया-स्थानीय नैनो) संग्रहीत करता है, न कि ईवेंट में एम्बेडेड eBPF bpf_ktime_get_ns() मान। यह उस समय-आधार बेमेल से बचाता है जो कर्नेल बूट समय को प्रक्रिया-स्थानीय समय के साथ मिलाने पर होता — एक बग जो पहले के संस्करणों में मौजूद था और सभी लाइवनेस प्रेडिकेट को चुपचाप विफल कर देता था।
NMI प्रोग्राम एक हार्डवेयर perf काउंटर (HW_CPU_CYCLES) से जुड़ा होता है जो हार्डवेयर इंटरप्ट स्तर पर फायर करता है। NMIs को cli से अक्षम नहीं किया जा सकता — उन्हें दबाने के लिए PMU को पुनर्प्रोग्राम करना या IDT को हुक करना आवश्यक है, जो दोनों ही शोरगुल और अस्थिर करने वाले हैं।
इंटीग्रिटी जाँच तंत्र बेहद सरल है:``` .bss (shared within SPiCa's ELF object — all programs see the same memory): SCHED_HEARTBEAT ← written by sched_switch on every invocation NMI_LAST_HB ← NMI's record of the last heartbeat it observed NMI_FIRST_TICK ← first NMI invocation timestamp (grace period reference) NMI_LAST_EMIT ← throttle: last event emission timestamp
sched_switch runs → SCHED_HEARTBEAT = bpf_ktime_get_ns()
NMI fires (once per second, throttled): if SCHED_HEARTBEAT == NMI_LAST_HB: if past grace period (5 seconds since first NMI tick): → sched_switch stopped running → emit event_type=1 (TAMPER) else: → sched_switch is alive → emit event_type=0 (heartbeat OK) NMI_LAST_HB = SCHED_HEARTBEAT
### यह दायरे में हर हमले का पता क्यों लगाता है
| हमला | क्या होता है | NMI क्या देखता है |
|---|---|---|
| फंक्स ऐरे से ट्रेसपॉइंट अलग किया गया | sched\_switch निष्पादन रोक देता है | `SCHED_HEARTBEAT` फ्रीज़ हो जाता है → TAMPER |
| BTF बग / अटैच विफलता (मौन) | sched\_switch कभी निष्पादित नहीं होता | `SCHED_HEARTBEAT` 0 रहता है → TAMPER (ग्रेस अवधि के बाद) |
| `bpf_override_return` एक शेड्यूलर फ़ंक्शन पर | यदि यह ट्रेसपॉइंट को फायर होने से रोकता है | `SCHED_HEARTBEAT` फ्रीज़ हो जाता है → TAMPER |
| बाइटकोड इन-प्लेस पैच किया गया | मनमाना कर्नेल राइट (LKM-स्तर) की आवश्यकता है | eBPF खतरे के मॉडल के बाहर |
| रिंग बफ़र कंज़्यूमर पॉइंटर में हेरफेर किया गया | sched इवेंट यूज़रस्पेस तक नहीं पहुँचते | `SCHED_HEARTBEAT` अभी भी बढ़ता है (प्रोग्राम चलता है) → कोई गलत TAMPER नहीं; यूज़रस्पेस `max(sched_last)` पुराना होने पर पता लगाता है → SILENT |
### विशेष रूप से `.bss` क्यों
`.bss` ग्लोबल्स BPF प्रोग्राम के आंतरिक डेटा सेक्शन में संग्रहीत होते हैं, जो लोडर द्वारा प्रबंधित एक आंतरिक ऐरे मैप द्वारा समर्थित होते हैं। वे हैं:
- **अलग से पिन करने योग्य नहीं** — वे `/sys/fs/bpf/` में नामित मैप के रूप में दिखाई नहीं देते
- **`bpf_map_update_elem` हुक के माध्यम से इंटरसेप्ट करने योग्य नहीं** — `.bss` राइट्स सीधे मेमोरी स्टोर होते हैं, मैप-अपडेट सिस्कॉल नहीं। पुराना `sc_canary` तंत्र (`.bss` कॉपी की तुलना नामित-मैप कॉपी से करके `bpf_map_update_elem` इंटरसेप्शन का पता लगाना) अब आवश्यक नहीं है।
- **एक ही ELF ऑब्जेक्ट में प्रोग्राम के बीच साझा** — sched\_switch और NMI बिना किसी बाहरी इंटरफ़ेस के `.bss` के माध्यम से संवाद करते हैं
### BPF-आधारित इंटरसेप्शन से प्रतिरक्षा
NMI अखंडता जाँचकर्ता संरचनात्मक रूप से BPF ओवरराइड हमलों से प्रतिरक्षित है क्योंकि एक मौलिक गुण: `bpf_override_return` फ़ंक्शन **कॉल** को इंटरसेप्ट करता है, लेकिन NMI जाँचकर्ता उन चीज़ों को *कॉल* नहीं करता जिन्हें वह सत्यापित करता है — वह *सीधे `.bss` मेमोरी पढ़ता है*। आप मेमोरी रीड के रिटर्न वैल्यू को ओवरराइड नहीं कर सकते क्योंकि मेमोरी रीड कोई फ़ंक्शन कॉल नहीं है।
इसके अतिरिक्त:
- `bpf_probe_read_kernel` (वैकल्पिक डिज़ाइनों में कर्नेल स्ट्रक्ट पढ़ने के लिए उपयोग किया जाता है) एक फॉल्ट-सेफ हेल्पर है जो किसी भी पते को स्वीकार करता है — लेकिन SPiCa के `.bss` हार्टबीट डिज़ाइन को इसकी आवश्यकता भी नहीं है। जाँचकर्ता सीधे लोड निर्देशों के माध्यम से `.bss` ग्लोबल्स पढ़ता है।
- NMI प्रोग्राम NMI संदर्भ में चलता है, जहाँ kprobes संरचनात्मक रूप से अविश्वसनीय होते हैं (कर्नेल उन्हें स्थगित या दबा देता है)। जाँचकर्ता के निष्पादन के खिलाफ kprobe-आधारित हमला हार्डवेयर से लड़ता है।
### NMI इवेंट अर्थशास्त्र
NMI रिंग बफ़र (`sc_nmi`) हल्के इवेंट ले जाता है:
| `event_type` | अर्थ | यूज़रस्पेस कार्रवाई |
|---|---|---|
| 0 | हार्टबीट — NMI जीवित, sched\_switch जीवित | `last_nmi_heartbeat` टाइमस्टैम्प अपडेट करें |
| 1 | TAMPER — NMI जीवित, sched\_switch हार्टबीट फ्रीज़ | तुरंत `[TAMPER]` प्रिंट करें |
इवेंट अधिकतम एक बार प्रति सेकंड उत्सर्जित होते हैं (`NMI_LAST_EMIT` द्वारा सीमित)। यदि NMI रिंग बफ़र >5 सेकंड के लिए शांत रहता है, तो यूज़रस्पेस `[SILENT]` फायर करता है — NMI चैनल स्वयं मृत है।
---
## 5. डिटेक्शन लॉजिक```mermaid
graph TD
subgraph RING0["Kernel Space: Four eBPF Programs"]
direction TB
SCHED_P["TracePoint, sched_switch<br/>read next_pid, next_comm<br/>write SCHED_HEARTBEAT (.bss)<br/>XOR obfuscate → sc_sched"]
NMI_P["PerfEvent hardware NMI<br/>read SCHED_HEARTBEAT (.bss)<br/>compare to NMI_LAST_HB<br/>frozen → event_type=1 (TAMPER)<br/>alive → event_type=0 (heartbeat)<br/>→ sc_nmi"]
LSM_P["BPF LSM, kernel_read_file<br/>READING_MODULE<br/>gate=0: allow + log<br/>gate=1: EPERM + log → sc_lsm"]
WATCH_P["TracePoint, sched_process_exit<br/>current == SPICA_PID (.bss)? → sc_wd flag"]
end
subgraph RING3["User Space: Differential Engine"]
ENGINE["SPiCa (Tokio async)"] -->|XOR deobfuscate| RB_S[(sc_sched RingBuf)]
ENGINE -->|event_type=0: heartbeat<br/>event_type=1: TAMPER| RB_N[(sc_nmi RingBuf)]
ENGINE -->|raw read| RB_L[(sc_lsm RingBuf)]
ENGINE -->|read_dir| PROC[" /proc"]
RB_S --> FSM{Detection FSM}
PROC --> FSM
FSM -->|scheduled, absent /proc, >2s| D1["[DKOM]"]
FSM -->|in /proc, never seen by sched, >5s| D2["[GHOST]"]
RB_N -->|event_type = 1| D3["[TAMPER]"]
FSM -->|sched channel silent, /proc ≠∅| D4["[SILENT]"]
FSM -->|raw getdents64 ≠ libc readdir| D9["[HOOK]"]
RB_S -->|same TGID, different start_time_ns| D5["[DUPE]"]
RB_L -->|allowed = 1| D7["[LKM-ALLOW]"]
RB_L -->|allowed = 0| D8["[LKM-DENY]"]
WATCH_P -.->|pin exists at startup| D6["[WATCHDOG]"]
end
classDef kernbox fill:#fdf2f8,stroke:#9333ea,stroke-width:2px,color:#581c87;
classDef userbox fill:#eff6ff,stroke:#2563eb,stroke-width:2px,color:#1e3a8a;
classDef alertbox fill:#fff7ed,stroke:#ea580c,stroke-width:1px,color:#7c2d12;
classDef storage fill:#f8fafc,stroke:#64748b,stroke-width:1px;
class RING0 kernbox;
class RING3 userbox;
class D1,D2,D3,D4,D5,D6,D7,D8,D9 alertbox;
class RB_S,RB_N,RB_L,PROC storage;
SILENT एक चैनल-स्तरीय फैसला है, प्रति-प्रक्रिया नहीं। यह तब सक्रिय होता है जब:
sched चैनल मृत: सभी रजिस्ट्री रिकॉर्ड पर max(sched_last) CHANNEL_DEAD_NANOS से अधिक स्टेल है जबकि /proc गैर-रिक्त है। गैर-रिक्त /proc गार्ड जीवंतता की गारंटी है: किसी भी चालू Linux सिस्टम पर, /proc गैर-रिक्त है (init, कर्नेल थ्रेड, kworkers), और शेड्यूलर कभी भी वास्तव में निष्क्रिय नहीं होता (टाइमर टिक, I/O प्रतीक्षाकर्ता, RCU कॉलबैक)। एक गैर-रिक्त सिस्टम पर स्टेल max(sched_last) का मतलब है कि चैनल मर गया — सिस्टम निष्क्रिय नहीं है।
NMI चैनल मृत: NMI रिंग बफर से >5 सेकंड तक कोई हार्टबीट इवेंट (event_type=0) नहीं आया है। इसका मतलब है कि NMI perf इवेंट ने फायर करना बंद कर दिया — या तो perf इवेंट अलग कर दिया गया या PMU को पुनःप्रोग्राम किया गया।
दोनों जांचों में 30 सेकंड का कूलडाउन है ताकि अलर्ट की बाढ़ को रोका जा सके।
[HOOK])SPiCa प्रत्येक टिक पर /proc को दो स्वतंत्र तंत्रों के माध्यम से पढ़ता है:
रॉ getdents64 syscall (read_tgids()) — Rust का std::fs::read_dir इसे सीधे libc::syscall(SYS_getdents64, ...) के माध्यम से कॉल करता है, सभी libc फंक्शन हुक को बायपास करते हुए। LD_PRELOAD रूटकिट इस पथ को इंटरसेप्ट नहीं कर सकते।
libc readdir (read_tgids_libc()) — FFI के माध्यम से libc::opendir / libc::readdir को कॉल करता है, जो डायनामिक लिंकर से होकर जाता है और /etc/ld.so.preload हुक द्वारा पूरी तरह से इंटरसेप्टेबल है।
यदि getdents64 उन PIDs को देखता है जो libc readdir नहीं देखता, तो एक उपयोगकर्ता-स्थान रूटकिट प्रक्रियाओं को छिपाने के लिए readdir को हुक कर रहा है। SPiCa [HOOK] सक्रिय करता है:```
[HOOK] 3 PID(s) hidden from libc readdir — LD_PRELOAD rootkit suspected
This technique SPiCa के कर्नेल-स्तरीय विशेषाधिकार को यूज़रस्पेस रूटकिट्स की सक्रिय पहचान में बदल देती है। रॉ सिस्कॉल पथ ग्राउंड ट्रुथ है — कोई भी यूज़रस्पेस हुक इसे अंधा नहीं कर सकता। libc पथ वह "अनुभवित" दृश्य है जो एक सामान्य टूल (ps, ls) देखेगा। यह विसंगति लाइब्रेरी इंटरसेप्शन का स्पष्ट प्रमाण है।
**`[HOOK]` द्वारा पहचाने गए रूटकिट्स:**
| रूटकिट | छिपाने की तंत्र | पहचाना गया |
|---------|-----------------|----------|
| Symbiote | परजीवी LD_PRELOAD, `readdir` को हुक करता है | हाँ (जब सक्रिय रूप से छिपाता है) |
| JynxKit | LD_PRELOAD, `readdir` के माध्यम से `MAGIC_GID` छिपाना | हाँ |
| Azazel | LD_PRELOAD, `readdir` + `stat` हुक | हाँ |
| Medusa/OrBit | LD_PRELOAD, `readdir` + क्रेडेंशियल चोरी | हाँ |
**वास्तविक Symbiote के विरुद्ध परीक्षण:** Ubuntu 24.04 VM पर `/etc/ld.so.preload` के माध्यम से एक लाइव Symbiote नमूना (SHA256 `f55af21f...`, MalwareBazaar) तैनात किया गया था। यह नमूना `readdir`, `readdir64`, `stat`, `fstatat`, `pam_authenticate`, `pcap_loop`, `recvmsg`, `fopen`, `read`, `execve` को हुक करता है। जब LD_PRELOAD हुक सक्रिय रूप से `readdir` से एक PID छिपाता है, SPiCa एक टिक चक्र (<1 s) के भीतर `[HOOK]` फायर करता है। जब Symbiote लोड होता है लेकिन निष्क्रिय है (सक्रिय रूप से नहीं छिपा रहा है), SPiCa शून्य गलत सकारात्मक उत्पन्न करता है।
### Grace विंडो
एक 50 ms की Grace विंडो ताज़ा-forked प्रक्रियाओं पर गलत सकारात्मक को रोकती है। चैनल-स्तरीय SILENT जाँचों में 5 सेकंड की Grace अवधि होती है (NMI प्रोग्राम के `NMI_FIRST_TICK` संदर्भ) ताकि स्टार्टअप विलंबता गलत TAMPER अलर्ट उत्पन्न न करे।
---
## 6. सत्यापनकर्ता-सीमित पता गोपनीयता
### परिभाषा
**सत्यापनकर्ता-सीमित पता गोपनीयता** एक कम्प्यूटेशनल सुरक्षा गुण है जो eBPF-प्रतिबंधित विरोधी मॉडल के अंतर्गत मान्य है। यह बताता है:
> ऐसे विरोधी को देखते हुए जो eBPF प्रोग्राम लोड कर सकता है (CAP\_BPF) लेकिन BPF सत्यापनकर्ता द्वारा प्रतिबंधित है (सीमित लूप, प्रतिबंधित सहायक, कोई मनमाना कर्नेल लेखन नहीं), BPF प्रोग्राम के `.bss` डेटा अनुभाग का कर्नेल पता कम्प्यूटेशनल रूप से खोजना अव्यावहारिक है।
### चार तंत्र
यह गुण चार स्वतंत्र अवरोधों का एक संयोजन है:
1. **kASLR** — कर्नेल एड्रेस स्पेस लेआउट रैंडमाइज़ेशन कर्नेल हीप क्षेत्र को यादृच्छिक करता है जहाँ BPF मैप मेमोरी (जिसमें `.bss` बैकिंग मैप शामिल हैं) आवंटित की जाती है। सटीक पता हर बूट पर बदलता है।
2. **सत्यापनकर्ता निर्देश सीमा** — BPF सत्यापनकर्ता प्रति प्रोग्राम कुल निर्देशों को ~1 मिलियन तक सीमित करता है और आवश्यकता है कि लूप सिद्ध रूप से समाप्त हों। एक विरोधी का स्कैनिंग प्रोग्राम `bpf_probe_read_kernel` के माध्यम से प्रति लोड अधिकतम N ≈ 10^6 पतों की जांच कर सकता है। kASLR-यादृच्छिक खोज स्थान दिया गया, प्रति-प्रोग्राम सफलता संभावना नगण्य है।
3. **LSM मैप-एक्सेस गेट** (देखें [§7](#7-lsm-map-access-gate)) — गेट के बिना, एक विरोधी BPF मैप्स को ID द्वारा गणना कर सकता है (`BPF_MAP_GET_NEXT_ID` → `BPF_MAP_GET_FD_BY_ID` → `BPF_MAP_LOOKUP_ELEM`) और `.bss` सामग्री को सीधे पढ़ सकता है। गेट इस आसान पथ को हटा देता है, विरोधी को सत्यापनकर्ता-सीमित मेमोरी-स्कैन पथ पर मजबूर करता है।
4. **`.bss` स्थान** — `.bss` ग्लोबल्स मानव-पठनीय नामों के साथ नामित-मैप गणना इंटरफेस में दिखाई नहीं देते हैं। आंतरिक `.bss` बैकिंग मैप का नाम libbpf/aya द्वारा `.bss` रखा गया है, लेकिन इसकी सामग्री (वास्तविक वैश्विक चर) तभी पहुँच योग्य होती हैं जब आपके पास मैप fd हो। मैप fd के लिए या तो लोडिंग प्रक्रिया की fd तालिका या मैप ID (गेट द्वारा अवरुद्ध) की आवश्यकता होती है।
### औपचारिक तर्क संरचना```
Adversary's goal: locate SPiCa's .bss in kernel virtual memory
Search space: kernel heap region, kASLR-randomized (~2^30 entropy)
Path A — map enumeration (blocked):
BPF_MAP_GET_NEXT_ID → BPF_MAP_GET_FD_BY_ID → BPF_MAP_LOOKUP_ELEM
→ blocked by LSM gate (§7) for non-SPiCa processes
Path B — memory scan (bounded):
bpf_probe_read_kernel(addr, buf, len) — fault-safe, accepts any addr
Per-program capacity: N ≈ 10^6 probes (instruction limit)
Success probability per load: N / |search space| ≈ 10^6 / 2^30 ≈ 10^-24
Can reload programs, but: each load is a syscall (observable), and
the target address doesn't change between loads (same boot session)
Path C — timing side channel (theoretical):
bpf_probe_read_kernel timing varies on fault vs success
Binary search over the heap could reduce scan complexity to O(log |A|)
→ O(30) probes, well within instruction budget
LIMITATION: must be acknowledged. Mitigated by rate-limiting BPF_PROG_LOAD
via the LSM gate (future work).
अधिकांश BPF सुरक्षा अनुसंधान वेरिफायर को एक आक्रमण सतह (वेरिफायर बग, असंगति, JIT स्प्रे) के रूप में मानता है। यह कार्य फ्रेमिंग को उलट देता है: वेरिफायर की बाधाएँ एक रक्षात्मक आदिम हैं जो प्रतिद्वंद्वी के खोज स्थान को सीमित करती हैं। गोपनीयता क्रिप्टोग्राफिक नहीं है (यह AES नहीं है); यह गणनात्मक है — वेरिफायर प्रतिद्वंद्वी को गणना करने की अनुमति देता है उससे बंधी हुई।
bpf_probe_read_kernel समय-अवलोकन योग्य है (विफलता बनाम सफलता में अलग-अलग चक्र लगते हैं)। हीप पर समय-आधारित बाइनरी खोज सैद्धांतिक रूप से रैखिक स्कैन से अधिक कुशल है। निर्देश सीमा प्रति प्रोग्राम प्रोब को सीमित करती है, लेकिन बाइनरी खोज प्रोब गणना को O(log |A|) ≈ 30 तक कम कर सकती है। यह एक वास्तविक सीमा है। शमन: LSM गेट के माध्यम से BPF_PROG_LOAD को दर-सीमित करें।.bss वैश्विक एक आंतरिक BPF मानचित्र द्वारा समर्थित हैं। CAP_BPF वाली कोई भी प्रक्रिया सिस्टम पर सभी मानचित्रों को गणना कर सकती है:```
bpf(BPF_MAP_GET_NEXT_ID, …) → iterate all map IDs
bpf(BPF_MAP_GET_FD_BY_ID, id) → get fd to any map
bpf(BPF_MAP_LOOKUP_ELEM, fd, key, value) → read map contents
eBPF विरोधी (जो सिस्टम कॉल नहीं कर सकता) के विरुद्ध, `.bss` छिपा हुआ है। यूज़रस्पेस रूट के विरुद्ध, यह एक मानक मैप है — पूरी तरह से पढ़ने योग्य। LSM गेट इस अंतर को बंद करता है।
### रक्षा
`bpf` सिस्टम कॉल पर एक BPF LSM हुक मैप-एक्सेस कमांड की जाँच करता है:```
hook = "bpf"
read cmd (arg 0)
if cmd == BPF_MAP_GET_FD_BY_ID:
read map_id from userspace bpf_attr (bpf_probe_read_user)
if map_id matches any of SPiCa's stored IDs (.bss):
if caller_tgid != SPICA_PID:
return -EPERM
मैप IDs उपयोगकर्तास्पेस लोडर द्वारा प्रोग्राम लोड होने के तुरंत बाद, LSM हुक सशस्त्र होने से पहले .bss में लिखे जाते हैं (LKM अवरोधन के लिए मौजूदा sc_gate के समान समय पैटर्न)। किसी BTF की आवश्यकता नहीं — cmd और map_id कर्नेल स्ट्रक्ट से नहीं, बल्कि syscall तर्कों से आते हैं।
सर्जिकल: केवल SPiCa के विशिष्ट मैप IDs तक पहुंच को अवरुद्ध करता है। अन्य प्रक्रियाओं के BPF उपकरण (tcpdump, bpftrace, bcc) अपने स्वयं के मैप्स तक पहुंचते हैं और प्रभावित नहीं होते।
/proc/pid/mem — कोई अन्य रूट प्रक्रिया ptrace के माध्यम से SPiCa की प्रक्रिया मेमोरी को सीधे पढ़ सकती है, BPF syscall को पूरी तरह से दरकिनार करते हुए। यह एक मूलभूत सीमा है: आप समान विशेषाधिकार स्तर वाली प्रक्रिया को /proc/pid/mem के माध्यम से आपकी मेमोरी पढ़ने से नहीं बचा सकते। बचाव: ऑफ-होस्ट लॉग शिपिंग (यदि हमलावर स्थानीय मेमोरी पढ़ सकता है, तो वे स्थानीय अलर्ट भी दबा सकते हैं — केवल दूरस्थ लॉगिंग मदद करती है)।TPM-सुसज्जित होस्ट पर, 64-बिट XOR अस्पष्टीकरण कुंजी tss-esapi क्रेट ( libtss2-esys के लिए प्रत्यक्ष लाइब्रेरी कॉल) के माध्यम से TPM2_GetRandom से प्राप्त होती है। कुंजी किसी पाइप, किसी सबप्रोसेस कमांड लाइन, या किसी डिस्क से नहीं गुजरती। यह केवल TPM हार्डवेयर और SPiCa प्रक्रिया की अवधि के लिए कर्नेल वाष्पशील मेमोरी (.bss बैकिंग मैप) में मौजूद रहती है।
TPM के बिना होस्ट पर (vTPM के बिना VM, कंटेनर, TPM चिप के बिना मशीनें), SPiCa बिल्ड स्क्रिप्ट द्वारा /dev/urandom से उत्पन्न प्रति-बिल्ड संकलन-समय कुंजी पर वापस आ जाता है। फ़ॉलबैक स्टार्टअप पर चेतावनी बैनर के साथ स्पष्ट रूप से तेज़ होता है।
.bss प्लेसमेंटनिम्नलिखित मान eBPF प्रोग्राम के .bss सेक्शन में रहते हैं, जो किसी भी प्रोग्राम को लोड करने से पहले उपयोगकर्तास्पेस द्वारा set_global() के माध्यम से लिखे जाते हैं:
नामित BPF मैप्स bpf() syscall के मैप पुनरावृत्ति इंटरफ़ेस के माध्यम से गणना योग्य हैं (LSM गेट द्वारा अवरुद्ध — देखें §7); .bss ग्लोबल्स उसी आंतरिक मैप के माध्यम से एक्सेस किए जाते हैं लेकिन अलग से नामित या पिन करने योग्य नहीं होते।
सभी ProcessInfo फ़ील्ड्स को रिंग बफ़र्स में लिखे जाने से पहले 64-बिट BASE_KEY के साथ XOR-फोल्ड किया जाता है। event_type फ़ील्ड जानबूझकर अस्पष्ट नहीं छोड़ा जाता है ताकि उपयोगकर्तास्पेस शेष को डीअस्पष्ट करने से पहले इसे एक सेंटिनल के रूप में पढ़ सके।
यह पढ़ने-रिसाव के खिलाफ अस्पष्टीकरण है, एक सक्षम प्रतिद्वंद्वी के खिलाफ एन्क्रिप्शन नहीं। 8-बाइट दोहराव वाली कुंजी के साथ XOR संरचनात्मक रूप से ज्ञात-प्लेनटेक्स्ट के लिए कमजोर है: अनुमानित comm मान ("bash", "systemd", "kthreadd") सिफरटेक्स्ट के विरुद्ध XOR किए जाने पर सीधे कुंजी बाइट्स प्राप्त होते हैं। यह बचाव eBPF प्रतिद्वंद्वी के लिए उपयुक्त है (जो रिंग बफर को आसानी से नहीं पढ़ सकता — इसके लिए वही क्षमता अंतर आवश्यक है जो .bss की रक्षा करता है), न कि बाइनरी एक्सेस + रिंग बफर इंटरसेप्शन वाले प्रतिद्वंद्वी के लिए।
स्थिति: डिज़ाइन चरण। अभी तक लागू नहीं किया गया। यह खंड शोध पत्र के लिए लक्ष्य वास्तुकला का दस्तावेजीकरण करता है।
PCR-बाउंड TPM सीलिंग कुंजी वितरण सीमा को सॉफ़्टवेयर परत से सिलिकॉन में स्थानांतरित करती है। कुंजी स्थापना के समय अपेक्षित प्लेटफ़ॉर्म कॉन्फ़िगरेशन रजिस्टर (PCR) मानों के विरुद्ध सील की जाती है, और केवल तभी अनसील की जा सकती है जब सिस्टम की बूट स्थिति अपेक्षित मापों से मेल खाती हो।
बूट के दौरान, फर्मवेयर, बूटलोडर और कर्नेल महत्वपूर्ण घटकों को PCRs में मापते हैं:
नोट: PCR आवंटन बूट-श्रृंखला-निर्भर है। GRUB कर्नेल को PCR 4 पर मापता है; systemd-stub PCR 4 पर और कमांड लाइन को PCR 8 पर मापता है। सीलिंग नीति लक्ष्य बूट श्रृंखला से मेल खानी चाहिए।
सील (स्थापना समय): 64-बिट कुंजी TPM2_PolicyPCR सत्र के साथ TPM2_Create का उपयोग करके अपेक्षित PCR मानों के विरुद्ध सील की जाती है। सील किया गया ब्लॉब डिस्क पर संग्रहीत होता है। यह TPM की आंतरिक कुंजी से एन्क्रिप्ट किया जाता है और केवल तभी डिक्रिप्ट किया जा सकता है जब निर्दिष्ट PCRs मेल खाते हों।
अनसील (प्रारंभिक बूट, इनिट्रैम्फ्स चरण): किसी भी अविश्वसनीय उपयोगकर्तास्पेस कोड के निष्पादित होने से पहले, SPiCa का इनिट्रैम्फ्स हुक TPM2_Unseal का अनुरोध करता है। यदि वर्तमान PCR मान सील नीति से मेल खाते हैं, तो TPM कुंजी जारी करता है।
इंजेक्ट: प्रोग्राम लोड होने से पहले set_global() के माध्यम से कुंजी .bss में लिखी जाती है।
जाल: यदि किसी हमलावर ने कर्नेल (PCR 4 बेमेल) को संशोधित किया है, इनिट्रैम्फ्स (PCR 9 बेमेल) को बदल दिया है, या सिक्योर बूट नीति (PCR 7 बेमेल) को बदल दिया है, तो PCR हैश अलग हो जाते हैं। TPM अनसील करने से इनकार करता है, और SPiCa सुरक्षित रूप से विफल होता है — यह समझौता की गई कुंजी के साथ अंधा चलने के बजाय शुरू करने से इनकार करता है।
शोध पत्र के लिए, मजबूत नीति प्रस्तुत करें और सीमाओं में पुनः सील व्यापार-बंद पर चर्चा करें।
दोनों तंत्र पूरक हैं:
.bss का पता नहीं लगा सकता या पढ़ नहीं सकता जहाँ कुंजी रहती है।अकेला कोई भी पर्याप्त नहीं है। PCR सीलिंग मदद नहीं करता यदि कुंजी रनटाइम पर समझौता की जाती है (मैप गणना)। एड्रेस गोपनीयता मदद नहीं करता यदि सिस्टम SPiCa शुरू होने से पहले समझौता किया गया था (शत्रुतापूर्ण इनिट्रैम्फ्स)।
spica_lsm_modblock, मैप-एक्सेस गेट) रनटाइम पर लोड होते हैं और किसी भी PCR पर मापे नहीं जाते। एक रूटकिट जो उन्हें बूट के बाद डिटैच करता है, PCR सीलिंग द्वारा नहीं पकड़ा जाता। वह NMI हार्टबीट द्वारा पकड़ा जाता है (यह पता लगाना कि पता लगाने की प्रणाली स्वयं चलना बंद कर गई)।SPiCa अंतिम प्रवर्तन परत है। यह एक उचित रूप से कॉन्फ़िगर किए गए सिस्टम का पूरक है, ऊपर की परतों को प्रतिस्थापित नहीं करता।```mermaid
flowchart TD
SB["UEFI Secure Boot
─────────────────────────────
Verifies bootloader signature against
the UEFI db certificate store
Measured to PCR 7"]
MS["Kernel Module Signing
─────────────────────────────
CONFIG_MODULE_SIG_FORCE=y
Kernel rejects unsigned .ko at load time"]
IMA["IMA, Integrity Measurement Architecture
─────────────────────────────
Measures file hashes to TPM PCR10
Appraise policy blocks non-matching signatures"]
SPICA["SPiCa
─────────────────────────────
LSM gate: blocks LKM loads + map enumeration
sched_switch + NMI integrity channels
Differential detection: DKOM · GHOST · TAMPER · SILENT · DUPE
PCR-bound key sealing (design phase)"]
SB -->|"boot chain verified"| MS
MS -->|"signed modules only"| IMA
IMA -->|"measured + appraised"| SPICA
classDef firmware fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f
classDef kernel fill:#fdf2f8,stroke:#9333ea,stroke-width:2px,color:#581c87
classDef spica fill:#eff6ff,stroke:#2563eb,stroke-width:2px,color:#1e3a8a
class SB firmware
class MS,IMA kernel
class SPICA spica
SPiCa स्टार्टअप पर सभी चार परतों की जाँच करता है और उनकी स्थिति प्रिंट करता है।
---
## 11. BTF बग घटना
### क्या हुआ
Ubuntu (नवीनतम कर्नेल) पर परीक्षण के दौरान, एक BTF असंगति के कारण `sched_switch` ट्रेसपॉइंट प्रोग्राम सफलतापूर्वक अटैच हो गया लेकिन **एक भी इवेंट फायर नहीं हुआ।** `attach()` सिस्कॉल ने `Ok(())` लौटाया, इसलिए SPiCa सामान्य रूप से आगे बढ़ा — लेकिन sched रिंग बफ़र खाली रहा। कोई डिटेक्शन अलर्ट फायर नहीं हुआ क्योंकि डिटेक्शन इंजन के पास शेड्यूलिंग डेटा नहीं था। **SPiCa विफलता के किसी भी संकेत के बिना अंधा हो गया।**
यह एक सुरक्षा टूल के लिए सबसे खराब विफलता मोड है: मूक अंधापन।
### यह क्यों नहीं पता चला
मूल डिटेक्शन इंजन **प्रति-प्रक्रिया** तर्क करता था: प्रत्येक प्रक्रिया रिकॉर्ड में `sched_last` और `nmi_last` टाइमस्टैम्प होते थे, और जीवंतता प्रति-रिकॉर्ड गणना की जाती थी। जब sched\_switch वैश्विक रूप से मर गया:
1. `sched_live` प्रत्येक रिकॉर्ड के लिए गलत हो गया (कोई नया sched इवेंट नहीं → सभी `sched_last` मान समय समाप्त हो गए)
2. प्रति-प्रक्रिया `TAMPER` विधेय (`in_proc && nmi_live && !sched_live`) फायर हो सकता था, लेकिन इसके लिए `nmi_live` को *लगातार* 2 सेकंड तक रखना आवश्यक था। NMI विरल रूप से नमूना लेता है (10M साइकिल अवधि), इसलिए `suspect_since` नमूना जिटर पर रीसेट होता रहा और कभी परिपक्व नहीं हुआ।
3. प्रति-प्रक्रिया `SILENT` विधेय के लिए `sched_live` आवश्यक था, जो अब सबके लिए गलत था — विधेय ठीक उसी स्थिति में उलट गया जिसका पता लगाने के लिए इसे डिज़ाइन किया गया था।
4. कोई **चैनल-स्तरीय जीवंतता जाँच** नहीं थी — कोई "क्या sched कभी फायर हुआ?" या "क्या `max(sched_last)` स्टेल है?" तंत्र नहीं था।
इसके अतिरिक्त, एक टाइम-बेस बेमेल पाया गया: `sched_last` ने `bpf_ktime_get_ns()` (कर्नेल बूट नैनोसेकंड) संग्रहीत किया जबकि `evaluate()` ने `nanos_since_startup()` (प्रक्रिया-स्थानीय नैनोसेकंड) के साथ तुलना की। इन विभिन्न टाइम बेसों के `wrapping_sub` ने बड़े मान उत्पन्न किए, जिससे सभी जीवंतता विधेय तुच्छ रूप से गलत हो गए। डिटेक्शन लॉजिक प्रोडक्शन में कभी सही ढंग से काम नहीं किया था — BTF बग ने इसे मास्क कर दिया क्योंकि इसने इवेंट को आने से ही रोक दिया।
### पुनःडिज़ाइन इसे कैसे ठीक करता है
| समस्या | समाधान |
|---|---|
| कोई चैनल-स्तरीय जीवंतता जाँच नहीं | `evaluate()` अब `max(sched_last)` की गणना करता है और यदि `/proc` गैर-खाली होने पर स्टेल होता है तो `[SILENT]` फायर करता है |
| प्रति-प्रक्रिया TAMPER कभी परिपक्व नहीं हुआ (जिटर रीसेट) | TAMPER अब NMI प्रोग्राम से एक सीधा संकेत है (`.bss` हार्टबीट तुलना), थ्रेसहोल्ड वाला प्रति-प्रक्रिया FSM नहीं |
| प्रति-प्रक्रिया SILENT तब उलट गया जब sched मर गया | SILENT अब चैनल-स्तरीय है, समुच्चय से गणना, प्रति-रिकॉर्ड विधेय नहीं |
| टाइम-बेस बेमेल | `sched_last` अब प्रक्रिया-स्थानीय समय (`now` पैरामीटर) संग्रहीत करता है, `bpf_ktime_get_ns()` नहीं |
| NMI एक अनावश्यक अवलोकन था | NMI अब एक अखंडता परीक्षक है — यह `.bss` हार्टबीट के माध्यम से सीधे sched मृत्यु का पता लगाता है |
---
## 12. ज्ञात सीमाएँ और आक्रमण सतह
### eBPF खतरा मॉडल के भीतर
| आक्रमण | स्थिति | नोट्स |
|---|---|---|
| **रिंग बफ़र उपभोक्ता पॉइंटर हेरफेर** | **पता चला** (SILENT) | `max(sched_last)` स्टेल हो जाता है → SILENT. `SCHED_HEARTBEAT` अभी भी आगे बढ़ता है (प्रोग्राम चलता है) → कोई गलत TAMPER नहीं। |
| **Perf इवेंट स्ट्रक्ट DKOM** (शून्य `state` फ़ील्ड) | **पता चला** (SILENT) | NMI फायर करना बंद कर देता है → कोई हार्टबीट इवेंट नहीं → NMI चैनल पर SILENT |
| **`task_struct` फ़ील्ड स्पूफिंग** (pid/tgid अधिलेखित) | **आंशिक** (DUPE) | यदि स्पूफ़ किया गया TGID एक भिन्न `start_time_ns` वाले मौजूदा रिकॉर्ड से मेल खाता है, तो DUPE फायर होता है। यदि `start_time_ns` भी स्पूफ़ किया गया है, तो यह फिसल जाता है। |
| **eBPF प्रोग्राम डिटैचमेंट** (funcs ऐरे से हटाना) | **पता चला** (TAMPER) | NMI हार्टबीट फ्रीज हो जाता है → TAMPER |
| **बाइटकोड इन-प्लेस पैच किया गया** | **पता नहीं चला** | इसके लिए मनमाना कर्नेल राइट (LKM-स्तरीय) आवश्यक है — eBPF खतरा मॉडल के बाहर |
| **`bpf_probe_read_kernel` पर टाइमिंग साइड चैनल** | **सैद्धांतिक जोखिम** | हीप पर बाइनरी सर्च ~30 प्रोब में `.bss` पा सकती है। [§6](#6-verifier-bounded-address-secrecy) सीमाएँ देखें। |
### eBPF खतरा मॉडल के बाहर (स्वीकार, हल नहीं)
| आक्रमण | स्थिति | नोट्स |
|---|---|---|
| **IDT/NMI हैंडलर हुक** | **द लाइव बम** | वास्तुशिल्प रूप से पूर्ण बाइपास। लगभग निश्चित कर्नेल पैनिक। |
| **दोहरी-चैनल दमन** | **द लाइव बम** | एक साथ sched + NMI दोनों को दबाएं। NMI हुकिंग की अस्थिरता को विरासत में लेता है। |
| **वेरीफायर शोषण** | **गैर-लक्ष्य** | मनमाना कर्नेल कोड निष्पादन में बच निकलता है। अलग खतरा वर्ग। |
| **ptrace / `/proc/pid/mem`** | **मौलिक सीमा** | समान-विशेषाधिकार मेमोरी पढ़ना। केवल ऑफ-होस्ट लॉग शिपिंग मदद करता है। |
| **उपभोक्ता पॉइंटर + हार्टबीट पैच** | **LKM-स्तरीय** | यदि हमलावर के पास मनमाना कर्नेल राइट है, तो वे उपभोक्ता पॉइंटर को आगे बढ़ा सकते हैं और नकली हार्टबीट लिख सकते हैं। लेकिन मनमाना कर्नेल राइट = LKM-स्तरीय = खतरा मॉडल के बाहर। |
---
## 13. बिल्ड और रन
### आवश्यक शर्तें
- Linux कर्नेल >= 5.15 जिसमें `CONFIG_DEBUG_INFO_BTF=y` (केवल LSM हुक के लिए)
- मॉड्यूल ब्लॉकिंग के लिए: कर्नेल cmdline में `CONFIG_BPF_LSM=y` और `lsm=bpf`
- TPM 2.0 चिप + `tpm2-tss` लाइब्रेरी (वैकल्पिक; दृश्य चेतावनी के साथ फ़ॉलबैक)
- Nightly Rust टूलचेन
> **नोट:** `generate-vmlinux` चरण **अब आवश्यक नहीं है**। eBPF प्रोग्राम पारंपरिक ट्रेसपॉइंट ऑफ़सेट और `.bss` ग्लोबल का उपयोग करते हैं — कोई CO-RE/BTF स्ट्रक्ट नेविगेशन की आवश्यकता नहीं है। xtask `generate-vmlinux` कमांड भविष्य में उपयोग के लिए रखा गया है लेकिन बिल्ड पाइपलाइन का हिस्सा नहीं है।
सत्यापित करें कि BPF LSM सक्रिय है: `cat /sys/kernel/security/lsm` में `bpf` होना चाहिए।
### सेटअप```shell
make install-deps # system packages + nightly Rust (pacman/apt/dnf auto-detected)
make install-tools # bpf-linker
make build # compiles eBPF + userspace (no vmlinux generation needed)
make run # sudo ./target/release/spica
### initramfs में स्थापित करें (प्रारंभिक बूट सुरक्षा)```shell
sudo make install # spica install — auto-detects Debian (initramfs-tools) or Fedora (dracut)
sudo insmod some_module.ko # should fail with EPERM (gate locked) cat /sys/kernel/security/lsm # should contain 'bpf' ls /sys/fs/bpf/spica_watchdog # exists if previous run was killed ungracefully
### विकास (macOS या Linux)
eBPF प्रोग्राम macOS पर नहीं चल सकते। `cargo check` और पहचान तर्क के लिए यूनिट परीक्षण काम करते हैं; पूर्ण रनटाइम सत्यापन के लिए Linux की आवश्यकता है।```shell
make check # cargo check for workspace
make check-ebpf # cargo check for the eBPF crate (bpfel-unknown-none)
make test # unit tests for detection FSM, key derivation, obfuscation
GetRandom-केवल TPM उपयोग को पूर्ण हार्डवेयर रूट ऑफ ट्रस्ट से बदलता है। देखें §9।bpf सिस्कॉल पर जो गैर-SPiCa प्रक्रियाओं से SPiCa की आंतरिक स्थिति के मैप गणना को अवरुद्ध करता है। देखें §7।BPF_PROG_LOAD दर सीमा — LSM गेट का विस्तार करके गैर-SPiCa प्रक्रियाओं से BPF प्रोग्राम लोड को दर-सीमित करना, .bss खोज पर समय-पक्षीय चैनल जोखिम को कम करना। देखें §6 सीमाएँ।siphasher निर्भरता पहले से ही अखंडता टोकन के लिए ट्री में है)।SPiCa इंजन लाइसेंस: MIT या Apache-2.0 (कार्यक्षेत्र)। eBPF प्रोग्राम _license स्टैटिक के माध्यम से GPL लाइसेंस निर्यात करता है — यह GPL-लाइसेंस प्राप्त हेल्पर्स का उपयोग करने वाले eBPF प्रोग्राम के लिए एक कर्नेल आवश्यकता है, और केवल लोड किए गए eBPF बाइटकोड पर लागू होता है, न कि यूजरस्पेस बाइनरी पर।
चरित्र एट्रिब्यूशन: "हात्सुने मिकू" और संबद्ध चरित्र कलाकृति क्रिप्टन फ्यूचर मीडिया, इंक. (www.piapro.net) की कॉपीराइट संपत्तियाँ हैं। यह परियोजना एक स्वतंत्र, गैर-वाणिज्यिक शोध उपकरण है, जो क्रिप्टन फ्यूचर मीडिया से संबद्ध नहीं है। चरित्र का उपयोग Piapro कैरेक्टर लाइसेंस (PCL) के अंतर्गत किया गया है।
SPiCa परियोजना का नाम Toku-P के मूल गीत से प्रेरित है।
| बाधा | प्रतिद्वंद्वी पर प्रभाव |
|---|
| बाउंडेड लूप (~1M निर्देश प्रति प्रोग्राम) | सभी कर्नेल मेमोरी को स्कैन नहीं कर सकता |
| पॉइंटर सुरक्षा (प्रत्यक्ष डीरेफरेंस के लिए प्रमाणित रूप से मान्य, बाउंडेड लक्ष्य आवश्यक) | मनमाने पॉइंटर्स का अनुसरण नहीं कर सकता |
| प्रतिबंधित हेल्पर्स (प्रति-प्रोग्राम-प्रकार अनुमति सूची) | kmalloc, vmalloc, या कोई कर्नेल आवंटन फ़ंक्शन नहीं बुला सकता |
कोई मनमाना कर्नेल लेखन नहीं (bpf_probe_write_kernel अत्यधिक प्रतिबंधित) | task_struct, ट्रेसपॉइंट funcs ऐरे, या BPF प्रोग्राम बाइटकोड को संशोधित नहीं कर सकता |
| मैप एक्सेस लोड किए गए संदर्भों तक सीमित | अन्य प्रोग्रामों से संबंधित मैप तक नहीं पहुंच सकता (जब तक कि ID द्वारा गणना न की जाए — §7 देखें) |
| चैनल | तंत्र | इसे क्या दबाता है | दमन की लागत |
|---|
/proc | procfs निर्देशिका गणना | DKOM (प्रक्रिया को कार्य सूची से अलिंक करें) | कम — अच्छी तरह से समझी जाने वाली रूटकिट तकनीक |
sched_switch | शेड्यूलर पर eBPF ट्रेसपॉइंट | funcs ऐरे से प्रोग्राम को डिटैच करें; संदर्भ फ़िल्टर करें | मध्यम — funcs ऐरे में कर्नेल लेखन की आवश्यकता |
| NMI | हार्डवेयर perf काउंटर इंटरप्ट → eBPF perf इवेंट | IDT/NMI हैंडलर को हुक करें | लगभग निश्चित कर्नेल पैनिक — हार्डवेयर इंटरप्ट, मास्क करने योग्य नहीं |
| Alert | Condition | What it detects |
|---|
[DKOM] | कर्नेल-अनुसूचित TGID /proc से >2 सेकंड के लिए अनुपस्थित | प्रत्यक्ष कर्नेल ऑब्जेक्ट हेरफेर — प्रक्रिया उपयोगकर्ता-स्थान गणना से छिपाई गई |
[GHOST] | TGID /proc में मौजूद है लेकिन sched_switch द्वारा >5 सेकंड तक कभी नहीं देखा गया | /proc स्पूफिंग — DKOM जांच पास करने के लिए नकली प्रविष्टि डाली गई |
[TAMPER] | NMI sched_switch हार्टबीट फ्रोजन का पता लगाता है (event_type=1) | ट्रेसपॉइंट अलग किया गया, दबाया गया, या BTF/अटैच विफलता |
[SILENT] | max(sched_last) पूरे रजिस्ट्री में स्टेल है जबकि /proc गैर-रिक्त है; या NMI हार्टबीट >5 सेकंड के लिए अनुपस्थित | चैनल-स्तरीय अवलोकन हानि — sched या NMI चैनल मृत है |
[DUPE] | एक ही TGID, घटनाओं में भिन्न start_time_ns | task_struct फ़ील्ड स्पूफिंग — रूटकिट वैध प्रक्रिया का रूप धारण करने के लिए tgid को पैच करता है |
[HOOK] | PIDs रॉ getdents64 के माध्यम से दिखाई देते हैं लेकिन libc readdir से अनुपस्थित | LD_PRELOAD रूटकिट — उपयोगकर्ता-स्थान लाइब्रेरी इंटरसेप्शन ps, ls, और अन्य उपकरणों से प्रक्रियाओं को छिपाता है (Symbiote, JynxKit, Azazel, Medusa/OrBit) |
[WATCHDOG] | स्टार्टअप पर /sys/fs/bpf/spica_watchdog पिन मौजूद है | पिछला इंस्टेंस अनुग्रहपूर्वक मारा गया (SIGKILL, OOM, क्रैश) |
[LKM-ALLOW] | गेट खुले होने पर READING_MODULE इंटरसेप्ट किया गया (बूट विंडो) | ऑडिट रिकॉर्ड: गेट लॉक होने से पहले मॉड्यूल लोड किया गया |
[LKM-DENY] | गेट लॉक होने पर READING_MODULE इंटरसेप्ट किया गया | इनिशियलाइज़ेशन के बाद insmod/modprobe अवरुद्ध |
| ग्लोबल | उद्देश्य | किसके द्वारा लिखा गया |
|---|
BASE_KEY | XOR अस्पष्टीकरण कुंजी | लोड समय पर उपयोगकर्तास्पेस |
SPICA_PID | SPiCa का स्वयं का TGID (वॉचडॉग) | लोड समय पर उपयोगकर्तास्पेस |
SCHED_HEARTBEAT | sched_switch लाइवनेस टाइमस्टैम्प | प्रत्येक आह्वान पर sched_switch प्रोग्राम |
NMI_LAST_HB | अंतिम sched हार्टबीट का NMI रिकॉर्ड | प्रत्येक जाँच पर NMI प्रोग्राम |
NMI_FIRST_TICK | पहला NMI आह्वान ktime (अनुग्रह अवधि) | पहले आह्वान पर NMI प्रोग्राम |
NMI_LAST_EMIT | थ्रॉटल: अंतिम घटना उत्सर्जन ktime | प्रत्येक उत्सर्जन पर NMI प्रोग्राम |
| PCR | यह क्या मापता है | स्थिरता |
|---|
| PCR 4 | बूटलोडर कोड + कर्नेल इमेज (GRUB दोनों को मापता है) | कर्नेल अपडेट पर बदलता है |
| PCR 5 | GPT/MBR विभाजन तालिका, बूट कॉन्फ़िगरेशन | अपडेट में स्थिर |
| PCR 7 | सिक्योर बूट नीति (SI नीति, MOK, db/dbx) | कर्नेल अपडेट में स्थिर |
| PCR 8 | कर्नेल कमांड लाइन (systemd-stub माप) | स्थिर जब तक cmdline न बदले |
| PCR 9 | इनिट्रैम्फ्स (GRUB यहाँ initrd को मापता है) | इनिट्रैम्फ्स अपडेट पर बदलता है |
| PCR 10 | IMA माप सूची | जैसे-जैसे निष्पादन योग्य मापे जाते हैं बदलता है |
| नीति | किसके विरुद्ध सील की गई | ताकत | परिचालन लागत |
|---|
| मजबूत | PCR 4 + 7 + 9 + 10 | कर्नेल, इनिट्रैम्फ्स, सिक्योर बूट और IMA परिवर्तनों को पकड़ता है | प्रत्येक कर्नेल/इनिट्रैम्फ्स अपडेट के बाद पुनः सील करें |
| संतुलित | PCR 7 + 10 | सिक्योर बूट और IMA मूल्यांकन परिवर्तनों को पकड़ता है; कर्नेल अपडेट में स्थिर | केवल सिक्योर बूट नीति या IMA नीति परिवर्तनों पर पुनः सील करें |
| न्यूनतम | PCR 7 केवल | केवल सिक्योर बूट स्थिति परिवर्तनों को पकड़ता है | बहुत स्थिर; सबसे कमजोर बंधन |
| शब्द | परिभाषा |
|---|
| BPF | बर्कले पैकेट फ़िल्टर — सैंडबॉक्स प्रोग्राम के लिए कर्नेल-आंतरिक निष्पादन इंजन। आधुनिक BPF (eBPF) पैकेट से आगे बढ़कर ट्रेसिंग, सुरक्षा और नेटवर्किंग तक फैला है। |
| BTF | BPF टाइप फ़ॉर्मेट — कर्नेल डीबग जानकारी जो CO-RE (एक बार कंपाइल करें, हर जगह चलाएँ) प्रोग्राम को पोर्टेबल रूप से कर्नेल स्ट्रक्ट को नेविगेट करने की अनुमति देती है। |
| CO-RE | एक बार कंपाइल करें, हर जगह चलाएँ — BTF का उपयोग करके पोर्टेबल प्रोग्राम लिखने की BPF तकनीक जो विभिन्न कर्नेल संस्करणों के अनुकूल होती है। |
| DKOM | डायरेक्ट कर्नेल ऑब्जेक्ट मैनिपुलेशन — रूटकिट तकनीक जो प्रक्रिया को /proc से छिपाने के लिए कर्नेल की लिंक्ड लिस्ट से हटाती है। |
| fmod_ret | BPF प्रोग्राम प्रकार जो BPF ट्रैम्पोलिन के माध्यम से कर्नेल फ़ंक्शन के रिटर्न वैल्यू को संशोधित करता है। |
| freplace | BPF प्रोग्राम एक्सटेंशन — किसी अन्य BPF प्रोग्राम के एक विशिष्ट (उप)फ़ंक्शन से जुड़ता है, उसके निष्पादन को रोकता है। |
| funcs ऐरे | कर्नेल ट्रेसपॉइंट स्ट्रक्ट में फ़ंक्शन-पॉइंटर ऐरे जो ट्रेसपॉइंट फायर होने पर कॉलबैक फ़ंक्शन (BPF प्रोग्राम सहित) को धारण करता है। |
| IDT | इंटरप्ट डिस्क्रिप्टर टेबल — CPU संरचना जो इंटरप्ट वेक्टर को हैंडलर फ़ंक्शन से मैप करती है। NMI एंट्री को हुक करने के लिए IDT में पैचिंग की आवश्यकता होती है। |
| kASLR | कर्नेल एड्रेस स्पेस लेआउट रैंडमाइजेशन — शोषण में बाधा उत्पन्न करने के लिए प्रत्येक बूट पर कर्नेल कोड/डेटा पतों को यादृच्छिक करता है। |
| NMI | नॉन-मास्केबल इंटरप्ट — हार्डवेयर इंटरप्ट जिसे सॉफ़्टवेयर (cli) द्वारा अक्षम नहीं किया जा सकता। हार्डवेयर-स्तरीय अवलोकन के लिए perf काउंटरों द्वारा उपयोग किया जाता है। |
| PCR | प्लेटफ़ॉर्म कॉन्फ़िगरेशन रजिस्टर — TPM रजिस्टर जो बूट घटकों के माप (हैश) संचित करता है। केवल रीबूट करके रीसेट किया जा सकता है, केवल बढ़ाया जा सकता है। |
| PMU | परफॉरमेंस मॉनिटरिंग यूनिट — CPU में हार्डवेयर काउंटर जो घटनाओं (चक्र, कैश मिस इत्यादि) की गिनती करते हैं और सीमाओं पर इंटरप्ट (NMI) ट्रिगर कर सकते हैं। |
| TPM | ट्रस्टेड प्लेटफ़ॉर्म मॉड्यूल — क्रिप्टोग्राफ़िक कोप्रोसेसर जो हार्डवेयर-जड़ित कुंजी भंडारण, यादृच्छिक संख्या पीढ़ी और माप प्रमाणीकरण प्रदान करता है। |
| वेरिफ़ायर | BPF वेरिफ़ायर — कर्नेल घटक जो लोड करने से पहले BPF प्रोग्राम का स्थैतिक विश्लेषण करता है ताकि यह सुनिश्चित हो सके कि वे समाप्त हों और असुरक्षित मेमोरी तक न पहुँचें। |