l'essentiel est invisible pour les yeux

Wednesday, November 15, 2006

[binary] Hack #5 ELFフォーマット

via Binary Hack #5 ELF入門



バイナリの勉強。一日目は、ELF入門からやった。
はじめてのバイナリ入門なため間違いがあるかもしれません。

ELF とは実行可能バイナリやオブジェクトファイルなどのフォーマットを規定したフォーマットである。

ELFは先頭4バイトが


0x7F 0x45 0x4C 0x46

のようなマジックナンバーを持つ。

また、fileコマンドで確認できる。

[4296]% file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.0, dynamically linked (uses shared libs), for GNU/Linux 2.2.0, stripped
[4297]% file /bin/cat
/bin/cat: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.0, dynamically linked (uses shared libs), for GNU/Linux 2.2.0, stripped
[4298]%


ELFは4つの部分からなる。
ELFヘッダ
プログラムヘッダテーブル
複数のセクション
セクションヘッダテーブル

プログラムヘッダは、次のコマンドで調べる。

[4298]% readelf -h /bin/ls
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8049a50
Start of program headers: 52 (bytes into file)
Start of section headers: 74948 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 25
Section header string table index: 24
[4299]%


ELFヘッダからプログラムヘッダの開始位置やプログラムヘッダ数・セッションヘッダ数といった情報を知ることが出来る。

odコマンドでプログラムヘッダをダンプする。

[4301]% od -N 34 -t x1z /bin/ls
0000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 >.ELF............<
0000020 02 00 03 00 01 00 00 00 50 9a 04 08 34 00 00 00 >........P...4...<
0000040 c4 24 >.$<
0000042
[4302]%


それぞれの値は、

typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
ElfN_Addr e_entry;
ElfN_Off e_phoff;
ElfN_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
}

に対応している。

プログラムヘッダには、実行開始時にメモリにロードされるべきデータが含まれます。プログラムコードや初期化済みグローバル変数の領域などが含まれる。(参考より)
プログラムヘッダを出力するには、readelfコマンドが使用できる。

[4300]% readelf -l /bin/ls


シンボルテーブルを表示

[4307]% readelf -s /bin/ls | head -10

Symbol table '.dynsym' contains 107 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08049484 60 FUNC GLOBAL DEFAULT UND readlink@GLIBC_2.0 (2)
2: 08049494 283 FUNC GLOBAL DEFAULT UND getgrnam@GLIBC_2.0 (2)
3: 080494a4 42 FUNC GLOBAL DEFAULT UND __fpending@GLIBC_2.2 (3)
4: 080494b4 58 FUNC GLOBAL DEFAULT UND acl_entries@ACL_1.0 (4)
5: 080494c4 83 FUNC GLOBAL DEFAULT UND sigaction@GLIBC_2.0 (2)
6: 080494d4 239 FUNC GLOBAL DEFAULT UND readdir64@GLIBC_2.2 (3)


シンボルテーブルを実際にダンプさせてみて上の表示と一致するか確かめる。

[4310]% readelf -S /bin/ls | grep DYNSYM
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 4] .dynsym DYNSYM 080484a0 0004a0 0006b0 10 A 5 1 4


先頭から0x4a0の位置から0x6b0バイト表示する。

[4313]% od -j 0x4a0 -N 0x6b0 -t x1z /bin/ls | head -10 [/home/furutani]
0002240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
0002260 6c 01 00 00 84 94 04 08 3c 00 00 00 12 00 00 00 >l.......<.......<
0002300 7d 03 00 00 94 94 04 08 1b 01 00 00 12 00 00 00 >}...............<
0002320 3b 01 00 00 a4 94 04 08 2a 00 00 00 12 00 00 00 >;.......*.......<
0002340 49 00 00 00 b4 94 04 08 3a 00 00 00 12 00 00 00 >I.......:.......<
0002360 9a 02 00 00 c4 94 04 08 53 00 00 00 12 00 00 00 >........S.......<
0002400 fd 00 00 00 d4 94 04 08 ef 00 00 00 12 00 00 00 >................<
0002420 dc 03 00 00 e4 94 04 08 67 01 00 00 12 00 00 00 >........g.......<
0002440 75 01 00 00 f4 94 04 08 67 00 00 00 12 00 00 00 >u.......g.......<
0002460 62 00 00 00 84 a1 05 08 00 00 00 00 11 00 f1 ff >b...............<
[4314]%

一つ目のインデクス(0002260)は本書に取り上げられている通り。
x86はリトルエンディアンであるので、最初の32ビットは0x000037dとなりこの値がシンボル名へのオフセット値となっている。
0002260のインデクスについても同じで、0x16cがオフセットである。

ストリングテーブルを調べてストリングテーブルの開始地点と上記で求めたオフセットを合計して、ダンプする領域を特定する。

[4319]% readelf -S /bin/ls | grep STRTAB
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 5] .dynstr STRTAB 08048b50 000b50 00047b 00 A 0 0 1
[24] .shstrtab STRTAB 00000000 012400 0000c3 00 0 0 1


.dynsymのOffsetの値0xb50+0x37D=0xECD

[4320]% od --skip-bytes 0xecd --read-bytes 16 -t x1z /bin/ls
0007315 67 65 74 67 72 6e 61 6d 00 5f 73 65 74 6a 6d 70 >getgrnam._setjmp<
0007335
[4321]%


getgrnamという文字列は、シンボルテーブルの二つ目のインデクスの値と一致する。

[4326]% readelf -s /bin/ls | head -10

Symbol table '.dynsym' contains 107 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08049484 60 FUNC GLOBAL DEFAULT UND readlink@GLIBC_2.0 (2)
2: 08049494 283 FUNC GLOBAL DEFAULT UND getgrnam@GLIBC_2.0 (2)
3: 080494a4 42 FUNC GLOBAL DEFAULT UND __fpending@GLIBC_2.2 (3)
4: 080494b4 58 FUNC GLOBAL DEFAULT UND acl_entries@ACL_1.0 (4)
5: 080494c4 83 FUNC GLOBAL DEFAULT UND sigaction@GLIBC_2.0 (2)
6: 080494d4 239 FUNC GLOBAL DEFAULT UND readdir64@GLIBC_2.2 (3)
[4327]%


参考
オブジェクトファイルについて