Get loaded address of a ELF binary, dlopen is not working as expected

后端 未结 1 930
旧时难觅i
旧时难觅i 2020-12-28 09:29

I\'m trying to get the loaded address of an ELF binary, but dlopen doesn\'t work as expected:

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


        
相关标签:
1条回答
  • 2020-12-28 10:29

    On Linux, dlopen doesn't return the address where the ELF binary was loaded. It returns struct link_map instead, which has .l_addr member. So you'll want something like:

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

    However, despite what comment in /usr/include/link.h says, .l_addr is actually not a load address either. Instead, it's the difference between where ELF image was linked to load, and where it was actually loaded.

    For non-PIE main executable, that difference is always 0. For non-prelinked shared library, that difference is always the load address (because non-prelinked ELF shared libraries are linked to load at address 0).

    So how do you find the base address of the main executable? The easiest method is to use this code (linked into main executable):

    #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;
      const char *cb = (const char *)&callback;
      const char *base = (const char *)info->dlpi_addr;
      const ElfW(Phdr) *first_load = NULL;
    
      for (j = 0; j < info->dlpi_phnum; j++) {
        const ElfW(Phdr) *phdr = &info->dlpi_phdr[j];
    
        if (phdr->p_type == PT_LOAD) {
          const char *beg = base + phdr->p_vaddr;
          const char *end = beg + phdr->p_memsz;
    
          if (first_load == NULL) first_load = phdr;
          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",
                   base + first_load->p_vaddr, first_load->p_vaddr, info->dlpi_addr);
            return 1;
          }
        }
      }
      return 0;
    }
    
    int
    main(int argc, char *argv[])
    {
      dl_iterate_phdr(callback, NULL);
      exit(EXIT_SUCCESS);
    }
    

    Here is what you should see on 32-bit system:

    $ 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
    

    (The last address: 0xf779a000 will vary from run to run if you have address randomization enabled (as you should)).

    0 讨论(0)
提交回复
热议问题