GNU/Linux >> Linux の 問題 >  >> Linux

プログラムは独自の elf セクションを読み取ることができますか?

<ブロック引用>

プログラム自体からプログラムのビルド バージョン (.note.gnu.build-id elf セクションにあります) を印刷するにはどうすればよいですか?

<オール>
  • ElfW(Ehdr) を読む必要があります (ファイルの先頭で)バイナリ(.e_phoffでプログラムヘッダーを見つけるため) と .e_phnum プログラム ヘッダーがどこにあり、何個読み取る必要があるかがわかります)。

  • PT_NOTE が見つかるまで、プログラム ヘッダーを読み取ります。 プログラムのセグメント。そのセグメントは、バイナリ内のすべてのノートの先頭へのオフセットを示します。

  • ElfW(Nhdr) を読む必要があります。 メモの残りをスキップします (メモの合計サイズは sizeof(Nhdr) + .n_namesz + .n_descsz です 、適切に整列)、.n_type == NT_GNU_BUILD_ID のメモが見つかるまで .

  • NT_GNU_BUILD_ID を見つけたら .n_namesz を過ぎてスキップすることに注意してください 、そして .n_descsz を読み取ります 実際のビルド ID を読み取るバイト。

  • 読み取ったデータを readelf -n a.out の出力と比較することで、正しいデータを読み取っていることを確認できます。 .

    追伸

    上記のように build-id をデコードするのに苦労する場合、if 実行可能ファイルは削除されていません。symbol をデコードして出力する方がよい場合があります。 代わりに名前を付けます (つまり、backtrace_symbols を複製するため) します) -- シンボル テーブルには固定サイズのエントリが含まれているため、実際には ELF ノートをデコードするよりも簡単です。


    基本的に、これは私の質問に対する回答に基づいて作成したコードです。コードをコンパイルするためにいくつかの変更を加える必要がありましたが、できるだけ多くの種類のプラットフォームで機能することを願っています。ただし、1 つのビルド マシンでのみテストされました。私が使用した仮定の 1 つは、プログラムがそれを実行するマシン上でビルドされているため、プログラムとマシンの間のエンディアンの互換性をチェックしても意味がないというものでした。

    [email protected]:~/$ uname -s -r -m -o
    Linux 3.2.0-45-generic x86_64 GNU/Linux
    [email protected]:~/$ g++ test.cpp -o test
    [email protected]:~/$ readelf -n test | grep Build
        Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
    [email protected]:~/$ ./test
        Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
    
    #include <elf.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    
    #if __x86_64__
    #  define ElfW(type) Elf64_##type
    #else
    #  define ElfW(type) Elf32_##type
    #endif
    
    /*
    detecting build id of a program from its note section
    http://stackoverflow.com/questions/17637745/can-a-program-read-its-own-elf-section
    http://www.scs.stanford.edu/histar/src/pkg/uclibc/utils/readelf.c
    http://www.sco.com/developers/gabi/2000-07-17/ch5.pheader.html#note_section
    */
    
    int main (int argc, char* argv[])
    {
      char *thefilename = argv[0];
      FILE *thefile;
      struct stat statbuf;
      ElfW(Ehdr) *ehdr = 0;
      ElfW(Phdr) *phdr = 0;
      ElfW(Nhdr) *nhdr = 0;
      if (!(thefile = fopen(thefilename, "r"))) {
        perror(thefilename);
        exit(EXIT_FAILURE);
      }
      if (fstat(fileno(thefile), &statbuf) < 0) {
        perror(thefilename);
        exit(EXIT_FAILURE);
      }
      ehdr = (ElfW(Ehdr) *)mmap(0, statbuf.st_size, 
        PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(thefile), 0);
      phdr = (ElfW(Phdr) *)(ehdr->e_phoff + (size_t)ehdr);
      while (phdr->p_type != PT_NOTE)
      {
        ++phdr;
      }
      nhdr = (ElfW(Nhdr) *)(phdr->p_offset + (size_t)ehdr); 
      while (nhdr->n_type != NT_GNU_BUILD_ID)
      {
        nhdr = (ElfW(Nhdr) *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz + nhdr->n_descsz);
      }
      unsigned char * build_id = (unsigned char *)malloc(nhdr->n_descsz);
      memcpy(build_id, (void *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz), nhdr->n_descsz);
      printf("    Build ID: ");
      for (int i = 0 ; i < nhdr->n_descsz ; ++i)
      {
        printf("%02x",build_id[i]);
      }
      free(build_id);
      printf("\n");
      return 0;
    }
    

    Linux
    1. Bashは独自の入力ストリームに書き込むことができますか?

    2. ユーザーが読み取れないファイルを見つけますか?

    3. スクリプトは実行可能であるが読み取り可能ではありませんか?

    1. 各プログラムまたはサービスが/etc/ passwdに独自のアカウントを持っているのはなぜですか?

    2. Strace / ptraceはプログラムをクラッシュさせる可能性がありますか?

    3. SIGHUP が生成される原因は何ですか?

    1. 共有ライブラリ (.so) は、ローディング プログラムに実装されている関数をどのように呼び出すことができますか?

    2. 自分のネットワークをテストできますか?

    3. maildir 内の単一のファイルを読み取るにはどうすればよいですか?