Dans un précédent article, je vous entretenais sur la manière d'appeler des fonctions d'un executable sans connaitre leur nom, et en utilisant les capacités du resolver de liens ld.so et ses amis dl{open|sym}. Si cette astuce fonctionne parfaitement bien dans le cas d'un binaire compilé dynamiquement, il n'en va pas de même pour un binaire compilé statiquement.
En réalité, aucune fonction de haut niveau de nous viendra en aide dans ce cas de figure, et nous devrons nous même simuler le fonctionnement de ld.so, mais avec des symboles statiques. C'est grace au format même de notre binaire que nous réaliseront cette opération, en effet, en parcourant et associant le format ELF à quelques pointeurs bien placés, nous obtenons strictement le même résultat qu'avec l'aide d'un dlsym().

Ce tutoriel est en partie inspiré de ftrace et du code de ld.elf-so (NetBSD).

La première étape consiste bien evidemment à associer notre fichier binaire à un descripteur de fichiers. Nous créons par ailleurs, pour l'ensemble de notre manipulation, une fonction sl_sym() :

void *
sl_sym(char *path, char *symname)
{
    int         fd;
    struct stat sb;
 
    if ((fd = open(path, O_RDONLY)) < 0)
        err("open");
    if ((fstat(fd, &sb)) < 0)
        err("fstat");

La liste des include dont nous aurons besoin pour notre experience sont les suivants :

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <elf.h>

Verifions que le fichier en question a une taille au moins égale à une en-tête ELF :

    if (sb.st_size < sizeof(Elf_Ehdr)) {
        fprintf(stderr, "\"%s\" is not an ELF object\n", path);
        return NULL;
    }

Nous pouvons alors procéder à l'operation cruciale de cette methode, le mapping du fichier en memoire à l'aide de mmap(2), en ayant préalablement déclaré en tant que void * une variable destinée à pointer l'espace mappé :

    map = mmap(NULL, sb.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
        (off_t)0);
    if (map == MAP_FAILED)
        err("mmap");
 
    close(fd);

Je vous renvoie au man de la fonction mmap(2) pour le descriptif complet des parametres et flags utilisés.
Si le mapping s'est bien passé, nous pouvons fermer le descripteur de fichier fd.

Déclarons maintenant une variable de type “ELF header” qui va pointer sur l'espace mappé, permettant ainsi, en utilisant les champs de la structure Elf_Ehdr de retrouver les informations concernant le programme à disséquer. En outre, muni de ce point d'entrée, nous nous assurerons qu'il s'agit bien d'un binaire au format ELF.

    ehdr = (Elf_Ehdr *)map;
 
    if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
        fprintf(stderr, "\"%s\" is not an ELF object\n", path);
        return NULL;
    }

Arrive le moment clé de l'aventure: permettre l'accès aux tables de symbole. Pour ce faire, la première étape consiste à pointer vers l'adresse des sections headers en ayant préalablement déclaré une variable de type Elf_Shdr :

    shdr = (Elf_Shdr *)(map + ehdr->e_shoff);

shdr pointe ici vers “l'adresse de map + l'offset jusqu'aux en-têtes de sections”.
Il “suffit” alors de parcourir ces en-têtes jusqu'à trouver la table des symboles statiques, SHT_SYMTAB. Une fois trouvée, on mémorise le pointeur vers le premier symbole, la taille de cette table, et un pointeur vers la liste des noms (ASCII) de ces symboles.
Pour stocker ces informations, on déclare :

  • Deux variables de type Elf_Sym *, symtbl et symtbl_end
  • Une variable de type char *, strtbl
    for (i = 0; i < ehdr->e_shnum; i++) {
        /* find symbol table */
        if (shdr[i].sh_type == SHT_SYMTAB) {
            symtbl = (Elf_Sym *)(map + shdr[i].sh_offset);
            symtbl_end = (Elf_Sym *)((char *)symtbl + shdr[i].sh_size);
            strtbl = (char *)(map + shdr[shdr[i].sh_link].sh_offset);
        }
    }

On verifie qu'on a bien initialisé le pointeur vers la table des symboles :

    if (symtbl == NULL) {
        fprintf(stderr, "symbol table not found !\n");
        return NULL;
    }

Il est alors possible de parcourir cette table afin d'y trouver le symbole souhaité. On crée pour cela une petite fonction qui retournera un pointeur vers le symbole trouvé, et par conséquent, la fonction désirée :

Elf_Sym *
sl_lookup(Elf_Sym *symtbl, Elf_Sym *symtbl_end, char *strtbl, char *symname)
{
    Elf_Sym *psymt;
 
    for (psymt = symtbl; psymt < symtbl_end; psymt++)
        if (strcmp(&strtbl[psymt->st_name], symname) == 0)
            return psymt;
 
    return NULL;
}

Dans cette fonction, on parcourt l'ensemble de la table des symboles jusqu'à trouver un nom (ASCII) de symbole correspondant à symname. Si le symbole est trouvé, il est retourné. On ajoute l'appel à cette fonction dans notre fonction principale :

    symp = sl_lookup(symtbl, symtbl_end, strtbl, symname);
    if (symp == NULL)
        return NULL; /* symbol not found */
 
    return (void *)symp->st_value;

Qui retournera simplement le pointeur vers la fonction recherchée.

C'est prêt ! il ne reste plus, dans un programme principal, qu'à déclarer un pointeur sur fonction, et effectuer une recherche de symbole comme on le ferait à l'aide de dlsym() :

int
pwet()
{   
    printf("bla\n");
}
 
int
main(int argc, char *argv[])
{   
    int (*f)(void);
 
    f = sl_sym("binaire", "pwet");
    if (f != NULL)
        (f)();
}

On n'oubliera pas de déclarer les prototypes des fonctions créées :

void *sl_sym(char *, char *);
Elf_Sym *sl_lookup(Elf_Sym *, Elf_Sym *, char *, char *);

Voici le Makefile BSD associé :

PROG= binaire
SRCS= toto.c slsym.c

CFLAGS+= -DELFSIZE=32

.include <bsd.prog.mk>

On n'oubliera pas de créer une fonction qui liberera le mapping du binaire en mémoire, par exemple à l'aide d'une telle fonction :

void
sl_close(void *map, size_t len)
{   
    if (munmap(map, len) <0)
        err("munmap");
}

Et de s'extasier devant la beauté des outils qui ont été mis à notre disposition par les divinités à 25 bras.

c/static_symbols_with_elf.txt · Last modified: 2010/01/12 13:29 (external edit)
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0