2017-05-24 25 views
0

私はC言語を使ってバイナリで使われる共有ライブラリの名前を読もうとしています。これまでのところ、私はtest.cで次のプログラムを持っている:SegfaultはELFファイルの.dynstr(strtab)を読むとき

#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; 
} 

私はそれをコンパイル:私は(そう、それ自体の共有ライブラリを読み込む)./a.out a.outを実行すると、正常に動作するようです

gcc -g -Wall -Wextra test.c 

、私は、次のような出力が得られます。

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

しかし、私はトン、./a.out /bin/lsと、/bin/lsのようなシステムバイナリ、に対してそれを実行すると私は78行目でセグメンテーションを取得します。

私はなぜそれがわかりません。 readelf/bin/lsを読むと、私が使用して住所が正しいように見えるし、オフセット文字列が右あまりにも思える:

$ 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]'で始まるものがヌルで終了する文字列であることを確かめていますか? – LPs

+0

あなたのメモリの問題を見つけるのに 'valgrind'を使用してください。 –

答えて

1

私は間違っていますか?非PIEバイナリについて

、これ:

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

点そのバイナリが実際に現在のプロセスで実行された場合.dynstrがあったであろう場所に。

バイナリがではない)の場合は、$where_mmaped - $load_addrでこの値を再配置する必要があります。 $where_mmapedls変数です。 $load_addrは、バイナリがロードされるように静的にリンクされたアドレスです(通常、最初のPT_LOADセグメントのp_vaddr、x86_64バイナリの場合は0x400000)。

./a.out a.outが正常に動作する理由を説明しています。a.outを使用して正しいオフセットを把握しながら、自分のアドレススペースから.dynstrを読んでください。

+0

ありがとう、2日以内に2つの答え! – conradkdotcom

+0

私は理解しているので、これは 'PT_LOAD'の' p_offset'が '0'であるために動作します。プログラムをより信頼できるものにするために、 'p_offset'を考慮に入れて、' strtab = ls + load_offset +(strtab_addr - load_addr) 'のような結果になると思います。その仮定は正しいのでしょうか? – conradkdotcom

関連する問題