echo
コマンドのオブジェクトファイルと戯れてみる
ELF
ELFとは実行可能バイナリやオブジェクトファイル等のフォーマットを規定したのもだそう
echo コマンドのELFヘッダを見てみる
ELFヘッダとは
ファイルの先頭に存在し、ELF識別子、アーキテクチャ情報および、他の2つのヘッダへの情報を持つ。
ヘッダは下記で確認できる
# readelf -h /bin/echo ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x40192a Start of program headers: 64 (bytes into file) Start of section headers: 31144 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 31 Section header string table index: 30
ehco コマンドのプログラムヘッダを見てみる
プログラムヘッダとは
ファイル上のどの部分(セグメント)がどのような属性で何処に読み込まれるかを保持するヘッダであり、ファイルローダによって扱われる。
ELFヘッダに続いて実行時ディスクから何らかの形で読み込まれるセグメントの数だけ存在する。
プログラムヘッダは下記でみれる
# readelf -l /bin/echo Elf file type is EXEC (Executable file) Entry point 0x40192a There are 9 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001f8 0x00000000000001f8 R E 8 INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238 0x000000000000001c 0x000000000000001c R 1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x0000000000005ecc 0x0000000000005ecc R E 200000 LOAD 0x0000000000006da8 0x0000000000606da8 0x0000000000606da8 0x0000000000000478 0x00000000000005d8 RW 200000 DYNAMIC 0x0000000000006e08 0x0000000000606e08 0x0000000000606e08 0x00000000000001d0 0x00000000000001d0 RW 8 NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254 0x0000000000000044 0x0000000000000044 R 4 GNU_EH_FRAME 0x00000000000051cc 0x00000000004051cc 0x00000000004051cc 0x000000000000023c 0x000000000000023c R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 10 GNU_RELRO 0x0000000000006da8 0x0000000000606da8 0x0000000000606da8 0x0000000000000258 0x0000000000000258 R 1 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 03 .init_array .fini_array .jcr .data.rel.ro .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07 08 .init_array .fini_array .jcr .data.rel.ro .dynamic .got
ELFファイルタイプはEXEC(実行可能)でエントリポイントが0x40192a
等の情報がわかる
また
Section to Segment mapping
以降の情報から最初のプログラムヘッダで示されるセグメントはPHDRでそれに含まれるセクションはなく
次がINTRTP。この中には.interp
が含まれてることがわかる
セクションヘッダを見てみる
セクションヘッダとは
オブジェクトファイルの論理的な構造を記述する部分で、ヘッダと名前がついているが実際にはファイルの最後あたりに置かれていることが多い。
ここはリンカやデバッガによって参照されることがある。セクションはセクション名があるが、それは特殊なセクションに置かれ何番目の文字列かという指し方を行う。
このことによって、エントリそのものは固定長にしつつセクション名の長さ制限を取り払っている。
下記で確認できる
# readelf -S /bin/echo There are 31 section headers, starting at offset 0x79a8: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400238 00000238 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 0000000000400254 00000254 0000000000000020 0000000000000000 A 0 0 4 [ 3] .note.gnu.build-i NOTE 0000000000400274 00000274 0000000000000024 0000000000000000 A 0 0 4 [ 4] .gnu.hash GNU_HASH 0000000000400298 00000298 000000000000001c 0000000000000000 A 5 0 8 [ 5] .dynsym DYNSYM 00000000004002b8 000002b8 0000000000000558 0000000000000018 A 6 1 8 [ 6] .dynstr STRTAB 0000000000400810 00000810 000000000000025d 0000000000000000 A 0 0 1 [ 7] .gnu.version VERSYM 0000000000400a6e 00000a6e 0000000000000072 0000000000000002 A 5 0 2 [ 8] .gnu.version_r VERNEED 0000000000400ae0 00000ae0 0000000000000060 0000000000000000 A 6 1 8 [ 9] .rela.dyn RELA 0000000000400b40 00000b40 0000000000000078 0000000000000018 A 5 0 8 [10] .rela.plt RELA 0000000000400bb8 00000bb8 0000000000000498 0000000000000018 AI 5 25 8 [11] .init PROGBITS 0000000000401050 00001050 000000000000001a 0000000000000000 AX 0 0 4 [12] .plt PROGBITS 0000000000401070 00001070 0000000000000320 0000000000000010 AX 0 0 16 [13] .plt.got PROGBITS 0000000000401390 00001390 0000000000000008 0000000000000000 AX 0 0 8 [14] .text PROGBITS 00000000004013a0 000013a0 000000000000308a 0000000000000000 AX 0 0 16 [15] .fini PROGBITS 000000000040442c 0000442c 0000000000000009 0000000000000000 AX 0 0 4 [16] .rodata PROGBITS 0000000000404440 00004440 0000000000000d8b 0000000000000000 A 0 0 32 [17] .eh_frame_hdr PROGBITS 00000000004051cc 000051cc 000000000000023c 0000000000000000 A 0 0 4 [18] .eh_frame PROGBITS 0000000000405408 00005408 0000000000000ac4 0000000000000000 A 0 0 8 [19] .init_array INIT_ARRAY 0000000000606da8 00006da8 0000000000000008 0000000000000008 WA 0 0 8 [20] .fini_array FINI_ARRAY 0000000000606db0 00006db0 0000000000000008 0000000000000008 WA 0 0 8 [21] .jcr PROGBITS 0000000000606db8 00006db8 0000000000000008 0000000000000000 WA 0 0 8 [22] .data.rel.ro PROGBITS 0000000000606dc0 00006dc0 0000000000000048 0000000000000000 WA 0 0 32 [23] .dynamic DYNAMIC 0000000000606e08 00006e08 00000000000001d0 0000000000000010 WA 6 0 8 [24] .got PROGBITS 0000000000606fd8 00006fd8 0000000000000028 0000000000000008 WA 0 0 8 [25] .got.plt PROGBITS 0000000000607000 00007000 00000000000001a0 0000000000000008 WA 0 0 8 [26] .data PROGBITS 00000000006071a0 000071a0 0000000000000080 0000000000000000 WA 0 0 32 [27] .bss NOBITS 0000000000607220 00007220 0000000000000160 0000000000000000 WA 0 0 32 [28] .gnu_debuglink PROGBITS 0000000000000000 00007220 0000000000000010 0000000000000000 0 0 4 [29] .gnu_debugdata PROGBITS 0000000000000000 00007230 0000000000000658 0000000000000000 0 0 1 [30] .shstrtab STRTAB 0000000000000000 00007888 000000000000011e 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific)
echo
はELFヘッダを確認した時にストリングテーブルのindexが30だったので30番目のセクションヘッダがそのストリングテーブルを保持していることがわかる
[30] .shstrtab STRTAB 0000000000000000 00007888 000000000000011e 0000000000000000 0 0 1
これでsh_offsetが0x00007888でサイズが0x000000000000011eということがわかる
ストリングテーブルを見てみる
下記で見れる
# od --skip-bytes 0x00007888 --read-bytes 0x11e -t x1z /bin/echo 0074210 00 2e 73 68 73 74 72 74 61 62 00 2e 69 6e 74 65 >..shstrtab..inte< 0074230 72 70 00 2e 6e 6f 74 65 2e 41 42 49 2d 74 61 67 >rp..note.ABI-tag< 0074250 00 2e 6e 6f 74 65 2e 67 6e 75 2e 62 75 69 6c 64 >..note.gnu.build< 0074270 2d 69 64 00 2e 67 6e 75 2e 68 61 73 68 00 2e 64 >-id..gnu.hash..d< 0074310 79 6e 73 79 6d 00 2e 64 79 6e 73 74 72 00 2e 67 >ynsym..dynstr..g< 0074330 6e 75 2e 76 65 72 73 69 6f 6e 00 2e 67 6e 75 2e >nu.version..gnu.< 0074350 76 65 72 73 69 6f 6e 5f 72 00 2e 72 65 6c 61 2e >version_r..rela.< 0074370 64 79 6e 00 2e 72 65 6c 61 2e 70 6c 74 00 2e 69 >dyn..rela.plt..i< 0074410 6e 69 74 00 2e 70 6c 74 2e 67 6f 74 00 2e 74 65 >nit..plt.got..te< 0074430 78 74 00 2e 66 69 6e 69 00 2e 72 6f 64 61 74 61 >xt..fini..rodata< 0074450 00 2e 65 68 5f 66 72 61 6d 65 5f 68 64 72 00 2e >..eh_frame_hdr..< 0074470 65 68 5f 66 72 61 6d 65 00 2e 69 6e 69 74 5f 61 >eh_frame..init_a< 0074510 72 72 61 79 00 2e 66 69 6e 69 5f 61 72 72 61 79 >rray..fini_array< 0074530 00 2e 6a 63 72 00 2e 64 61 74 61 2e 72 65 6c 2e >..jcr..data.rel.< 0074550 72 6f 00 2e 64 79 6e 61 6d 69 63 00 2e 67 6f 74 >ro..dynamic..got< 0074570 2e 70 6c 74 00 2e 64 61 74 61 00 2e 62 73 73 00 >.plt..data..bss.< 0074610 2e 67 6e 75 5f 64 65 62 75 67 6c 69 6e 6b 00 2e >.gnu_debuglink..< 0074630 67 6e 75 5f 64 65 62 75 67 64 61 74 61 00 >gnu_debugdata.< 0074646
.shstrtabの先頭からのoffsetがストリングテーブルでのindexとなる
シンボルテーブルを見てみる
シンブルテーブルはシンボルとその値を対応させるためのテーブル
下記でみることができる
# readelf -s /bin/echo Symbol table '.dynsym' contains 57 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __uflow@GLIBC_2.2.5 (2) 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getenv@GLIBC_2.2.5 (2) 3: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND __progname@GLIBC_2.2.5 (2) 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@GLIBC_2.2.5 (2) 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.2.5 (2) 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@GLIBC_2.2.5 (2) 7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strncmp@GLIBC_2.2.5 (2) 8: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND stdout@GLIBC_2.2.5 (2) 9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _exit@GLIBC_2.2.5 (2) 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcpy@GLIBC_2.2.5 (2) 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __fpending@GLIBC_2.2.5 (2) 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND textdomain@GLIBC_2.2.5 (2) 13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fclose@GLIBC_2.2.5 (2) 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bindtextdomain@GLIBC_2.2.5 (2) 15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dcgettext@GLIBC_2.2.5 (2) 16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __ctype_get_mb_cur_max@GLIBC_2.2.5 (2) 17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.2.5 (2) 18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3) 19: 0000000000000000 0 FUNC GLOBAL DEFAULT UND mbrtowc@GLIBC_2.2.5 (2) 20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __overflow@GLIBC_2.2.5 (2) 21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strrchr@GLIBC_2.2.5 (2) 22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND lseek@GLIBC_2.2.5 (2) 23: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@GLIBC_2.2.5 (2) 24: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fscanf@GLIBC_2.2.5 (2) 25: 0000000000000000 0 FUNC GLOBAL DEFAULT UND close@GLIBC_2.2.5 (2) 26: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2) 27: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcmp@GLIBC_2.2.5 (2) 28: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs_unlocked@GLIBC_2.2.5 (2) 29: 0000000000000000 0 FUNC GLOBAL DEFAULT UND calloc@GLIBC_2.2.5 (2) 30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcmp@GLIBC_2.2.5 (2) 31: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ 32: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (4) 33: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND program_invocation_name@GLIBC_2.2.5 (2) 34: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fileno@GLIBC_2.2.5 (2) 35: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc@GLIBC_2.2.5 (2) 36: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fflush@GLIBC_2.2.5 (2) 37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND nl_langinfo@GLIBC_2.2.5 (2) 38: 0000000000000000 0 FUNC GLOBAL DEFAULT UND ungetc@GLIBC_2.2.5 (2) 39: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __freading@GLIBC_2.2.5 (2) 40: 0000000000000000 0 FUNC GLOBAL DEFAULT UND realloc@GLIBC_2.2.5 (2) 41: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fdopen@GLIBC_2.2.5 (2) 42: 0000000000000000 0 FUNC GLOBAL DEFAULT UND setlocale@GLIBC_2.2.5 (2) 43: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __printf_chk@GLIBC_2.3.4 (5) 44: 0000000000000000 0 FUNC GLOBAL DEFAULT UND error@GLIBC_2.2.5 (2) 45: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND __progname_full@GLIBC_2.2.5 (2) 46: 0000000000000000 0 FUNC GLOBAL DEFAULT UND open@GLIBC_2.2.5 (2) 47: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fseeko@GLIBC_2.2.5 (2) 48: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@GLIBC_2.2.5 (2) 49: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.2.5 (2) 50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fwrite@GLIBC_2.2.5 (2) 51: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __fprintf_chk@GLIBC_2.3.4 (5) 52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND mbsinit@GLIBC_2.2.5 (2) 53: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND program_invocation_short_@GLIBC_2.2.5 (2) 54: 0000000000000000 0 FUNC GLOBAL DEFAULT UND iswprint@GLIBC_2.2.5 (2) 55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __ctype_b_loc@GLIBC_2.3 (6) 56: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND stderr@GLIBC_2.2.5 (2)
セクションヘッダを見るとシンボルテーブルは下記.dynsm
であることがわかる
[ 5] .dynsym DYNSYM 00000000004002b8 000002b8 0000000000000558 0000000000000018 A 6 1 8
これをdumpしてみると
# od --skip-bytes 0x2b8 --read-bytes 0x558 -t x1z /bin/echo 0001270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0001310 00 00 00 00 00 00 00 00 21 01 00 00 12 00 00 00 >........!.......< 0001330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0001350 43 01 00 00 12 00 00 00 00 00 00 00 00 00 00 00 >C...............< 0001370 00 00 00 00 00 00 00 00 ef 01 00 00 11 00 00 00 >................< 0001410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0001430 ea 01 00 00 12 00 00 00 00 00 00 00 00 00 00 00 >................< 0001450 00 00 00 00 00 00 00 00 7a 00 00 00 12 00 00 00 >........z.......< 0001470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0001510 d1 00 00 00 12 00 00 00 00 00 00 00 00 00 00 00 >................< 0001530 00 00 00 00 00 00 00 00 38 00 00 00 12 00 00 00 >........8.......< 0001550 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0001570 f7 00 00 00 11 00 00 00 00 00 00 00 00 00 00 00 >................< ~~~(ry~~~~~
シンボルテーブルの構造はこのようになっている
64bit
uint32_t st_name; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; Elf64_Addr st_value; uint64_t st_size;
0番目のsymble情報は空
0001270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
進んでいってここの情報は
0001350 43 01 00 00 12 00 00 00 00 00 00 00 00 00 00 00 >C...............<
これはアドレス0001350から始まるこのバイト列は最初の43 01 00 00
つまりst_name=0x143に対応する
逆順になっているのは今回のアーキテクチャがx86でリトルインディアンだから
.dynstr
セクションを見るとストリングテーブルは先頭から0x810バイト目から始まっている
[ 6] .dynstr STRTAB 0000000000400810 00000810 000000000000025d 0000000000000000 A 0 0 1
そこで0x810に0x143を足した0x953から始まる文字列を見てみる
# od --skip-bytes 0x953 --read-bytes 32 -t x1z /bin/echo 0004523 67 65 74 65 6e 76 00 5f 5f 66 72 65 61 64 69 6e >getenv.__freadin< 0004543 67 00 73 74 64 65 72 72 00 66 73 63 61 6e 66 00 >g.stderr.fscanf.< 0004563
見た通り0x143に対応するシンボルはgetenvであることがわかった
これはreadelfのここに対応している
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getenv@GLIBC_2.2.5 (2)
次回に続く
参考