NDT : Dans le jargon, on utilise souvent hook et hooking, ... tel quels, sans les traduire. C'est un mot qui porte beaucoup de sens et pas facile à traduire. D'habitude, on traduit par détournement/redirection, qui expriment clairement l'idée d'un hook de fonction. Mais ces terme perdent toute la saveur et les idées qu'il y a derrière "hooking". Ici, on traduira par "crochet" et "crochetage", qui gardent cette idée de détourner/contourner quelque chose, avec toute la saveur que le crochetage implique. Ces termes étants expliqués, bonne lecture. ==Phrack Inc.== Volume 0x0b, Issue 0x3a, Phile #0x08 of 0x0e |=-------------=[ Crochetage avancé de fonction sur IA32 ]=--------------=| |=----------------=[ (IA32 ADVANCED FUNCTION HOOKING) ]=-----------------=| |=-----------------------------------------------------------------------=| |=-------------------=[ mayhem ]=---------------------=| |=------=[ Traduit par Thiébaud Weksteen ]=-------=| |=-----------------------=[ 08 Décembre 2001 ]=------------------------=| --[ Sommaire 1 - Introduction 1.1 - Histoire 1.2 - Nouvelle demande 2 - Les bases du crochetage 2.1 - Techniques usuelles 2.2 - Choses à ne pas oublier 3 - Le code expliqué 4 - Utilisation de la bibliothèque 4.1 - L'API 4.2 - Résolution des symboles du noyau 4.3 - Les rouages de LKH : l'objet hook_t 5 - Tester le code 5.1 - Chargement du module 5.2 - Amusons-nous un peu 5.3 - Le code 6 - Références --[ 1 - Introduction Les abus, l'enregistrement des traces, les corrections ou même le débogage : voilà de bonnes raisons de penser que le crochetage (hooking) à son importance. Nous allons essayer de comprendre comment cela fonctionne. Le contexte de démonstration est l'environnement du noyau Linux. On trouvera, à la fin de l'article une bibliothèque universelle de détournement pour la série du noyau linux 2.4, développée sous 2.4.5 et tournant sur une architecture IA32. Elle s'appelle LKH, Linux Kernel Hooker [Crocheteur de noyau Linux]. ----[ 1.1 - Histoire Un des documents de référence sur le sujet du crochetage de fonction a été publié en Novembre 1999 et son auteur est Silvio Cesare (salut mec ;-). Cette implémentation était assez simple puisque le crochetage consistait à modifier les premiers octets de la fonction pour sauter vers un autre code, en vue de filtrer l'accès à la fonction acct_process du noyau, pour éviter que certains processus ne soient pris en compte. ----[ 1.2 - Nouveaux besoins Plusieurs travaux ont été effectués depuis cette époque : - l'utilisation pragmatique des redirections nécessite souvent, si ce n'est toujours, les paramètres originaux, quelque soit leur nombre ou leur taille (par exemple, si l'on désire modifier et transférer des paquets IP). - on peut avoir besoin de désactiver un crochet à la demande, ce qui est parfait pour la configuration du noyau en cours de fonctionnement. On peut aussi vouloir appeler les fonctions originales (crochetage discret, utilisé par les programmes de surveillance) ou non (crochetage agressif, utilisé par les rustines de sécurité pour gérer ACL - Access Control Lists - ) sur les objets du noyau. - dans certains cas, on peut aussi avoir besoin de détruire le crochet juste après le premier appel, par exemple, pour faire des statistiques (on peut crocheter une fois par seconde ou par minute). --[ 2 - Les bases du crochetage ----[ 2.1 Techniques usuelles Bien sûr, le code de base du crochetage doit être réalisé en langage assembleur, mais le code de formatage du crochetage est réalisé en C. L'interface de haut-niveau LKH est décrite dans la section API. Commençons par comprendre les bases du crochetage. Voici en quoi consiste, en gros, le crochetage : - modifier le début du code d'une fonction pour pointer vers un autre code (nommé 'code de crochetage'). C'est une façon de faire ce qui ne date pas d'hier et qui est efficace. On peut aussi poser une rustine sur chaque appel référençant cette fonction dans le segment du code. Cette seconde méthode a quelques avantages (c'est vraiment discret) mais l'implémentation est un peu plus compliquée (il faut analyser les blocs de mémoire, puis examiner le code) et n'est pas très rapide. - modifier, lors de l'exécution, l'adresse de retour de la fonction pour prendre le contrôle lorsque l'exécution de la fonction crochetée est terminée. - le code de crochetage doit posséder deux parties différentes. La première doit être executée avant la fonction (préparer la pile pour accéder aux paramètres, lancer les fonctions de rappel [callback], restaurer le code de l'ancienne fonction). La seconde doit être exécutée après (réinitialiser de nouveau le crochet si nécessaire). - les paramètres par défaut (définissant le comportement du crochet) doivent être attribués lors de la création du crochet (avant de modifier le code de la fonction). Les paramètres dépendant de la fonction doivent être fixés à ce moment-là. - ajouter des fonctions de rappel. Chaque fonction de rappel peut accéder aux paramètres de la fonction originale et même les modifier. - activer, désactiver, changer les paramètres, ajouter ou retirer des fonctions de rappel en fonction de nos besoins. ----[ 2.2 - Choses à ne pas oublier -> Fonctions sans pointeur de cadre: Une caractéristique importante est la capacité de crocheter des fonctions compilées avec l'option gcc -fomit-frame-pointer. Cette caractéristique implique l'absence de %ebp dans le code du crochetage, c'est pourquoi nous allons utiliser uniquement %esp pour les opérations sur la pile. Nous allons aussi devoir faire quelques mises à jour (quelques octets par-ci, par-là) pour corriger les décalages relatifs d'%ebp dans le code du crochet. Pour plus d'informations à ce sujet, voir khook_create() dans lkh.c. Le code du crochet doit également être indépendant de sa position. Ceci explique pourquoi tellement de décalages sont corrigés lors de l'exécution (Comme nous sommes dans le noyau, les décalages doivent être corrigés lors de la création du crochet, mais des techniques tout à fait comparables sont possibles pour le crochetage de fonction dans les processus *à l'exécution*). -> Récursivité Il faut pouvoir appeler la fonction originale à partir d'une fonction de rappel, donc le code original doit être restauré avant l'exécution de n'importe quelle fonction de rappel. -> Valeurs de retour Il faut retourner la valeur correcte dans %eax, qu'il y ait des fonctions de rappel ou non, que la fonction originale soit appelée ou non. Dans la démonstration, la valeur de retour est celle de la dernière fonction de rappel si la fonction originale n'est pas appelée. Si ni les fonctions de rappel ni la fonction originale ne sont appelées, la valeur de retour est indépendante de notre volonté. -> Les fonctions de rappel POST Il est impossible d'accéder aux paramètres de la fonction si des fonctions de rappel ont été exécutées après la fonction originale. C'est donc quelque chose à éviter si possible. Voici quand même comment on le fait : - définir le crochet comme agressif. - appeler les fonctions de rappel PRE. - appeler la fonction originale à partir d'une fonction de rappel avec ses propres paramètres. - appeler les fonctions de rappel POST. --[ 3 - Le code expliqué Premièrement, il faut installer le crochet. A - Écraser, avec un saut indirect vers la zone de code du crochet, les 7 premiers octets de la routine détournée. L'adresse virtuelle introduit dans %eax est l'adresse absolue du code du crochet. Ainsi à chaque appel de la fonction piratez_moi(), le code du crochet prendra le contrôle. Avant le détournement: 0x80485ec : mov 0x4(%esp,1),%eax 0x80485f0 : push %eax 0x80485f1 : push $0x8048e00 0x80485f6 : call 0x80484f0 0x80485fb : add $0x8,%esp Après le détournement: 0x80485ec : mov $0x804a323,%eax 0x80485f1 : jmp *%eax 0x80485f3 : movl (%eax,%ecx,1),%es 0x80485f6 : call 0x80484f0 0x80485fb : add $0x8,%esp Les 3 instructions affichées après le saut ne veulent rien dire, puisque gdb est induit en erreur par notre crochet. B - Réinitialiser les octets originaux de la fonction crochetée. En effet, il faut pouvoir appeler la fonction originale sans tout casser. pusha movl $0x00, %esi (1) movl $0x00, %edi (2) push %ds pop %es cld xor %ecx, %ecx movb $0x07, %cl rep movsl Les deux décalages NULL ont, en fait, été modifiés pendant la création du crochet (comme leurs valeurs dépendent du décalage de la fonction crochetée, il faut corriger le code de crochetage lors de l'exécution). (1) est fixé par le décalage du tampon contenant les 7 premiers octets enregistrés de la fonction originale. (2) est fixé par l'adresse de la fonction originale. Si vous connaissez bien le langage assembleur x86, vous savez que ces instructions copient les %ecx octets de %ds:%esi vers %ds:%edi. Voir [2] pour connaître les instructions spécifiques à INTEL. C - Initialiser la pile pour autoriser l'accès en lecture/écriture aux paramètres et le lancement de fonctions de rappel. On copie l'adresse du premier paramètre original dans %eax puis on l'empile. leal 8(%esp), %eax push %eax nop; nop; nop; nop; nop nop; nop; nop; nop; nop nop; nop; nop; nop; nop nop; nop; nop; nop; nop nop; nop; nop; nop; nop nop; nop; nop; nop; nop nop; nop; nop; nop; nop nop; nop; nop; nop; nop Notez que les emplacements vides sont plein d'instruction NOP (code opération 0x90). Cela signifie aucune opération. Lorsqu'un emplacement est rempli (en utilisant la fonction khook_add_entry), 5 octets sont utilisés : - le code opération de 'call' (code opération 0xE8) - l'adresse virtuelle de la fonction de rappel (adresse relative de 4 octets) On a choisi de fixer un maximum de 8 fonctions de rappel. Chaque fonction de rappel insérée est appelée avec un paramètre (la valeur empilée de %eax contenant l'adresse des paramètres de la fonction originale, posés sur la pile). D - Réinitialiser la pile. add $0x04, %esp On retire alors l'adresse du paramètre de la fonction originale insérée en (C). De cette façon, %esp est réinitialisée à son ancienne valeur (celle avant d'arriver à l'étape C). À cet instant, la pile ne contient plus le cadre de pile de la fonction originale puisque celui-ci a été écrasé lors de l'étape (A). E - Modifier l'adresse de retour de la fonction originale sur la pile. Sur les processeurs INTEL, les adresses de retour de fonction sont enregistrés sur la pile, ce qui n'est pas une très bonne idée du point de vue sécurité ;-). Avec cette modification, on retourne où on veut (vers le code du crochet) après l'exécution de la fonction originale. On appelle alors la fonction originale. Au retour, le code de crochetage récupère le contrôle. Regardons ce processus attentivement : -> Premièrement, nous récupérons l'%eip courant et l'enregistrons dans %esi (l'étiquette "end" fait référence à du code que l'on peut facilement identifier lors de l'étape E5). On utilise toujours cette astuce pour du code indépendant de sa position. 1. jmp end begin: pop %esi -> Puis nous récupérons l'ancienne adresse de retour qui se trouve à 4(%esp) et on l'enregistre dans %eax. 2. movl 4(%esp), %eax -> On utilise cette adresse de retour sauvegardée comme un décalage de 4 octets à la fin du code de crochetage (voir le pointeur NULL dans l'étape G), afin de pouvoir revenir au bon endroit à la fin du processus de crochetage. 3. movl %eax, 20(%esi) -> On modifie l'adresse de retour de la fonction originale pour pouvoir y retourner juste après l'instruction 'call begin'. 4. movl %esi, 4(%esp) movl $0x00, %eax -> On appelle la fonction originale. L'étiquette 'end' est utilisée à l'étape 1, et l'étiquette 'begin' fait référence au code qui suit immédiatement l'instruction 'jmp end' (toujours à l'étape 1). La fonction originale se terminera juste après l'instruction 'call begin' puisque nous avons modifié son adresse de retour. 5. jmp *%eax end: call begin F - Retour au code de crochetage. On réaffecte les 7 octets malicieux dans le code de la fonction originale. Ces octets ont été réinitialisé à leur valeur originale avant l'appel à la fonction, on doit donc crocheter de nouveau la fonction (comme dans l'étape A). Cette étape est remplacée par des symboles de non-opération (NOP) si le crochet est à usage unique (non permanent), pour que les 7 octets de notre saut malicieux indirect (étape A) ne soient pas copiés. Cette étape est vraiment proche de l'étape (B) puisqu'elle utilise le même mécanisme de copie (en utilisant les instructions movs* de manière répétitive), donc pour plus d'explications, voir cette étape. Les décalages NULL dans le code doivent être fixés lors de la création du crochet : - le premier (le tampon source) est remplacé par l'adresse du tampon des octets malicieux. - le second (le tampon destination) est remplacé par l'adresse du point d'entrée de la fonction originale. movl $0x00, %esi movl $0x00, %edi push %ds pop %es cld xor %ecx, %ecx movb $0x07, %cl rep movsb G - On utilise l'adresse de retour originale (enregistrée lors de l'étape E2) et on retourne à la fonction appelante originale. Le décalage NULL que l'on peut voir (*) doit être fixé à l'étape E2 avec l'adresse de retour de la fonction originale. La valeur de %ecx est alors empilée sur la pile de sorte que la prochaine instruction 'ret' l'utilisera comme s'il s'agissait d'un %eip enregistré sur la pile. Cela renvoie à l'endroit (correct) original. movl $0x00, %ecx * pushl %ecx ret --[ 4 - Utilisation de la bibliothèque ----[ 4.1 - L'API L'API de LKH est assez simple à utiliser : hook_t *khook_create(int addr, int mask); Crée un crochet sur l'adresse 'addr'. Accepte aussi le type par défaut (HOOK_PERMANENT [permanent] ou HOOK_SINGLESHOT [à usage unique]), l'état par défaut (HOOK_ENABLED [activé] ou HOOK_DISABLED [désactivé]) et le mode par défaut (HOOK_AGGRESSIVE [intrusif] ou HOOK_DISCRETE [discret]). Le type, l'état et le mode sont combinés à l'aide de l'opération logique OU dans le paramètre 'mask'. void khook_destroy(hook_t *h); Désactive, supprime et libère les ressources associées à un crochet. int khook_add_entry(hook_t *h, char *routine, int range); Ajoute un fonction de rappel au crochet, au rang 'range'. Renvoie -1 si le rang fourni est invalide. Sinon, renvoie 0. int khook_remove_entry(hook_t *h, int range); Supprime la fonction de rappel mise dans l'emplacement 'range'. Renvoie -1 si le rang fourni est invalide. Sinon, renvoie 0. void khook_purge(hook_t *h); Supprime toutes les fonctions de rappel associées à ce crochet. int khook_set_type(hook_t *h, char type); Change le type du crochet 'h'. Le type peut être HOOK_PERMANENT (le code de crochetage est exécuté à chaque appel de la fonction) ou HOOK_SINGLESHOT (le code de crochetage est exécuté seulement pour un détournement, ensuite, le crochet est retiré proprement). int khook_set_state(hook_t *h, char state); Change l'état du crochet 'h'. L'état peur être HOOK_ENABLED (le crochet est activé) ou HOOK_DISABLED (le crochet est désactivé). int khook_set_mode(hook_t *h, char mode); Change le mode du crochet 'h'. Ce mode peut être HOOK_AGGRESSIVE (le crochet n'appelle pas la fonction détournée) ou HOOK_DISCRETE (le crochet appelle la fonction détournée après avoir exécuté les fonctions de rappel). Une partie du code du crochet est remplacée par des symboles de non-opération si le crochet est agressif (étape E). int khook_set_attr(hook_t *h, int mask); Change le mode, l'état et/ou le type en utilisant un unique appel de fonction. Cette fonction retourne 0 en cas de succès ou -1 si le masque spécifié contient des options incompatibles. Il est possible d'ajouter et de supprimer des entrées à volonté, quel que soit l'état, le type et le mode utilisé par le crochet. ----[ 4.2 - Résolution des symboles du noyau Une fonction de résolution des symboles a été ajoutée à LKH, permettant d'accéder aux valeurs des fonctions exportées. int ksym_lookup(char *name); Cela renvoi NULL si le symbole reste non-résolu. Cette recherche peut résoudre les symboles contenus dans la section __ksymtab du noyau, une liste exhaustive de ces symboles est affichées lors de l'exécution de la commande 'ksyms -a' : bash-2.03# ksyms -a | wc -l 1136 bash-2.03# wc -l /boot/System.map 14647 /boot/System.map bash-2.03# elfsh -f /usr/src/linux/vmlinux -s # affiche les sections [SECTION HEADER TABLE] (nil) --- foffset: (nil) 0 bytes [*Unknown*] (...) 0xc024d9e0 a-- __ex_table foffset: 0x14e9e0 5520 bytes [Program data] 0xc024ef70 a-- __ksymtab foffset: 0x14ff70 9008 bytes [Program data] 0xc02512a0 aw- .data foffset: 0x1522a0 99616 bytes [Program data] (...) (nil) --- .shstrtab foffset: 0x1ad260 216 bytes [String table] (nil) --- .symtab foffset: 0x1ad680 245440 bytes [Symbol table] (nil) --- .strtab foffset: 0x1e9540 263805 bytes [String table] [END] En fait, la section __ksymtab mappée dans la mémoire ne contient pas tous les symboles du noyau que l'on désire détourner. D'un autre côté, la section non-mappée .symtab est assurément plus grande (245440 octets contre 9008 octets). Lorsqu'on utilise 'ksyms', l'appel système __NR_query_module (ou __NR_get_kernel_syms pour les anciens noyaux) est utilisé en interne. Cet appel système n'a accès qu'à la section __ksymtab puisque la table complète des symboles du noyau contenu dans __ksymtab n'est pas chargée en mémoire. La solution pour accéder à la table complète des symboles consiste à prendre les octets de décalages dans le fichier System.map (on le crée en utilisant la commande `nm -a vmlinux > System.map`). bash-2.03# ksyms -a | grep sys_fork bash-2.03# grep sys_fork /boot/System.map c0105898 T sys_fork bash-2.03# #define SYS_FORK 0xc0105898 if ((s = khook_create((int) SYS_FORK, HOOK_PERMANENT, HOOK_ENABLED)) == NULL) KFATAL("init_module: impossible de poser le crochet sur la fonction *sys_fork* ! \n", -1); khook_add_entry(s, (int) fork_callback, 0); #undef SYS_FORK Pour les systèmes qui n'ont pas de fichier System.map ni d'images de noyau décompressée (vmlinux), il est possible de décompresser le fichier vmlinuz (attention, ce n'est pas un format gzip standard ! Pour des informations utiles à ce sujet, voir [3]) et de créer manuellement un nouveau fichier System.map. Une autre manière de résoudre les symboles non-exportés du noyau peut être la recherche par statistique : l'analyse des références dans le code hexadécimal du noyau peut nous permettre de prédire les valeurs des symboles (en allant chercher les instructions call ou jmp). La difficulté de cet outil serait la portabilité, puisque les codes du noyau changent d'une version à l'autre. N'oubliez pas de changer SYS_FORK en y mettant votre propre valeur de décalage de sys_fork. ----[ 4.3 - Les rouages de LKH : l'objet hook_t Jetons un coup d'œil à la structure hook_t (l'entité crochet dans la mémoire) : typedef struct s_hook { int addr; int offset; char saved_bytes[7]; char voodoo_bytes[7]; char hook[HOOK_SIZE]; char cache1[CACHE1_SIZE]; char cache2[CACHE2_SIZE]; } hook_t; h->addr Adresse de la fonction originale, utilisée pour activer ou désactiver le crochet. h->offset Ce champ contient le décalage d'octet à partir de h->addr où commencer l'écrasement pour mettre en place le détournement. Sa valeur est 3 ou 0, cela dépend si la fonction possède un cadre de pile ou non. h->original_bytes Les sept octets écrasés de la fonction originale. h->voodoo_bytes Les sept octets que l'on doit placer au début de la fonction pour la rediriger (contient le code d'un saut indirect détaillé dans l'étape A du paragraphe 3). h->hook Tampon des codes-opération contenant le code du crochetage, où on insère les références des fonctions de rappel en utilisant khook_add_entry() . Les tampons cache1 et cache2 sont utilisés pour sauvegarder une partie du code du crochet lorsque le mode HOOK_AGGRESSIVE est activé (comme il faut remplir l'appel de la fonction original avec des codes de non-opération, il est nécessaire de sauvegarder ce code, pour finalement remettre en mode discret le crochet). Chaque fois qu'un crochet est créé, une instance de hook_t est déclarée et allouée. Il faut créer un crochet par fonction à détourner si on le souhaite. ----[ 5 - Tester le code Rendez-vous d'abord sur http://www.devhell.org/~mayhem/ pour obtenir une version récente du code. Le paquet (version 1.1) est fourni à la fin de cet article. Il faut juste faire #include "lkh.c" et jouer ! Dans ce module donné en exemple et qui utilise LKH, on veut détourner : - la fonction hijack_me(), ici on peut vérifier que le passage des paramètres s'est bien déroulé et que leur modification, à travers les fonctions de rappel, est correct. - la fonction schedule(), détournement unique [SINGLESHOT]. - la fonction sys_fork(), détournement permanent [PERMANENT]. ------[ 5.1 - Chargement du module bash-2.03# make load insmod lkh.o Test d'un crochet permanent, agressif et activé avec 3 fonctions de rappel: A in hijack_one = 0 -OK- B in hijack_one = 1 -OK- A in hijack_zero = 1 -OK- B in hijack_zero = 2 -OK- A in hijack_two = 2 -OK- B in hijack_two = 3 -OK- -------------------- Test d'un crochet désactivé: A in HIJACKME!!! = 10 -OK- B in HIJACKME!!! = 20 -OK- -------------------- Appel à hijack_me après la destruction du crochet A in HIJACKME!!! = 1 -OK- B in HIJACKME!!! = 2 -OK- SCHEDULING! ------[ 5.2 - Amusons-nous un peu bash-2.05# ls FORKING! Makefile doc example.c lkh.c lkh.h lkh.o user user.c user.h user.o bash-2.05# pwd /usr/src/coding/LKH (N'affiche pas FORKING! puisque pwd est une commande intégré au terminal :) bash-2.05# make unload FORKING! rmmod lkh; LKH unloaded - sponsorized by the /dev/hell crew! bash-2.05# ls Makefile doc example.c lkh.c lkh.h lkh.o user user.c user.h user.o bash-2.05# On peut voir "FORKING!" à chaque appel de la fonction sys_fork() du noyau (le crochet est permanent) et "SCHEDULING!" lors du premier appel de la fonction schedule() du noyau (puisqu'il s'agit d'un crochet à usage unique, la fonction schedule() est détournée seulement une fois, puis le crochet est retiré). Voici le code commenté pour cette démo: ------[ 5.3 - Le code /* ** Code de démonstration de LKH, développé et testé sous Linux x86 2.4.5 ** ** Le code de la bibliothèque est joint. ** Pour les mises à jour, voir http://www.devhell.org/~mayhem/. ** ** Cette archive contient un code mode utlisateur [userland] (qu'on peut lancer ** à partir de GDB), le module noyau LKH et son fichier d'inclusion, et ** ce fichier (lkm-example.c). ** ** Toutes suggestions {et, ou} rapport de bogue sont les bienvenues ! LKH 1.2 ** est déjà en développement. ** ** Un merci particulier à b1nf pour le contrôle de la qualité ;) ** Un grand hourra pour kraken, continue comme ça, mec, tu fais du bon ** boulot avec psh! ** ** Merci à csp0t (il n'y a qu'un mot pour te décrire : *elite*) ** et cma4 (la puissance d'EPITECH, mon pro favori du noyau win32) ** ** Gros bisous à l'équipe devhell (r1x et nitrogen fux0r) ** Lightman, Gab et Xfred de chx-labs (arrêtez de fumer, bande de drogués ;) ** ** Merci à l'équipe de Phrack et particulièrement skyper pour son ** super soutien. Le Havre en force ! Case mais oui je t'aime ;) */ #include "lkh.c" int hijack_me(int a, int b); /* fonction détournée */ int hijack_zero(void *ptr); /* première fonction de rappel */ int hijack_one(void *ptr); /* deuxième fonction de rappel */ int hijack_two(void *ptr); /* troisième fonction de rappel */ void hijack_fork(void *ptr); /* fonction de rappel sys_fork */ void hijack_schedule(void *ptr); /* fonction de rappel schedule */ static hook_t *h = NULL; static hook_t *i = NULL; static hook_t *j = NULL; int init_module() { int ret; printk(KERN_ALERT "Change the SYS_FORK value then remove the return \n"); return (-1); /* ** Crée les crochets */ #define SYS_FORK 0xc010584c j = khook_create(SYS_FORK , HOOK_PERMANENT | HOOK_ENABLED | HOOK_DISCRETE); #undef SYS_FORK h = khook_create(ksym_lookup("hijack_me") , HOOK_PERMANENT | HOOK_ENABLED | HOOK_AGGRESSIVE); i = khook_create(ksym_lookup("schedule") , HOOK_SINGLESHOT | HOOK_ENABLED | HOOK_DISCRETE); /* ** Encore une autre vérification */ if (!h || !i || !j) { printk(KERN_ALERT "Impossible de crocheter les fonctions du noyau \n"); return (-1); } /* ** Ajout de quelques fonctions de rappel pour les fonctions sys_fork et schedule */ khook_add_entry(i, (int) hijack_schedule, 0); khook_add_entry(j, (int) hijack_fork, 0); /* ** Test du crochet hijack_me */ printk(KERN_ALERT "LKH: permanent, agressif et activé avec 3 fonctions de rappel:\n"); khook_add_entry(h, (int) hijack_zero, 1); khook_add_entry(h, (int) hijack_one, 0); khook_add_entry(h, (int) hijack_two, 2); ret = hijack_me(0, 1); printk(KERN_ALERT "--------------------\n"); printk(KERN_ALERT "Test d'un crochet désactivé:\n"); khook_set_state(h, HOOK_DISABLED); ret = hijack_me(10, 20); khook_destroy(h); printk(KERN_ALERT "------------------\n"); printk(KERN_ALERT "Appel à hijack_me après la destruction du crochet\n"); hijack_me(1, 2); return (0); } void cleanup_module() { khook_destroy(i); khook_destroy(j); printk(KERN_ALERT "LKH unloaded - sponsorized by the /dev/hell crew!\n"); } /* ** Fonction à détourner */ int hijack_me(int a, int b) { printk(KERN_ALERT "A in HIJACKME!!! = %u \t -OK- \n", a); printk(KERN_ALERT "B in HIJACKME!!! = %u \t -OK- \n", b); return (42); } /* ** Première fonction de rappel pour hijack_me() */ int hijack_zero(void *ptr) { int *a; int *b; a = ptr; b = a + 1; printk(KERN_ALERT "A in hijack_zero = %u \t -OK- \n", *a); printk(KERN_ALERT "B in hijack_zero = %u \t -OK- \n", *b); (*b)++; (*a)++; return (0); } /* ** Deuxième fonction de rappel pour hijack_me() */ int hijack_one(void *ptr) { int *a; int *b; a = ptr; b = a + 1; printk(KERN_ALERT "A in hijack_one = %u \t -OK- \n", *a); printk(KERN_ALERT "B in hijack_one = %u \t -OK- \n", *b); (*a)++; (*b)++; return (1); } /* ** Troisième fonction de rappel pour hijack_me() */ int hijack_two(void *ptr) { int *a; int *b; a = ptr; b = a + 1; printk(KERN_ALERT "A in hijack_two = %u \t -OK- \n", *a); printk(KERN_ALERT "B in hijack_two = %u \t -OK- \n", *b); (*a)++; (*b)++; return (2); } /* ** Fonction de rappel pour schedule() (symbole exporté du noyau) */ void hijack_schedule(void *ptr) { printk(KERN_ALERT "SCHEDULING! \n"); } /* ** Fonction de rappel pour sys_fork() (symbole non exporté du noyau) */ void hijack_fork(void *ptr) { printk(KERN_ALERT "FORKING! \n"); } --[ 6 - Références [1] Détournement de fonction du noyau http://www.big.net.au/~silvio/ [2] Manuel des développeurs INTEL http://developers.intel.com/design/pentiu m4/manuals/ [3] Rouages internes du noyau Linux http://www.linuxdoc.org/guides.html |=[ EOF ]=---------------------------------------------------------------=|