ELF形式の解析を行う際の手順をまとめておきます。
まず、main関数で解析対象のバイナリをfopenします。
そしてfstatやmmapを利用してベタバイナリをメモリに確保したあと、ようやく解析が始まります。
int fd;
struct stat sb;
char *head;
// 対象のELF形式のファイル名はargv[1]に入っている
fd = open(argv[1], O_RDONLY);
fstat(fd, &sb);
head = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
// elfdumpで解析を実際に行う
elfdump(head);
close(fd);
return 0;
はじめはmmapで確保された先頭アドレスをheadというポインタに渡します。
このheadは読み込んだELFの先頭アドレスを指すことになるので、実質ELFヘッダを指しているのと同じになります。
ELFヘッダにはELF形式が32bit/64bitのどちらであるかを表すフラグやエンディアンについての情報、そして実行に不可欠なエントリポイントのアドレスやセクション/プログラムヘッダのオフセットなど、重要な情報が多く含まれています。
ELFを解析する際にはこの部分の情報を基本として解析を行います。
次に実際にELFヘッダを解析します。
はじめにやらなければいけないことは対象のELFヘッダが自分の解析プログラムの条件と合致しているかどうかです。
多くの場合はELFヘッダの先頭から4bit目にある32bit/64bitのフラグと、5bit目にあるエンディアンのフラグを確認します。
// static int elfdump(char *head)
Elf32_Ehdr *ehdr = head;
// 32bit?? (EI_CLASS == 4)
if(ehdr->e_ident[EI_CLASS] != ELF_CLASS32){
// bad
}
// little endian?? (EI_DATA == 5)
if(ehdr->e_ident[EI_DATA] != ELF_DATA2LSB){
// bad
}
お次はセクション名が格納されているセクションである.shstrtabの先頭アドレスを取得します。
// head(ehdr)はELFヘッダを指す
// ehdr->e_shoffはheadからセクションテーブルへのオフセット
// ehdr->e_shentsizeはセクションテーブルエントリのサイズ
// ehdr->e_shstrndxはセクションテーブル上での.shstrtabの番号
Elf32_Shdr shstr = (Elf32_Shdr *) (head + ehdr->e_shoff + ehdr->e_shentsize * ehdr->e_shstrndx);
ここまでくれば後はセクション名を表示しようが、セグメントの情報を表示しようが何でもできます。