==Phrack Inc.== Volume 0x0b, Issue 0x3f, Phile #0x07 of 0x14 |=-------=[ Playing Games With Kernel Memory ... FreeBSD Style ]=--------=| |=-----------------------------------------------------------------------=| |=-----------------=[ Joseph Kong ]=-----------------=| |=--------------------------=[ July 8, 2005 ]=---------------------------=| --[ Sommaire 1.0 - Introduction 2.0 - Trouver les appels système 3.0 - Comprendre les déclarations call et l'injection de code machine 4.0 - Allocation de mémoire noyau 5.0 - Assembler tout cela 6.0 - Remarques de conclusion 7.0 - Références --[ 1.0 - Introduction L'interface mémoire du noyau, ou interface kvm, a été introduite pour la première fois dans SunOS. Bien qu'elle existe depuis un certain temps, beaucoup de monde la considère toujours comme plutôt obscure. Cet article documente l'usage basique de la librairie d'accès aux données du noyau (Kernel Data Access Library, libkvm), et explorera plusieurs moyens d'utiliser libkvm (/dev/kmem) dans le but d'altérer le comportement d'un système FreeBSD en cours de fonctionnement. Des connaissance de niveau moyen concernant le kernel hacking sous FreeBSD, ainsi qu'une bonne connaissance du C et de l'assembleur x86 (syntaxe AT&T) sont requises pour comprendre le contenu de cet article. Cet article a été écrit depuis le point de vue d'un système FreeBSD 5.4 Stable. Note : bien que les techniques décrites dans cet article ont été explorées dans d'autres articles (voir Références), elles viennent toujours du point de vue de GNU/*/Linux ou de Windows. Je n'ai personnellement connaissance que d'un seul autre texte touchant au informations décrites ici. Ce texte, écrit par Stephanie Wehner et intitulé "Fun and Games with FreeBSD Kernel Modules", expliquait certaines des choses que l'on peut faire avec libkvm. Considérant le fait qu'on peut faire bien plus, et que la documentation concernant libkvm est rare (à part les pages de manuel et le code source), j'ai décidé d'écrire cet article. --[ 2.0 Trouver les appels système Note : cette section est extrêment basique, si vous connaissez déjà bien les fonctions de libkvm lisez le paragraphe suivant puis passez à la section suivante. Stephanie Wehner a écrit un programme nommé checkcall, qui vérifie si sysent[CALL] a été trafiqué et, si c'est bien le cas, la remet en place à sa position d'origine. Pour aider au debugging durant les dernières sections de cet article, nous allons utiliser la fonctionnalité de recherche d'appel système de checkcall. Ce qui suit est une version diminuée de checkcall, avec juste la fonction de recherche d'appel système. C'est également un bon exemple pour apprendre la base de libkvm. Une explication ligne par ligne des fonctions de libkvm apparaît après le listing de code source. find_syscall.c: /* * Takes two arguments: the name of a syscall and corresponding number, * and reports the location in memory where the syscall is located. * * If you enter the name of a syscall with an incorrect syscall number, * the output will be fubar. Too lazy to implement a check * * Based off of Stephanie Wehner's checkcall.c,v 1.1.1.1 * * find_syscall.c,v 1.0 2005/05/20 */ #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; u_int32_t addr; int callnum; struct sysent call; struct nlist nl[] = { { NULL }, { NULL }, { NULL }, }; /* Check for the correct number of arguments */ if(argc != 3) { printf("Usage:\n%s " " \n\n", argv[0]); printf("See /usr/src/sys/sys/syscall.h for syscall numbers" " \n"); exit(0); } /* Find the syscall */ nl[0].n_name = "sysent"; nl[1].n_name = argv[1]; callnum = atoi(argv[2]); printf("Finding syscall %d: %s\n\n", callnum, argv[1]); /* Initialize kernel virtual memory access */ kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if(kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } /* Find the addresses */ if(kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } if(!nl[0].n_value) { fprintf(stderr, "ERROR: %s not found (fubar?)\n" , nl[0].n_name); exit(-1); } else { printf("%s is 0x%x at 0x%x\n", nl[0].n_name, nl[0].n_type , nl[0].n_value); } if(!nl[1].n_value) { fprintf(stderr, "ERROR: %s not found\n", nl[1].n_name); exit(-1); } /* Calculate the address */ addr = nl[0].n_value + callnum * sizeof(struct sysent); /* Print out location */ if(kvm_read(kd, addr, &call, sizeof(struct sysent)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } else { printf("sysent[%d] is at 0x%x and will execute function" " located at 0x%x\n", callnum, addr, call.sy_call); } if(kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); } Il y a cinq fonctions de libkvm inclues dans le programme ci-dessus ; il s'agit de : kvm_openfiles kvm_nlist kvm_geterr kvm_read kvm_close kvm_openfiles : Basiquement, kvm_openfiles initialise l'accès à la mémoire virtuelle du noyau, et retourne un descripteur à utiliser lors des appels ultérieurs à la librairie kvm. Dans find_syscall la syntaxe était comme suit : kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); kd est utilisé pour stocker le decripteur retourné, si après l'appel kd est égal à NULL alors une erreur s'est produite. Les trois premiers arguments correspondent respectivement à const char *execfile, const char *corefile, et const char *swapfiles. Malgré tout, pour nos activités ils ne sont pas nécessaires, donc NULL. Le quatrième argument indique que nous voulons un accès en lecture/écriture. Le cinquième argument indique dans quel tampon placer tout message d'erreur, nous en reparlerons plus tard. kvm_nlist : La page de manuel déclare que kvm_nlist récupère les entrées de la table des symboles indiquées par l'argument name de la liste (struct nlist). Les membres de struct nlist qui nous intéresse sont comme suit : char *n_name; /* symbol name (in memory) */ unsigned long n_value; /* address of the symbol */ Dans find_syscall, avant l'appel à kvm_nlist un tableau de struct nlist a été mis en place de la manière suivante : struct nlist nl[] = { { NULL }, { NULL }, { NULL }, }; nl[0].n_name = "sysent"; nl[1].n_name = argv[1]; La syntaxe de l'appel à kvm_nlist est comme suit : kvm_nlist(kd, nl) Cela remplit le membre n_value de chaque élément du tableau nl par l'adresse mémoire de début correspondant à la valeur dans n_name. En d'autres termes nous connaissons à présent la localisation en mémoire de sysent et de l'appel système fournit par l'utilisateur (argv[1]). nl a été initialisée avec trois éléments parce que kvm_nlist attend en deuxième argument un tableau de structures nlist terminé par NULL. kvm_geterr : Comme déclaré dans la page de manuel cette fonction retourne une chaîne décrivant la condition d'erreur la plus récente. Si vous examinez le listing de code source vous verrez que kvm_geterr est appelée après chaque fonction libkvm, excepté kvm_openfiles. kvm_openfiles utilise sa propre forme de rapport d'erreur parce ce que kvm_geterr requiert un decripteur en argument, qui n'existerait pas si kvm_openfiles n'a pas encore été appelée. Un exemple d'usage de kvm_geterr suit : fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); kvm_read: Cette fonction est utilisée pour lire la mémoire virtuelle du noyau. Dans find_syscall la syntaxe était la suivante : kvm_read(kd, addr, &call, sizeof(struct sysent)) Le premier argument est le descripteur. Le deuxième est l'adresse depuis laquelle lire. Le troisième argument est l'endroit en espace utilisateur où stocker les données lues. Le quatrième argument est le nombre d'octets à lire. kvm_close : Cette fonction casse la connexion entre le pointeur et l'adresse mémoire virtuelle établie avec kvm_openfiles. Dans find_syscall cette fonction était appelée comme suit : kvm_close(kd) Ce qui suit est une explication algorithmique de find_sycall.c : 1. S'assurer que l'utilisateur a fourni un nom d'appel système et un numéro. (Pas de vérification d'erreur, juste vérifier si on a bien deux arguments.) 2. Mettre en place le tableau de structures nlist de la manière appropriée. 3. Initilaiser l'accès à la mémoire virtuelle du noyau (kvm_openfiles). 4. Trouver l'adresse de sysent et l'appel système fourni par l'utilisateur (kvm_nlist). 5. Calculer la localisation de l'appel système dans sysent. 6. Copier la structure sysent de l'appel système depuis l'espace noyau vers l'espace utilisateur (kvm_read). 7. Afficher la localisation de l'appel système dans la structure sysent et la localisation de la fonction exécutée. 8. Fermer le descripteur (kvm_close). Pour vérifier que la sortie de find_syscall est précise, on peut utiliser ddb comme suit : Note : La sortie suivante a été modifiée pour se tenir à la limitation des 75 caractères par ligne. [---------------------------------------------------------] ghost@slavetwo:~#ls find_syscall.c ghost@slavetwo:~#gcc -o find_syscall find_syscall.c -lkvm ghost@slavetwo:~#ls find_syscall find_syscall.c ghost@slavetwo:~#sudo ./find_syscall Password: Usage: ./find_syscall See /usr/src/sys/sys/syscall.h for syscall numbers ghost@slavetwo:~#sudo ./find_syscall mkdir 136 Finding syscall 136: mkdir sysent is 0x4 at 0xc06dc840 sysent[136] is at 0xc06dcc80 and will execute function located at 0xc0541900 ghost@slavetwo:~#KDB: enter: manual escape to debugger [thread pid 12 tid 100004 ] Stopped at kdb_enter+0x32: leave db> examine/i 0xc0541900 mkdir: pushl %ebp db> mkdir+0x1: movl %esp,%ebp db> c ghost@slavetwo:~# [---------------------------------------------------------] --[ 3.0 - Comprendre les déclarations call et l'injection de code machine En Assembleur x86 une déclaration Call est une instruction de transfert de contrôle utilisée pour appeler une procédure. Il existe deux types de déclarations Call : Proche (Near) et Lointaine (Far), pour les besoins de cet article on n'a besoin de comprendre que les Calls Proches. Le code suivant illustre les détails d'une déclaration Call Proche (en syntaxe Intel) : 0200 BB1295 MOV BX,9512 0203 E8FA00 CALL 0300 0206 B82F14 MOV AX,142F Dans le morceau de code ci-dessus, lorsque que le pointeur d'instruction (IP) arrive à 0203 il saute à 0300. La représentation hexadécimale de CALL est E8, mais FA00 n'est pas 0300. 0x300 - 0x206 = 0xFA. Dans un appel proche, l'adresse (IP) de l'instruction suivant le call est sauvegardée sur la pile, donc la procédure appelée sait où retourner. Ceci explique pourquoi l'opérande du call de cet exemple est 0xFA00 et non 0x300. C'est un point important qui entrera en jeu plus tard. L'une des choses les plus amusantes que l'on peut faire avec les fonctions de libkvm est patch-er la mémoire noyau. Comme toujours nous commençons avec un exemple très simple... Hello World! Ce qui suit est un KLD qui ajoute un appel système fonctionnant comme un programme Hello World! hello.c: /* * Prints "FreeBSD Rox!" 10 times * */ #include #include #include #include #include #include #include /* * The function for implementing the syscall. */ static int hello (struct thread *td, void *arg) { printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); printf ("FreeBSD Rox!\n"); return 0; } /* * The `sysent' for the new syscall */ static struct sysent hello_sysent = { 0, /* sy_narg */ hello /* sy_call */ }; /* * The offset in sysent where the syscall is allocated. */ static int offset = 210; /* * The function called at load/unload. */ static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : printf ("syscall loaded at %d\n", offset); break; case MOD_UNLOAD : printf ("syscall unloaded from %d\n", offset); break; default : error = EOPNOTSUPP; break; } return error; } SYSCALL_MODULE(hello, &offset, &hello_sysent, load, NULL); Voici le programme en espace utilisateur pour le KLD ci-dessus : interface.c: #include #include #include #include int main(int argc, char **argv) { return syscall(210); } Si nous compilons le KLD ci-dessus en utilisant un Makefile standard, que nous le chargeons, et le lançons ensuite, nous obtenons des sorties très ennuyeuses. Pour rendre cet appel système moins ennuyeux nous pouvons utiliser le programme suivant. Comme auparavant une explication de toutes les nouvelles fonctions et nouveaux concepts apparaît après le listing du code source. test_call.c: /* * Test understanding of call statement: * Operand for call statement is the difference between the called function * and the address of the instruction following the call statement. * * Tested on syscall hello. Normally prints out "FreeBSD Rox!" 10 times, * after patching only prints it out once. * * test_call.c,v 2.1 2005/06/15 */ #include #include #include #include #include #include /* * Offset of string to be printed * Starting at the beginning of the syscall hello */ #define OFFSET_1 0xed /* * Offset of instruction following call statement */ #define OFFSET_2 0x12 /* * Replacement code */ unsigned char code[] = "\x55" /* push %ebp */ "\x89\xe5" /* mov %esp,%ebp */ "\x83\xec\x04" /* sub $0x4,%esp */ "\xc7\x04\x24\x00\x00\x00\x00" /* movl $0,(%esp) */ "\xe8\x00\x00\x00\x00" /* call printf */ "\xc9" /* leave */ "\x31\xc0" /* xor %eax,%eax */ "\xc3" /* ret */ "\x8d\xb4\x26\x00\x00\x00\x00" /* lea 0x0(%esi),%esi */ "\x8d\xbc\x27\x00\x00\x00\x00"; /* lea 0x0(%edi),%edi */ int main(int argc, char *argv[]) { char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; u_int32_t offset_1; u_int32_t offset_2; struct nlist nl[] = { { NULL }, { NULL }, { NULL }, }; /* Initialize kernel virtual memory access */ kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if(kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } /* Find the address of hello and printf */ nl[0].n_name = "hello"; nl[1].n_name = "printf"; if(kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } if(!nl[0].n_value) { fprintf(stderr, "ERROR: Symbol %s not found\n" , nl[0].n_name); exit(-1); } if(!nl[1].n_value) { fprintf(stderr, "ERROR: Symbol %s not found\n" , nl[1].n_name); exit(-1); } /* Calculate the correct offsets */ offset_1 = nl[0].n_value + OFFSET_1; offset_2 = nl[0].n_value + OFFSET_2; /* Set the code to contain the correct addresses */ *(unsigned long *)&code[9] = offset_1; *(unsigned long *)&code[14] = nl[1].n_value - offset_2; /* Patch hello */ if(kvm_write(kd, nl[0].n_value, code, sizeof(code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } printf("Luke, I am your father!\n"); /* Close kd */ if(kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); } La seule fonction libkvm incluse dans le programme ci-dessus et n'ayant pas été discutée avant est kvm_write. kvm_write : Cette fonction est utilisée pour écrire dans la mémoire virtuelle du noyau. Dans test_call la syntaxe était la suivante : kvm_write(kd, nl[0].n_value, code, sizeof(code)) Le premier argument est le descripteur. Le deuxième argument est l'adresse où commencer à écrire. Le troisième argument est l'endroit en espace utilisateur où lire. Le quatrième argument est le nombre d'octets à lire. Le code de remplacement (code machine) dans test_call a été généré à l'aide de objdump. [---------------------------------------------------------] ghost@slavetwo:~#objdump -DR hello.ko | less hello.ko: file format elf32-i386-freebsd Disassembly of section .hash: 00000094 <.hash>: 94: 11 00 adc %eax,(%eax) 96: 00 00 add %al,(%eax) OUTPUT SNIPPED Disassembly of section .text: 00000500 : 500: 55 push %ebp 501: 89 e5 mov %esp,%ebp 503: 83 ec 04 sub $0x4,%esp 506: c7 04 24 ed 05 00 00 movl $0x5ed,(%esp) 509: R_386_RELATIVE *ABS* 50d: e8 fc ff ff ff call 50e 50e: R_386_PC32 printf 512: c7 04 24 ed 05 00 00 movl $0x5ed,(%esp) 515: R_386_RELATIVE *ABS* 519: e8 fc ff ff ff call 51a 51a: R_386_PC32 printf 51e: c7 04 24 ed 05 00 00 movl $0x5ed,(%esp) 521: R_386_RELATIVE *ABS* 525: e8 fc ff ff ff call 526 526: R_386_PC32 printf OUTPUT SNIPPED 57e: c9 leave 57f: 31 c0 xor %eax,%eax 581: c3 ret 582: 8d b4 26 00 00 00 00 lea 0x0(%esi),%esi 589: 8d bc 27 00 00 00 00 lea 0x0(%edi),%edi [---------------------------------------------------------] Note : votre sortie peut varier selon la version et les drapeaux de votre compilateur. En comparant la sortie de la section texte avec le code machine dans test_call on peut voir qu'ils sont à peu près identiques, à part la mise en place de neuf appels de plus à printf. Une chose importante à prendre en note est quand objdump rapporte quelque chose comme étant relatif : il y a movl $0x5ed,(%esp) (met en place la chaîne à afficher) et call printf. Ce qui nous amène à ... Dans test_call il y a deux déclaration #define, qui sont : #define OFFSET_1 0xed #define OFFSET_2 0x12 La première représente l'adresse de la chaîne à afficher relativement au début de l'appel système hello (le numéro est dérivé de la sortie de objdump), tandis que la seconde représente l'offset de l'instruction suivant l'appel à printf dans le code machine. Plus loin dans test_call il y a ces quatre déclarations : /* Calculate the correct offsets */ offset_1 = nl[0].n_value + OFFSET_1; offset_2 = nl[0].n_value + OFFSET_2; /* Set the code to contain the correct addresses */ *(unsigned long *)&code[9] = offset_1; *(unsigned long *)&code[14] = nl[1].n_value - offset_2; De part les commentaires, ce que font ces quatre déclarations devrait être évident. code[9] est la section du code machine où l'adresse de la chaîne à afficher est stockée. code[14] est l'opérande de la déclaration call ; l'adresse de printf moins l'adresse de la déclaration suivante. Ce qui suit est la sortie avant et après le lancement de test_call : [---------------------------------------------------------] ghost@slavetwo:~#ls Makefile hello.c interface.c test_call.c ghost@slavetwo:~#make Warning: Object directory not changed from original /usr/home/ghost @ -> /usr/src/sys machine -> /usr/src/sys/i386/include OUTPUT SNIPPED J% objcopy % hello.kld ld -Bshareable -d -warn-common -o hello.ko hello.kld objcopy --strip-debug hello.ko ghost@slavetwo:~#sudo kldload ./hello.ko Password: syscall loaded at 210 ghost@slavetwo:~#gcc -o interface interface.c ghost@slavetwo:~#./interface FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! FreeBSD Rox! ghost@slavetwo:~#gcc -o test_call test_call.c -lkvm ghost@slavetwo:~#sudo ./test_call Luke, I am your father! ghost@slavetwo:~#./interface FreeBSD Rox! ghost@slavetwo:~# [---------------------------------------------------------] --[ 4.0 - Allocation de mémoire noyau Etre en mesure de patch-er la mémoire du noyau a ses limites, car vous n'avez pas beaucoup de place pour jouer. Etre capable d'allouer de la mémoire noyau soulage de ce problème. Ce qui suit est un KLD qui fait juste ça : kmalloc.c: /* * Module to allow a non-privileged user to allocate kernel memory * * kmalloc.c,v 2.0 2005/06/01 * Date Modified 2005/06/14 */ #include #include #include #include #include #include #include #include /* * Arguments for kmalloc */ struct kma_struct { unsigned long size; unsigned long *addr; }; struct kmalloc_args { struct kma_struct *kma; }; /* * The function for implementing kmalloc. */ static int kmalloc (struct thread *td, struct kmalloc_args *uap) { int error = 1; struct kma_struct kts; if(uap->kma) { MALLOC(kts.addr, unsigned long*, uap->kma->size , M_TEMP, M_NOWAIT); error = copyout(&kts, uap->kma, sizeof(kts)); } return (error); } /* * The `sysent' for kmalloc */ static struct sysent kmalloc_sysent = { 1, /* sy_narg */ kmalloc /* sy_call */ }; /* * The offset in sysent where the syscall is allocated. */ static int offset = 210; /* * The function called at load/unload. */ static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : uprintf ("syscall loaded at %d\n", offset); break; case MOD_UNLOAD : uprintf ("syscall unloaded from %d\n", offset); break; default : error = EOPNOTSUPP; break; } return error; } SYSCALL_MODULE(kmalloc, &offset, &kmalloc_sysent, load, NULL); Voici le programme en espace utilisateur pour le KLD ci-dessus : interface.c: /* * User Program To Interact With kmalloc module */ #include #include #include #include struct kma_struct { unsigned long size; unsigned long *addr; }; int main(int argc, char **argv) { struct kma_struct kma; if(argc != 2) { printf("Usage:\n%s \n", argv[0]); exit(0); } kma.size = (unsigned long)atoi(argv[1]); return syscall(210, &kma); } En utilisant les techniques/fonctions décrites dans les deux sections précédentes et l'algorithme suivant (inventé par Silvio Cesare), on peut allouer de la mémoire noyau sans utiliser de KLD. Le kmalloc depuis l'espace utilisateur de Silvio Cesare : 1. Obtenir l'adresse d'un appel système. 2. Ecrire une fonction qui allouera de la mémoire noyau. 3. Sauvegarder sizeof(notre_fonction) octets d'un appel système. 4. Ecraser un appel système par notre_fonction. 5. Appeler notre appel système nouvellement réécrit. 6. Restaurer l'appel système. test_kmalloc.c: /* * Allocate kernel memory from user-space * * Algorithm to allocate kernel memory is as follows: * * 1. Get address of mkdir * 2. Overwrite mkdir with function that calls man 9 malloc() * 3. Call mkdir through int $0x80 * This will cause the kernel to run the new "mkdir" syscall, which will * call man 9 malloc() and pass out the address of the newly allocated * kernel memory * 4. Restore mkdir syscall * * test_kmalloc.c,v 2.0 2005/06/24 */ #include #include #include #include #include #include #include #include /* * Offset of instruction following call statements * Starting at the beginning of the function kmalloc */ #define OFFSET_1 0x3a #define OFFSET_2 0x56 /* * kmalloc function code */ unsigned char code[] = "\x55" /* push %ebp */ "\xba\x01\x00\x00\x00" /* mov $0x1,%edx */ "\x89\xe5" /* mov %esp,%ebp */ "\x53" /* push %ebx */ "\x83\xec\x14" /* sub $0x14,%esp */ "\x8b\x5d\x0c" /* mov 0xc(%ebp),%ebx */ "\x8b\x03" /* mov (%ebx),%eax */ "\x85\xc0" /* test %eax,%eax */ "\x75\x0b" /* jne 20 */ "\x83\xc4\x14" /* add $0x14,%esp */ "\x89\xd0" /* mov %edx,%eax */ "\x5b" /* pop %ebx */ "\xc9" /* leave */ "\xc3" /* ret */ "\x8d\x76\x00" /* lea 0x0(%esi),%esi */ "\xc7\x44\x24\x08\x01\x00\x00" /* movl $0x1,0x8(%esp) */ "\x00" "\xc7\x44\x24\x04\x00\x00\x00" /* movl $0x0,0x4(%esp) */ "\x00" "\x8b\x00" /* mov (%eax),%eax */ "\x89\x04\x24" /* mov %eax,(%esp) */ "\xe8\xfc\xff\xff\xff" /* call 36 */ "\x89\x45\xf8" /* mov %eax,0xfffffff8(%ebp) */ "\xc7\x44\x24\x08\x08\x00\x00" /* movl $0x8,0x8(%esp) */ "\x00" "\x8b\x03" /* mov (%ebx),%eax */ "\x89\x44\x24\x04" /* mov %eax,0x4(%esp) */ "\x8d\x45\xf4" /* lea 0xfffffff4(%ebp),%eax */ "\x89\x04\x24" /* mov %eax,(%esp) */ "\xe8\xfc\xff\xff\xff" /* call 52 */ "\x83\xc4\x14" /* add $0x14,%esp */ "\x89\xc2" /* mov %eax,%edx */ "\x5b" /* pop %ebx */ "\xc9" /* leave */ "\x89\xd0" /* mov %edx,%eax */ "\xc3"; /* ret */ /* * struct used to store kernel address */ struct kma_struct { unsigned long size; unsigned long *addr; }; int main(int argc, char **argv) { int i = 0; char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; u_int32_t offset_1; u_int32_t offset_2; struct nlist nl[] = {{ NULL },{ NULL },{ NULL },{ NULL },{ NULL },}; unsigned char origcode[sizeof(code)]; struct kma_struct kma; if(argc != 2) { printf("Usage:\n%s \n", argv[0]); exit(0); } /* Initialize kernel virtual memory access */ kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if(kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } /* Find the address of mkdir, M_TEMP, malloc, and copyout */ nl[0].n_name = "mkdir"; nl[1].n_name = "M_TEMP"; nl[2].n_name = "malloc"; nl[3].n_name = "copyout"; if(kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } for(i = 0; i < 4; i++) { if(!nl[i].n_value) { fprintf(stderr, "ERROR: Symbol %s not found\n" , nl[i].n_name); exit(-1); } } /* Calculate the correct offsets */ offset_1 = nl[0].n_value + OFFSET_1; offset_2 = nl[0].n_value + OFFSET_2; /* Set the code to contain the correct addresses */ *(unsigned long *)&code[44] = nl[1].n_value; *(unsigned long *)&code[54] = nl[2].n_value - offset_1; *(unsigned long *)&code[82] = nl[3].n_value - offset_2; /* Save mkdir syscall */ if(kvm_read(kd, nl[0].n_value, origcode, sizeof(code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Patch mkdir */ if(kvm_write(kd, nl[0].n_value, code, sizeof(code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Allocate kernel memory */ kma.size = (unsigned long)atoi(argv[1]); syscall(136, &kma); printf("Address of kernel memory: 0x%x\n", kma.addr); /* Restore mkdir */ if(kvm_write(kd, nl[0].n_value, origcode, sizeof(code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Close kd */ if(kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); } En utilisant ddb on peut vérifier les résultats du programme ci-dessus comme suit : [---------------------------------------------------------] ghost@slavetwo:~#ls test_kmalloc.c ghost@slavetwo:~#gcc -o test_kmalloc test_kmalloc.c -lkvm ghost@slavetwo:~#sudo ./test_kmalloc Usage: ./test_kmalloc ghost@slavetwo:~#sudo ./test_kmalloc 10 Address of kernel memory: 0xc2580870 ghost@slavetwo:~#KDB: enter: manual escape to debugger [thread pid 12 tid 100004 ] Stopped at kdb_enter+0x32: leave db> examine/x 0xc2580870 0xc2580870: 70707070 db> 0xc2580874: 70707070 db> 0xc2580878: dead7070 db> c ghost@slavetwo:~# [---------------------------------------------------------] --[ 5.0 - Assembler tout cela Savoir comment patch-er et allouer la mémoire du noyau donne beaucoup de liberté. Cette dernière section démontrera comment appliquer un hook d'appel système en utilisant les techniques décrites dans les sections précédentes. Habituellement les hooks d'appels système sous FreeBSD se font en changeant la sysent et en la faisant pointer sur une autre fonction, mais nous ne feront pas ça. A la place nous utiliserons l'algorithme suivant (avec quelques entorses mineures, montrées plus loin) : 1. Copier l'appel système que nous voulons hook-er. 2. Allouer la mémoire noyau (en utilisant la technique décrite dans la section précédente). 3. Placer une nouvelle routine dans l'espace d'adressage nouvellement alloué. 4. Ecraser les 7 premiers octets de l'appel système par une instruction pour sauter vers notre nouvelle routine. 5. Exécuter la nouvelle routine, plus les x premiers octets de l'appel système (cette étape sera clarifiée plus tard). 6. Revenir à appel système + offset, où offset est égal à x. En volant une idée de pragmatic (de THC) nous allons hook-er mkdir pour afficher un message de debug. Ce qui suit est le KLD utilisé en conjonction à objdump pour extraire le code machine requit pour le hook d'appel système. hacked_mkdir.c: /* * mkdir call hook * * Prints a simple debugging message */ #include #include #include #include #include #include #include #include #include #include /* The hacked system call */ static int hacked_mkdir (struct proc *p, struct mkdir_args *uap) { uprintf ("MKDIR SYSCALL : %s\n", uap->path); return 0; } /* The sysent for the hacked system call */ static struct sysent hacked_mkdir_sysent = { 1, /* sy_narg */ hacked_mkdir /* sy_call */ }; /* The offset in sysent where the syscall is allocated */ static int offset = NO_SYSCALL; /* The function called at load/unload */ static int load (struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD : uprintf ("syscall loaded at %d\n", offset); break; case MOD_UNLOAD : uprintf ("syscall unloaded from %d\n", offset); break; default : error = EINVAL; break; } return error; } SYSCALL_MODULE(hacked_mkdir, &offset, &hacked_mkdir_sysent, load, NULL); Voici un programme d'exemple qui hook-e mkdir pour afficher un simple message de debug. Comme toujours une explication de tout nouveau concept apparaît après le listing du code source. test_hook.c: /* * Intercept mkdir system call, printing out a debug message before * executing mkdir. * * Algorithm is as follows: * 1. Copy mkdir syscall upto but not including \xe8. * 2. Allocate kernel memory. * 3. Place new routine in newly allocated address space. * 4. Overwrite first 7 bytes of mkdir syscall with an instruction to jump * to new routine. * 5. Execute new routine, plus the first x bytes of mkdir syscall. * Where x is equal to the number of bytes copied from step 1. * 6. Jump back to mkdir syscall + offset. * Where offset is equal to the location of \xe8. * * test_hook.c,v 3.0 2005/07/02 */ #include #include #include #include #include #include #include #include /* * Offset of instruction following call statements * Starting at the beginning of the function kmalloc */ #define KM_OFFSET_1 0x3a #define KM_OFFSET_2 0x56 /* * kmalloc function code */ unsigned char km_code[] = "\x55" /* push %ebp */ "\xba\x01\x00\x00\x00" /* mov $0x1,%edx */ "\x89\xe5" /* mov %esp,%ebp */ "\x53" /* push %ebx */ "\x83\xec\x14" /* sub $0x14,%esp */ "\x8b\x5d\x0c" /* mov 0xc(%ebp),%ebx */ "\x8b\x03" /* mov (%ebx),%eax */ "\x85\xc0" /* test %eax,%eax */ "\x75\x0b" /* jne 20 */ "\x83\xc4\x14" /* add $0x14,%esp */ "\x89\xd0" /* mov %edx,%eax */ "\x5b" /* pop %ebx */ "\xc9" /* leave */ "\xc3" /* ret */ "\x8d\x76\x00" /* lea 0x0(%esi),%esi */ "\xc7\x44\x24\x08\x01\x00\x00" /* movl $0x1,0x8(%esp) */ "\x00" "\xc7\x44\x24\x04\x00\x00\x00" /* movl $0x0,0x4(%esp) */ "\x00" "\x8b\x00" /* mov (%eax),%eax */ "\x89\x04\x24" /* mov %eax,(%esp) */ "\xe8\xfc\xff\xff\xff" /* call 36 */ "\x89\x45\xf8" /* mov %eax,0xfffffff8(%ebp) */ "\xc7\x44\x24\x08\x08\x00\x00" /* movl $0x8,0x8(%esp) */ "\x00" "\x8b\x03" /* mov (%ebx),%eax */ "\x89\x44\x24\x04" /* mov %eax,0x4(%esp) */ "\x8d\x45\xf4" /* lea 0xfffffff4(%ebp),%eax */ "\x89\x04\x24" /* mov %eax,(%esp) */ "\xe8\xfc\xff\xff\xff" /* call 52 */ "\x83\xc4\x14" /* add $0x14,%esp */ "\x89\xc2" /* mov %eax,%edx */ "\x5b" /* pop %ebx */ "\xc9" /* leave */ "\x89\xd0" /* mov %edx,%eax */ "\xc3"; /* ret */ /* * Offset of instruction following call statements * Starting at the beginning of the function hacked_mkdir */ #define HA_OFFSET_1 0x2f /* * hacked_mkdir function code */ unsigned char ha_code[] = "\x4d" /* M */ "\x4b" /* K */ "\x44" /* D */ "\x49" /* I */ "\x52" /* R */ "\x20" /* sp */ "\x53" /* S */ "\x59" /* Y */ "\x53" /* S */ "\x43" /* C */ "\x41" /* A */ "\x4c" /* L */ "\x4c" /* L */ "\x20" /* sp */ "\x3a" /* : */ "\x20" /* sp */ "\x25" /* % */ "\x73" /* s */ "\x0a" /* nl */ "\x00" /* null */ "\x55" /* push %ebp */ "\x89\xe5" /* mov %esp,%ebp */ "\x83\xec\x08" /* sub $0x8,%esp */ "\x8b\x45\x0c" /* mov 0xc(%ebp),%eax */ "\x8b\x00" /* mov (%eax),%eax */ "\xc7\x04\x24\x0d\x00\x00\x00" /* movl $0xd,(%esp) */ "\x89\x44\x24\x04" /* mov %eax,0x4(%esp) */ "\xe8\xfc\xff\xff\xff" /* call 17 */ "\x31\xc0" /* xor %eax,%eax */ "\x83\xc4\x08" /* add $0x8,%esp */ "\x5d"; /* pop %ebp */ /* * jump code */ unsigned char jp_code[] = "\xb8\x00\x00\x00\x00" /* movl $0,%eax */ "\xff\xe0"; /* jmp *%eax */ /* * struct used to store kernel address */ struct kma_struct { unsigned long size; unsigned long *addr; }; int main(int argc, char **argv) { int i = 0; char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; u_int32_t km_offset_1; u_int32_t km_offset_2; u_int32_t ha_offset_1; struct nlist nl[] = { { NULL },{ NULL },{ NULL },{ NULL },{ NULL },{ NULL},{ NULL }, }; unsigned long diff; int position; unsigned char orig_code[sizeof(km_code)]; struct kma_struct kma; /* Initialize kernel virtual memory access */ kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if(kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } /* Find the address of mkdir, M_TEMP, malloc, copyout, uprintf, and kern_rmdir */ nl[0].n_name = "mkdir"; nl[1].n_name = "M_TEMP"; nl[2].n_name = "malloc"; nl[3].n_name = "copyout"; nl[4].n_name = "uprintf"; nl[5].n_name = "kern_rmdir"; if(kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } for(i = 0; i <= 5; i++) { if(!nl[i].n_value) { fprintf(stderr, "ERROR: Symbol %s not found\n" , nl[i].n_name); exit(-1); } } /* Determine size of mkdir syscall */ diff = nl[5].n_value - nl[0].n_value; unsigned char mk_code[diff]; /* Save a copy of mkdir syscall */ if(kvm_read(kd, nl[0].n_value, mk_code, diff) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Determine position of 0xe8 */ for(i = 0; i < (int)diff; i++) { if(mk_code[i] == 0xe8) { position = i; } } /* Calculate the correct offsets for kmalloc */ km_offset_1 = nl[0].n_value + KM_OFFSET_1; km_offset_2 = nl[0].n_value + KM_OFFSET_2; /* Set the km_code to contain the correct addresses */ *(unsigned long *)&km_code[44] = nl[1].n_value; *(unsigned long *)&km_code[54] = nl[2].n_value - km_offset_1; *(unsigned long *)&km_code[82] = nl[3].n_value - km_offset_2; /* Save mkdir syscall */ if(kvm_read(kd, nl[0].n_value, orig_code, sizeof(km_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Replace mkdir with kmalloc */ if(kvm_write(kd, nl[0].n_value, km_code, sizeof(km_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Allocate kernel memory */ kma.size = (unsigned long)sizeof(ha_code) + (unsigned long)position + (unsigned long)sizeof(jp_code); syscall(136, &kma); /* Restore mkdir */ if(kvm_write(kd, nl[0].n_value, orig_code, sizeof(km_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Calculate the correct offsets for hacked_mkdir */ ha_offset_1 = (unsigned long)kma.addr + HA_OFFSET_1; /* Set the ha_code to contain the correct addresses */ *(unsigned long *)&ha_code[34] = (unsigned long)kma.addr; *(unsigned long *)&ha_code[43] = nl[4].n_value - ha_offset_1; /* Place hacked_mkdir routine into kernel memory */ if(kvm_write(kd, (unsigned long)kma.addr, ha_code, sizeof(ha_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Place mk_code into kernel memory */ if(kvm_write(kd, (unsigned long)kma.addr + (unsigned long)sizeof(ha_code) - 1, mk_code, position) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Set the jp_code to contain the correct address */ *(unsigned long *)&jp_code[1] = nl[0].n_value + (unsigned long)position; /* Place jump code into kernel memory */ if(kvm_write(kd, (unsigned long)kma.addr + (unsigned long)sizeof(ha_code) - 1 + (unsigned long)position , jp_code, sizeof(jp_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Set the jp_code to contain the correct address */ *(unsigned long *)&jp_code[1] = (unsigned long)kma.addr + 0x14; if(kvm_write(kd, nl[0].n_value, jp_code, sizeof(jp_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } printf("I love the PowerGlove. It's so bad!\n"); /* Close kd */ if(kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); } Les commentaires déclarent que l'algorithme de ce programme est le suivant : 1. Copier l'appel système mkdir jusqu'au \xe8 non inclus. 2. Allouer de la mémoire noyau. 3. Placer la nouvelle routine dans l'espace d'adressage nouvellement alloué. 4. Ecraser les 7 permiers octets de l'appel système mkdir par une instruction pour sauter vers la nouvelle routine. 5. Exécuter la nouvelle routine, plus les x premiers octets de l'appel système mkdir, où x est égal au nombre d'octets copiés à l'étape 1. 6. Sauter à nouveau, vers mkdir + offset où offset est égal à la localisation du \xe8. La raison de la copie de mkdir jusqu'au \xe8 non inclus est que sur différentes "builds" (versions) de FreeBSD le désassemblage de l'appel système mkdir est différent. On ne peut donc pas déterminer une localisation statique où sauter pour revenir. Mais sur toutes les "builds" de FreeBSD mkdir fait appel à kern_mkdir, nous choisissons donc de sauter vers ce point. Ce qui suit illustre ceci. [---------------------------------------------------------] ghost@slavezero:~#nm /boot/kernel/kernel | grep mkdir c047c560 T devfs_vmkdir c0620e40 t handle_written_mkdir c0556ca0 T kern_mkdir c0557030 T mkdir c071d57c B mkdirlisthd c048a3e0 t msdosfs_mkdir c05e2ed0 t nfs4_mkdir c05d8710 t nfs_mkdir c05f9140 T nfsrv_mkdir c06b4856 r nfsv3err_mkdir c063a670 t ufs_mkdir c0702f40 D vop_mkdir_desc c0702f64 d vop_mkdir_vp_offsets ghost@slavezero:~#nm /boot/kernel/kernel | grep kern_rmdir c0557060 T kern_rmdir ghost@slavezero:~#objdump -d --start-address=0xc0557030 --stop-address=0xc0557060 /boot/kernel/kernel | less /boot/kernel/kernel: file format elf32-i386-freebsd Disassembly of section .text: c0557030 : c0557030: 55 push %ebp c0557031: 31 c9 xor %ecx,%ecx c0557033: 89 e5 mov %esp,%ebp c0557035: 83 ec 10 sub $0x10,%esp c0557038: 8b 55 0c mov 0xc(%ebp),%edx c055703b: 8b 42 04 mov 0x4(%edx),%eax c055703e: 89 4c 24 08 mov %ecx,0x8(%esp) c0557042: 89 44 24 0c mov %eax,0xc(%esp) c0557046: 8b 02 mov (%edx),%eax c0557048: 89 44 24 04 mov %eax,0x4(%esp) c055704c: 8b 45 08 mov 0x8(%ebp),%eax c055704f: 89 04 24 mov %eax,(%esp) c0557052: e8 49 fc ff ff call c0556ca0 c0557057: c9 leave c0557058: c3 ret c0557059: 8d b4 26 00 00 00 00 lea 0x0(%esi),%esi ghost@slavezero:~# [---------------------------------------------------------] [---------------------------------------------------------] ghost@slavetwo:~#nm /boot/kernel/kernel | grep mkdir c046f680 T devfs_vmkdir c0608fd0 t handle_written_mkdir c05415d0 T kern_mkdir c0541900 T mkdir c074a9bc B mkdirlisthd c047d270 t msdosfs_mkdir c05c7160 t nfs4_mkdir c05bcfd0 t nfs_mkdir c05db750 T nfsrv_mkdir c06a2676 r nfsv3err_mkdir c06216a0 t ufs_mkdir c06fef40 D vop_mkdir_desc c06fef64 d vop_mkdir_vp_offsets ghost@slavetwo:~#nm /boot/kernel/kernel | grep kern_rmdir c0541930 T kern_rmdir ghost@slavetwo:~#objdump -dR --start-address=0xc0541900 --stop-address=0xc0541930 /boot/kernel/kernel | less /boot/kernel/kernel: file format elf32-i386-freebsd Disassembly of section .text: c0541900 : c0541900: 55 push %ebp c0541901: 89 e5 mov %esp,%ebp c0541903: 83 ec 10 sub $0x10,%esp c0541906: 8b 55 0c mov 0xc(%ebp),%edx c0541909: 8b 42 04 mov 0x4(%edx),%eax c054190c: c7 44 24 08 00 00 00 movl $0x0,0x8(%esp) c0541913: 00 c0541914: 89 44 24 0c mov %eax,0xc(%esp) c0541918: 8b 02 mov (%edx),%eax c054191a: 89 44 24 04 mov %eax,0x4(%esp) c054191e: 8b 45 08 mov 0x8(%ebp),%eax c0541921: 89 04 24 mov %eax,(%esp) c0541924: e8 a7 fc ff ff call c05415d0 c0541929: c9 leave c054192a: c3 ret c054192b: 90 nop c054192c: 8d 74 26 00 lea 0x0(%esi),%esi ghost@slavetwo:~# [---------------------------------------------------------] La sortie suivante a été générée depuis deux différentes "builds" de FreeBSD 5.4. Comme on peut clairement le voir, le dump du désassemblage de mkdir est différent pour chacune. Dans test_hook l'adresse de kern_mkdir est recherchée, et c'est parce qu'en mémoire kern_rmdir vient juste après mkdir, donc son adresse est la borne de fin de mkdir. Le code machine pour le hook d'appel système est comme suit : unsigned char ha_code[] = "\x4d" /* M */ "\x4b" /* K */ "\x44" /* D */ "\x49" /* I */ "\x52" /* R */ "\x20" /* sp */ "\x53" /* S */ "\x59" /* Y */ "\x53" /* S */ "\x43" /* C */ "\x41" /* A */ "\x4c" /* L */ "\x4c" /* L */ "\x20" /* sp */ "\x3a" /* : */ "\x20" /* sp */ "\x25" /* % */ "\x73" /* s */ "\x0a" /* nl */ "\x00" /* null */ "\x55" /* push %ebp */ "\x89\xe5" /* mov %esp,%ebp */ "\x83\xec\x08" /* sub $0x8,%esp */ "\x8b\x45\x0c" /* mov 0xc(%ebp),%eax */ "\x8b\x00" /* mov (%eax),%eax */ "\xc7\x04\x24\x0d\x00\x00\x00" /* movl $0xd,(%esp) */ "\x89\x44\x24\x04" /* mov %eax,0x4(%esp) */ "\xe8\xfc\xff\xff\xff" /* call 17 */ "\x31\xc0" /* xor %eax,%eax */ "\x83\xc4\x08" /* add $0x8,%esp */ "\x5d"; /* pop %ebp */ Les 20 premiers octets sont pour la chaîne à afficher ; à cause de cela, lorsque nous sautons sur cette fonction nous devons commencer à un offset de 0x14, comme illustré depuis cette ligne de code : *(unsigned long *)&jp_code[1] = (unsigned long)kma.addr + 0x14; Les trois dernières déclarations dans le code machine de hacked_mkdir mettent à zéro le registre eax, nettoyent la pile, et restaurent le registre ebp. Ceci est fait de façon à ce que lorsque mkdir est exécuté cela se passe comme si rien n'était arrivé. Une chose dont il faut se souvenir à propos des tableaux de caractères en C est qu'ils sont tous terminés par NULL. Par exemple si nous déclarons la variable suivante, unsigned char example[] = "\x41"; sizeof(example) retournera 2. C'est la raison pour laquelle dans test_hook nous soustrayons 1 de sizeof(ha_code), sinon nous écririons au mauvais endroit. Ce qui suit est la sortie avant et après le lancement de test_hook : [---------------------------------------------------------] ghost@slavetwo:~#ls test_hook.c ghost@slavetwo:~#gcc -o test_hook test_hook.c -lkvm ghost@slavetwo:~#mkdir before ghost@slavetwo:~#ls -F before/ test_hook* test_hook.c ghost@slavetwo:~#sudo ./test_hook Password: I love the PowerGlove. It's so bad! ghost@slavetwo:~#mkdir after MKDIR SYSCALL : after ghost@slavetwo:~#ls -F after/ before/ test_hook* test_hook.c ghost@slavetwo:~# [---------------------------------------------------------] On aurait également pu utiliser find_syscall et ddb pour vérifier les réseultats de test_hook. --[ 6.0 Remarques de conclusions Etre en mesure de patch-er et d'allouer de la mémoire noyau donne beaucoup de pouvoir sur un système. Tous les exemples de cet article sont triviaux car il était dans mon intention de montrer la manière de faire, pas l'oeuvre d'art. De toute façon, d'autres auteurs ont de meilleures idées que moi à propos de ce qu'on peut faire (voir les Références). Je voudrais prendre le temps de m'excuser si certaine de mes explications ne sont pas claires, espérons que la lecture du code source et l'étude des sorties produites par les programmes réparerons mes torts. Enfin, j'aimerais remercier Silvio Cesare, pragmatic et Stephanie Wehner, pour l'inspiration et les idées. --[ 7.0 - Références [ Internet ] [1] Silvio Cesare, "Runtime Kernel Kmem Patching" http://reactor-core.org/runtime-kernel-patching.html [2] devik & sd, "Linux on-th-fly kernel patching without LKM" http://www.phrack.org/show.php?p=58&a=7 [3] pragmatic, "Attacking FreeBSD with Kernel Modules" http://www.thc.org/papers/bsdkern.html [4] Andrew Reiter, "Dynamic Kernel Linker (KLD) Facility Programming Tutorial" http://ezine.daemonnews.org/200010/blueprints.html [5] Stephanie Wehner, "Fun and Games with FreeBSD Kernel Modules" http://www.r4k.net/mod/fbsdfun.html [ Books ] [6] Muhammad Ali Mazidi & Janice Gillispie Mazidi, "The 80x86 IBM PC And Compatible Computers: Assembly Language, Design, And Interfacing" (Prentice Hall) |=[ EOF ]=---------------------------------------------------------------=| Traduction : [DegenereScience]DecereBrain, le 04/10/2007, 19:28. Relecture : Rayan, le 08/10/2007. Notez que cet article est à considérer comme une "ébauche" de l'excellant livre qu'à écrit son auteur, Joseph Kong, intitulé "Designing BSD Rootkits", publié en Avril 2007 par No Starch Press (http://www.nostarch.com/frameset.php?startat=rootkits), et dont une traduction en langue française doit être publiée d'ici fin 2007 (enfin, il paraît...).