ELFファイルを作る part5
ELFファイルを作るpart4で、ライブラリとのリンクについて詳細を追ってみる手順の案に書いていた
ELFファイルを分解するプログラム、また、その分解したものから再生成するプログラム」
これについて考えてみる。
まずは先だって分解をしてみる。
目標
ELFファイルをsection単位に分解し、それを保持する。
実装
分解するファイル
今回対象とするコードはこんな感じ
システムコールではなく、libcから呼び出すputcharを使っている。
内容としては以前にやっていた、コード42でexitするのに加え、せっかくなのでputcharを使って"T"という文字を出力させている。実際に試してみると
$ nasm -f elf64 test.asm $ gcc -s -nostartfiles -o test.out test.o $ ./test.out T $ echo $? 42
という出力を得られる。先に述べてる様に、libcが必要な状態にしているので、当然実行可能ファイルはライブラリとリンクする。このtest.outが分解対象。
オブジェクトファイルを見てみる
実行可能ファイルを作成する前、test.oというオブジェクトファイルが作られるが、このファイルがリンクする前の状態であることはいろんな所に情報があるので特には触れない。
readelfコマンドでこのファイルのセクションを見てみると
$ readelf -S test.o There are 6 section headers, starting at offset 0x40: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 000001c0 0000000000000014 0000000000000000 AX 0 0 16 [ 2] .shstrtab STRTAB 0000000000000000 000001e0 000000000000002c 0000000000000000 0 0 1 [ 3] .symtab SYMTAB 0000000000000000 00000210 0000000000000090 0000000000000018 4 3 4 [ 4] .strtab STRTAB 0000000000000000 000002a0 000000000000001e 0000000000000000 0 0 1 [ 5] .rela.text RELA 0000000000000000 000002c0 0000000000000030 0000000000000018 3 1 4
という結果を得られる。つまり、リンクも行った後にあるセクションで、この中に無いものがリンク時に追加されたセクションである。
リンク済みのファイルを分解
実行可能ファイルとして先に作成したtest.out。これを各セクションに分解してみる。分解していじった後は、セクションとセグメントの対応 part2で得られた対応関係を元にセグメントを作成、その後にELFヘッダーを作成してやれば元の実行ファイルに戻るはず。
分解に使うコードは以前にELFファイルを作る part4等で使ったものを流用する。主にヘッダー類の読み書きとか。
やってみた
コードはこんな感じ
パッケージで使ってるファイル達はこちら。各ファイルの階層は
/ |- teardown.py -- elf/ |- Utils.py -- components/ |- SectionAggregator.py |- Section.py -- headers/ |- Eh.py |- Header.py |- Ph.py -- Sh.py
という具合。gistで階層わかる様に置ける方法無いかな・・・。githubの方だとあとで書き換えちゃう可能性があるしな・・・。
teardown.py
ELFファイルからsectionを取り出してるこの処理の説明をば。
- 8~9行目
見ての通り、今回の処理対象であるtest.outを読み込んでます。そして、処理しやすいようにバイト毎の値を各要素として持つリスト、byteListへ格納。
- 12~13行目
ELF header用クラスのインスタンスを作成し、byteListの先頭から64byte相当のデータを読み込ませる。この処理でインスタンスに、ヘッダ内の各要素が持つべき値を振り分ける。
- 15~17行目
セクションサイズ、セクション数、セクションヘッダ開始位置オフセットの取得
- 20~27行目
shstrtabセクションのデータを取得し、セクション名テーブルを作成(各セクション名を\0区切りにした文字列となる)。
- 30~44行目
nullセクションとshstrtabセクション以外の全セクションについて、ヘッダ、名前、データを取得してSectionクラスのインスタンスを作成。それをSectionAggregatorへ追加。
比較してみる
readelfで得られる情報と、今回作成した処理で得られる結果を比較してみる
- readelf -S test.out
$ readelf -S test.out There are 15 section headers, starting at offset 0x1090: 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 00000000004001c8 000001c8 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.gnu.build-i NOTE 00000000004001e4 000001e4 0000000000000024 0000000000000000 A 0 0 4 [ 3] .hash HASH 0000000000400208 00000208 0000000000000018 0000000000000004 A 5 0 8 [ 4] .gnu.hash GNU_HASH 0000000000400220 00000220 000000000000001c 0000000000000000 A 5 0 8 [ 5] .dynsym DYNSYM 0000000000400240 00000240 0000000000000048 0000000000000018 A 6 1 8 [ 6] .dynstr STRTAB 0000000000400288 00000288 0000000000000024 0000000000000000 A 0 0 1 [ 7] .gnu.version VERSYM 00000000004002ac 000002ac 0000000000000006 0000000000000002 A 5 0 2 [ 8] .gnu.version_r VERNEED 00000000004002b8 000002b8 0000000000000020 0000000000000000 A 6 1 8 [ 9] .rela.plt RELA 00000000004002d8 000002d8 0000000000000030 0000000000000018 A 5 10 8 [10] .plt PROGBITS 0000000000400308 00000308 0000000000000030 0000000000000010 AX 0 0 4 [11] .text PROGBITS 0000000000400340 00000340 0000000000000014 0000000000000000 AX 0 0 16 [12] .dynamic DYNAMIC 0000000000600e98 00000e98 0000000000000150 0000000000000010 WA 6 0 8 [13] .got.plt PROGBITS 0000000000600fe8 00000fe8 0000000000000028 0000000000000008 WA 0 0 8 [14] .shstrtab STRTAB 0000000000000000 00001010 000000000000007e 0000000000000000 0 0 1
- teardown.py
$ python teardown.py ====== Section Header(.interp) ====== name_index: 11 type: 1 flag: 2 address: 4194760(0x4001c8) offset: 456(0x1c8) size: 28(0x1c) link: 0 info: 0 address_align: 1 entry_table_size:0 ====== Section Header(.note.gnu.build-id) ====== name_index: 19 type: 7 flag: 2 address: 4194788(0x4001e4) offset: 484(0x1e4) size: 36(0x24) link: 0 info: 0 address_align: 4 entry_table_size:0 ====== Section Header(.hash) ====== name_index: 42 type: 5 flag: 2 address: 4194824(0x400208) offset: 520(0x208) size: 24(0x18) link: 5 info: 0 address_align: 8 entry_table_size:4 ... 以下略
良さそうな感じだ。