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.
Version txt, cet article a été traduit très gentillement par Thiébaud Weksteen et relu par mayhem.
==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 ]=---------------------------------------------------------------=|