【Rev・バイナリ解析関係】ELFパーサをつくろう:ELFフォーマット1/4
前書き
前の記事でも書いたように、現在、SagradaFamiliaプロジェクトと銘打ってオレオレ統合バイナリ解析プログラムを作成しています。
こういえば聞こえは良いけど、実際は自分がインプットしたものを何も考えずにアウトプットするためのプラットフォームです。
今日まで、プログラムを立ち上げたときの最初の画面とかをQtでごにょごにょしていたのですが、何とかパーサに取りかかることができそうになってきたので、自分の知識の確認の意味も込めてELFフォーマットについてまとめた記事を書いていきます。全部まとめて一気に書いてしまおうかと思いましたが、中だるみ防止と記事自体を最後まで読んでもらいたいという観点から小出しにして書いていきます。今のところ全4回の記事でELFフォーマットについてまとめていきます。
ELFフォーマット
ELFに限らず、ファイルにはそれ固有のフォーマットが設定されていて、フォーマットの各部分毎に固有の役割を持っています。
ELFのフォーマットの概略を以下に示してみます。
言うまでもありませんが、バイトの先頭からELFヘッダ、プログラムヘッダテーブル、セクション、セクションヘッダテーブルとなっています。
ELFヘッダ
ELFヘッダのサイズは、64bit及び32bitアーキテクチャでそれぞれ64Byteと52Byteとなっています。 ヘッダ内のフィールドは同じですがフィールドのサイズが異なります。
ELFヘッダには、後ろに続くプログラムヘッダテーブル、セクション及びセクションヘッダテーブルに関する情報や、対応するアーキテクチャの情報などが格納されています。他にも様々な情報が格納されていますが、今回は、特にバイナリ解析に役立ちそうな、以下のフィールドについて説明していきます。
- e_ident配列
- e_type
- e_machine
- e_entry
- e_*hoff
- e_*entsize
- e_*hnum
- e_shstrndx
これらの情報は、いちいちバイナリを読み解かずともreadelf -h (ファイル名)
で確認することが出来ます。
e_ident配列
e_ident配列は16Byteの配列です。先頭の4Byteは0x7f 0x45 0x4c 0x46
であり、ELF形式ファイル特有ののシグネチャとなっています。残る12Byteにはそれぞれ固有のフィールドが設定されていますが、その中で特にEI_CLASS
バイトはアーキテクチャの識別に利用でき、32bitアーキテクチャの場合には1が値としてセットされ、64bitアーキテクチャの場合は、値として2がセットされます。また、EI_OSABI
バイトは、コンパイル時に指定されたOSとABIに関する情報を保持しています。
e_type
e_typeフィールドはバイナリ自体の情報を保持します。そのバイナリが実行可能形式なのか、.soで表されるような共有オブジェクトファイルなのかはこのフィールドを調べることで判断できます。
e_machine
e_machineフィールドはバイナリが対応するアーキテクチャに関する情報を保持します。x86-64やARMとかのレベルでの識別に利用します。
e_entry
e_entryフィールドはプログラムのエントリポイントを明らかにしたいときに参照します。ところで、-pieオプションが有効になっている時は、ここのアドレスが0x400000番台から始まってなかったりする気がします。(未確認&要調査)
e_*hoff
ELF形式のファイルにはセクションヘッダテーブルとプログラムヘッダテーブルと呼ばれる領域が存在します。これらのオフセットを表すのがe_shoffフィールドとe_phoffです。セクションヘッダとプログラムヘッダは、また今度扱います。
e_*hentsize
色々な言葉の使い分けを見ていると、セクションヘッダテーブルとセクションヘッダは下の図のような関係になっていることが分かります。 各テーブル内のヘッダのサイズに関する情報を保持するのがe_shentsize及びe_phentsizeです。
e_*hnum
各ヘッダテーブル内には複数個のヘッダが格納されています。その個数に関する情報を保持するのがe_shnum及びe_phnumです。ところで、プログラムヘッダテーブルのサイズはe_phnumとe_phentnumをかけた数になります
e_shstrndx
.shstrtabというセクションがあります。このセクションはプログラムのすべてのセクションの名前の情報を保持しています。e_shstrndxフィールドを参照することで、この.shstrtabがセクションヘッダテーブルの何番目に位置するのかが分かります。実際に、readelf -h (プログラム名)
を実行したのちにreadelf -S (プログラム名)
を実行することで確認することが出来ます。
今後
次はプログラムヘッダとセクションをとばしてセクションヘッダについての記事を書いていきます。休日もはさむので、一週間以内には出せるといいなぁ