2017-05-24 217 views
0

我正在尝试使用C语言读取二进制文件中使用的共享库的名称。到目前为止,我有以下程序test.c在ELF文件中读取.dynstr(strtab)时发生Segfault

#include <string.h> 
#include <sys/mman.h> 
#include <elf.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <fcntl.h> 

int main(int argc, char **argv) { 
    const char *ls = NULL; 
    int fd = -1; 
    struct stat stat = {0}; 

    if (argc != 2) { 
     printf("Missing arg\n"); 
     return 0; 
    } 

    // open the file in readonly mode 
    fd = open(argv[1], O_RDONLY); 
    if (fd < 0) { 
     perror("open"); 
     goto cleanup; 
    } 

    // get the file size 
    if (fstat(fd, &stat) != 0) { 
     perror("stat"); 
     goto cleanup; 
    } 

    // put the file in memory 
    ls = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 
    if (ls == MAP_FAILED) { 
     perror("mmap"); 
     goto cleanup; 
    } 

    Elf64_Ehdr *eh = (Elf64_Ehdr *)ls; 
    // looking for the PT_DYNAMIC segment 
    for (int i = 0; i < eh->e_phnum; i++) { 
     Elf64_Phdr *ph = (Elf64_Phdr *)((char *)ls + (eh->e_phoff + eh->e_phentsize * i)); 
     const char *strtab = NULL; 
     if (ph->p_type == PT_DYNAMIC) { 
      const Elf64_Dyn *dtag_table = (const Elf64_Dyn *)(ls + ph->p_offset); 

      // looking for the string table 
      for (int j = 0; 1; j++) { 
       // the end of the dtag table is marked by DT_NULL 
       if (dtag_table[j].d_tag == DT_NULL) { 
        break; 
       } 

       if (dtag_table[j].d_tag == DT_STRTAB) { 
        strtab = (const char *)dtag_table[j].d_un.d_ptr; 
        printf("string table addr: %p\n", strtab); 
       } 
      } 

      // no string table ? we're stuck, bail out 
      if (strtab == NULL) { 
       printf("no strtab, abort\n"); 
       break; 
      } 

      // now, i print shared libraries 
      for (int j = 0; 1; j++) { 
       // the end of the dtag table is marked by DT_NULL 
       if (dtag_table[j].d_tag == DT_NULL) { 
        break; 
       } 

       if (dtag_table[j].d_tag == DT_NEEDED) { 
        printf("too long: %d\n", &strtab[dtag_table[j].d_un.d_val] >= ls + stat.st_size); 
        printf("string offset in strtab: %lu\n", dtag_table[j].d_un.d_val); 
        printf("string from strtab: %s\n", &strtab[dtag_table[j].d_un.d_val]); 
       } 
      } 

      // only go through the PT_DYNAMIC segment we found, 
      // other segments dont matter 
      break; 
     } 
    } 

    // cleanup memory 
    cleanup: 
    if (fd != -1) { 
     close(fd); 
    } 
    if (ls != MAP_FAILED) { 
     munmap((void *)ls, stat.st_size); 
    } 

    return 0; 
} 

我编译:

gcc -g -Wall -Wextra test.c 

当我运行./a.out a.out(使阅读本身的共享库),它似乎做工精细,我得到以下输出:

string table addr: 0x4003d8 
too long: 0 
string offset in strtab: 1 
string from strtab: libc.so.6 

然而,当我运行对系统二进制文件,像/bin/ls,与./a.out /bin/ls,T如果我在78行出现段错误。

string table addr: 0x401030 
too long: 0 
string offset in strtab: 1 
Segmentation fault (core dumped) 

我不知道为什么。读/bin/lsreadelf,我用的是不会忽略似乎是正确和字符串偏移似乎是正确太:

$ readelf -a /bin/ls | grep dynstr 
... 
[ 6] .dynstr   STRTAB   0000000000401030 00001030 
... 

$ readelf -p .dynstr /bin/ls 
String dump of section '.dynstr': 
    [  1] libselinux.so.1 
    ... 

我在做什么错?

+0

如果你在一个调试器中运行,发生崩溃时所有涉及变量的值是什么?他们看起来有效吗? –

+0

你确定在'&strtab [dtag_table [j] .d_un.d_val]'开始的是一个以nul结尾的字符串吗? – LPs

+0

使用'valgrind'找到你的记忆问题 –

回答

1

我在做什么错?

对于非PIE二进制,这样的:

strtab = (const char *)dtag_table[j].d_un.d_ptr; 

点,其中.dynstr本来如果是二进制文件实际上是在当前进程运行。

如果二进制文件是而不是正在运行,您需要将此值重新定位$where_mmaped - $load_addr$where_mmaped是你的ls变量。 $load_addr是二进制静态链接到加载的地址(通常是第一个PT_LOAD段的p_vaddr;对于x86_64二进制,典型值为0x400000)。

您会注意到,这整洁地解释了为什么./a.out a.out有效:您从自己的地址空间中读取.dynstr,而使用a.out找出正确的偏移量。

+0

非常感谢,2天内答复两次! – conradkdotcom

+0

据我所知,这是有效的,因为'PT_LOAD'的'p_offset'是'0'。为了使程序更可靠,我想我们必须考虑'p_offset',导致类似'strtab = ls + load_offset +(strtab_addr - load_addr)'。这个假设是否正确? – conradkdotcom

相关问题