2013-10-18 8 views
8

sto cercando di ottenere l'indirizzo caricato di un binario ELF, but dlopen doesn't lavoro come previsto:Ottenere indirizzo caricato di un binario ELF, dlopen non sta funzionando come previsto

void *elf = (char *)dlopen (0, RTLD_NOW); 
printf ("%p\n", elf); 
sleep (100); 

Esso stampa 0xb772d918, ma da quello che /proc/1510/maps dice, che non punta all'indirizzo carico del dlfn binario, ma il ld-2.15.so,

08048000-08049000 r-xp 00000000 fc:00 1379  /root/dlfn 
08049000-0804a000 r--p 00000000 fc:00 1379  /root/dlfn 
0804a000-0804b000 rw-p 00001000 fc:00 1379  /root/dlfn 
b7550000-b7552000 rw-p 00000000 00:00 0 
b7552000-b76f5000 r-xp 00000000 fc:00 9275  /lib/i386-linux-gnu/libc-2.15.so 
b76f5000-b76f7000 r--p 001a3000 fc:00 9275  /lib/i386-linux-gnu/libc-2.15.so 
b76f7000-b76f8000 rw-p 001a5000 fc:00 9275  /lib/i386-linux-gnu/libc-2.15.so 
b76f8000-b76fb000 rw-p 00000000 00:00 0 
b76fb000-b76fe000 r-xp 00000000 fc:00 9305  /lib/i386-linux-gnu/libdl-2.15.so 
b76fe000-b76ff000 r--p 00002000 fc:00 9305  /lib/i386-linux-gnu/libdl-2.15.so 
b76ff000-b7700000 rw-p 00003000 fc:00 9305  /lib/i386-linux-gnu/libdl-2.15.so 
b7708000-b770b000 rw-p 00000000 00:00 0 
b770b000-b770c000 r-xp 00000000 00:00 0   [vdso] 
b770c000-b772c000 r-xp 00000000 fc:00 9299  /lib/i386-linux-gnu/ld-2.15.so 
b772c000-b772d000 r--p 0001f000 fc:00 9299  /lib/i386-linux-gnu/ld-2.15.so 
b772d000-b772e000 rw-p 00020000 fc:00 9299  /lib/i386-linux-gnu/ld-2.15.so 
bfc34000-bfc55000 rw-p 00000000 00:00 0   [stack] 

Quindi, altro che l'analisi /proc/pid/maps, c'è un modo per recuperare l'indirizzo caricato di un binario ELF? (0x0848000 in questo caso)

risposta

11

Su Linux, dlopen non restituisce l'indirizzo in cui è stato caricato il file binario ELF. Restituisce invece struct link_map, che ha il membro .l_addr. Così si vorrà qualcosa di simile:

struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW); 
printf("%p\n", lm->l_addr); 

Tuttavia, nonostante ciò che commentare in /usr/include/link.h dice, .l_addr non è in realtà un indirizzo di carico sia. Al contrario, è la differenza tra dove e l'immagine ELF è stata collegata al caricamento e dove è stata effettivamente caricata.

Per l'eseguibile principale non-PIE, tale differenza è sempre 0. Per la libreria condivisa non prelinkata, tale differenza è sempre l'indirizzo di caricamento (poiché le librerie condivise ELF non prelinkate sono collegate per caricare all'indirizzo 0).

Quindi, come si trova l'indirizzo di base dell'eseguibile principale? Il metodo più semplice è quello di utilizzare questo codice (legato in eseguibile principale):

#ifndef _GNU_SOURCE 
#define _GNU_SOURCE 
#endif 

#include <link.h> 
#include <stdio.h> 
#include <stdlib.h> 

static int 
callback(struct dl_phdr_info *info, size_t size, void *data) 
{ 
    int j; 
    for (j = 0; j < info->dlpi_phnum; j++) { 
    if (info->dlpi_phdr[j].p_type == PT_LOAD) { 
     const char *beg = (const char*) info->dlpi_addr + info->dlpi_phdr[j].p_vaddr; 
     const char *end = beg + info->dlpi_phdr[j].p_memsz; 
     const char *cb = (const char *)&callback; 
     if (beg < cb && cb < end) { 
     // Found PT_LOAD that "covers" callback(). 
     printf("ELF header is at %p, image linked at 0x%zx, relocation: 0x%zx\n", 
       beg, info->dlpi_phdr[j].p_vaddr, info->dlpi_addr); 
     return 1; 
     } 
     return 0; 
    } 
    } 
    return 0; 
} 

int 
main(int argc, char *argv[]) 
{ 
    dl_iterate_phdr(callback, NULL); 
    exit(EXIT_SUCCESS); 
} 

Ecco cosa si dovrebbe vedere il sistema a 32 bit:

$ gcc -g t.c -ldl -m32 && ./a.out 
ELF header is at 0x8048000, image linked at 0x8048000, relocation: 0x0 
$ gcc -g t.c -ldl -m32 -pie -fPIE && ./a.out 
ELF header is at 0xf779a000, image linked at 0x0, relocation: 0xf779a000 

(l'ultimo indirizzo: 0xf779a000 varieranno da corri a correre se hai attivato la randomizzazione degli indirizzi (come dovresti)).

+0

Interessante ... che non è stato nemmeno menzionato in 'dlopen (3)'. Grazie! – daisy

+1

Molto utile. Se si desidera eseguire questa operazione su Android, dl_iterate_phdr è disponibile dalla versione della piattaforma 21 in poi. – sheltond

Problemi correlati