リバースエンジニアリングへの道 - その20

# リバースエンジニアリングへの道
出田 守です。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか！かっこいい！」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。

## 軌跡
[リバースエンジニアリングへの道 - その19](https://qiita.com/deta-mamoru/items/7a047b0596cb29a6c680)

## 環境
OS: Windows10 64bit Home (日本語)
CPU: Intel® Core™ i3-6006U CPU @ 2.00GHz × 1
メモリ: 2048MB
Python: 3.6.5

私の環境は、普段Ubuntu16.04を使っていますが、ここではWindows10 64bitを仮想マシン上で立ち上げております。
ちなみに教科書では、Windowsの32bitで紹介されています。

## さらっとELF
[前回](https://qiita.com/deta-mamoru/items/7a047b0596cb29a6c680)までは、逆アセンブラについて学んでいました。前回メモリエディタを学ぼうと言っていましたが・・・やめます！笑
逆アセンブラを学んでいるうちになんとなーくメモリエディタの基本的なところも理解できましたので(恐らく)。
なので今回は逆アセンブラでも少し必要になった「ELF」について学びます。ドキュメント( https://refspecs.linuxfoundation.org/elf/elf.pdf )、実際のELFヘッダファイル(elf.h)、Webサイトを参考に学んでいきます。

### ELFとは
ドキュメントによると、ELF(The Executable and Linking Format)は元々、UNIX System Laboratories(USL)がApplication Binary Interface(ABI)の一部として開発・公開されました。ABIは、同じプロセッサ環境内でアプリケーションが実行できるようにするインタフェースのことらしいです。The Tool Interface Standards committee(TIS)は、様々なOSの32bitインテルアーキテクチャ環境で動作するポートブルオブジェクトファイルフォーマットとして、進化したELF標準を選択しました。
ELF標準は複数のOSにまたがって拡張するバイナリインタフェース定義のセットを開発者に提供することで、ソフトウェア開発を簡素化することを目的としました。これにより、異なるインタフェース実装の数が減少し、コードの記録と再コンパイルの必要性が軽減されます。
つまり、たまたまABIとして作ったELFが一般に広まって、標準となっていったということでしょうかね。
あと、このドキュメントは32bit環境に焦点を置いているようです。

### オブジェクトファイル

ELFには主に3つのオブジェクトファイルが存在するようです。

1. relocatable file(再配置可能ファイル)  
   他のオブジェクトファイルでリンクするために適切なコードとデータを保持し、executable fileまたはshared object fileを作成します。
2. executable file(実行可能ファイル)  
   実行するための適切なプログラムを保持します。
2. shared object file(共有オブジェクトファイル)  
   2つのコンテキストでリンクするために適切なコードとデータを保持します。一つ目のリンクエディタは他のrelocatable fileとshared object fileを使って、他のオブジェクトファイルを作成します。二つ目の動的リンカはexecutable fileと他のshared object fileを結合し、プロセスイメージを作成します。

リンクプログラムと実行プログラムのファイルフォーマットは以下です。
[ELFドキュメントより]
![Screenshot from 2018-09-02 11-41-02.png](https://qiita-image-store.s3.amazonaws.com/0/254464/3f093631-6f34-d021-232b-422ea8eff3a5.png)

ELFヘッダはファイルの最初に存在し、ファイル構成を示す「ロードマップ」を保持しています。
セクションはリンクするために命令、データ、シンボルテーブル、再配置情報などの大部分のオブジェクトファイル情報を保持しています。
プログラムヘッダテーブルはプロセスイメージがどのように作られたかをシステムに通知します。プロセスイメージをビルドするために使用するファイルはプログラムヘッダテーブルを持つ必要があります。relocatable fileはこれを必要としません。
セクションヘッダテーブルはファイルのセクション情報を含んでいます。
各セクションはテーブルのエントリを持っています。
各エントリはセクション名、セクションサイズなどの情報を提供します。リンクするときに使用されるファイルはセクションヘッダテーブルを持つ必要があります。

### ELFヘッダ
ELFファイルに実際のサイズが含まれているため、一部のオブジェクトファイル制御構造を大きくすることが出来ます。オブジェクトファイルフォーマットが変更された場合、プログラムは制御構造が予想よりも大きいまたは小さくてもかまいません。

ここで私の環境(Ubuntu 16.04LTS)でのelf.h内のELFヘッダファイルを見てみます。

```sudo file / -name elf.h```で検索するといっぱいelf.hが出てきました。とりあえず、/usr/include/elf.hが位置的にリーダーっぽそうなので(リーダーってなんや笑)、これを参考にしていきます。

```c:elf.h
#define EI_NIDENT (16)

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf32_Half	e_type;			/* Object file type */
  Elf32_Half	e_machine;		/* Architecture */
  Elf32_Word	e_version;		/* Object file version */
  Elf32_Addr	e_entry;		/* Entry point virtual address */
  Elf32_Off	e_phoff;		/* Program header table file offset */
  Elf32_Off	e_shoff;		/* Section header table file offset */
  Elf32_Word	e_flags;		/* Processor-specific flags */
  Elf32_Half	e_ehsize;		/* ELF header size in bytes */
  Elf32_Half	e_phentsize;		/* Program header table entry size */
  Elf32_Half	e_phnum;		/* Program header table entry count */
  Elf32_Half	e_shentsize;		/* Section header table entry size */
  Elf32_Half	e_shnum;		/* Section header table entry count */
  Elf32_Half	e_shstrndx;		/* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf64_Half	e_type;			/* Object file type */
  Elf64_Half	e_machine;		/* Architecture */
  Elf64_Word	e_version;		/* Object file version */
  Elf64_Addr	e_entry;		/* Entry point virtual address */
  Elf64_Off	e_phoff;		/* Program header table file offset */
  Elf64_Off	e_shoff;		/* Section header table file offset */
  Elf64_Word	e_flags;		/* Processor-specific flags */
  Elf64_Half	e_ehsize;		/* ELF header size in bytes */
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;		/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */
  Elf64_Half	e_shnum;		/* Section header table entry count */
  Elf64_Half	e_shstrndx;		/* Section header string table index */
} Elf64_Ehdr;
```

上が32bit用で下が64bit用ですね。両方にサイズ以外の違いはなさそうです。

- e_ident  
  固定値といくつかの情報が示されているようです。
  - 0:EI_MAG0  
	固定値。ELFMAG0:0x7f
  - 1:EI_MAG1  
	固定値。ELFMAG1:'E'
  - 2:EI_MAG2  
	固定値。ELFMAG2:'L'
  - 3:EI_MAG3  
	固定値。ELFMAG3:'F'
  - 4:EI_CLASS  
	ファイルクラス。  
	無効クラス。ELFCLASSNONE:0  
	32bitオブジェクト。ELFCLASS32:1  
	64bitオブジェクト。ELFCLASS64:2  
  - 5:EI_DATA  
	データエンコーディング。
	無効なデータエンコーディング。ELFDATANONE:0  
	2の補数リトルエンディアン。ELFDATA2LSB:1  
	2の補数ビッグエンディアン。ELFDATA2MSB:2
  - 6:EI_VERSION  
	ELFヘッダバージョン。現在は下で説明するEV_CURRENTの値を持ちます。
  - 7:EI_OSABI  
	OS ABI識別情報。
	Unix System V ABI。ELFOSABI_NONE,ELFOSABI_SYSV:0  
	HP-UX。ELFOSABI_HPUX:1  
	NetBSD。ELFOSABI_NETBSD:2  
	GNU拡張ELF。ELFOSABI_GNU, ELFOSABI_LINUX:3  
	Sun Solaris。ELFOSABI_SOLARIS:6  
	IBM AIX。ELFOSABI_AIX:7  
	SGI Irix。ELFOSABI_IRIX:8  
	FreeBSD。ELFOSABI_FREEBSD:9  
	Compaq TRU64 Unix。ELFOSABI_TRU64:10  
	Novell Modesto。ELFOSABI_MODESTO:11  
	OpenBSD。ELFOSABI_OPENBSD:12  
	ARM EABI。ELFOSABI_ARM_AEABI:64  
	ARM。ELFOSABI_ARM:64  
	スタンドアロン組み込みアプリケーション。ELFOSABI_STANDALONE:255
  - 8:EI_ABIVERSION  
	ABIバージョン。
  - 9:EI_PAD  
	これはe_identで使用しないByteの開始位置を示します。これらのByteは予約済みで、0にセットされます。
- e_type  
  オブジェクトファイルタイプ。  
  ファイルタイプなし。ET_NONE:0  
  relocatable file。ET_REL:1  
  executable file。ET_EXEC:2  
  shared object file。ET_DYN:3  
  core file。ET_CORE:4  
- e_machine
  個々のファイルに必要なアーキテクチャを示します。  
  マシンなし。EM_NONE:0  
  AT&T WE 32100。EM_M32:1  
  SUN SPARC。EM_SPARC:2  
  Intel 80386。EM_386:3  
  Motorola m68k family。EM_68K:4  
  Motorola m88k family。EM_88K:5  
  Intel 80860。EM_860:6  
  MIPS R3000 big-endian。EM_MIPS:7  
  IBM System 370。EM_S370:8  
  ...  
  Tilera TILE-Gx。EM_TILEGX:191  
- e_version  
  オブジェクトファイルバージョン。  
  無効なELFバージョン。EV_NONE:0  
  現在のバージョン。EV_CURRENT:1
- e_entry  
  仮想アドレスのエントリーポイント。こんなところに書いてあったんですね！笑
- e_phoff  
  プログラムヘッダテーブルのファイルオフセット。
- e_shoff  
  セクションヘッダテーブルのファイルオフセット。
- e_flags  
  プロセッサ指定フラグ。
  PowerPC embedded flag。EF_PPC_EMB:0x80000000  
  PowerPC -mrelocatable flag。EF_PPC_RELOCATABLE:0x00010000  
  PowerPC -mrelocatable-lib。EF_PPC_RELOCATABLE_LIB:0x00008000  
- e_ehsize  
  ELFヘッダサイズ。
- e_phentsize  
  プログラムヘッダテーブルエントリサイズ。
- e_phnum  
  プログラムヘッダテーブルエントリカウント。
- e_shentsize  
  セクションヘッダテーブルエントリサイズ。
- e_shnum  
  セクションヘッダテーブルエントリカウント。
- e_shstrndx  
  セクション名文字列テーブルのインデックス。

では実際にELFヘッダを表示させてみたいと思います。

```c:elf_header.c
#include <stdio.h>
#include <elf.h>

int main(int argc, char *argv[]) {
  int i = 0;
  Elf64_Ehdr elf_header;

  FILE *fd = fopen(argv[1], "rb");
  if (fd) {
    fread(&elf_header, 1, sizeof(elf_header), fd);
    for (i=0; i<EI_NIDENT; i++) {
      printf("e_ident[%d]=0x%x\n", i, elf_header.e_ident[i]);
    }
    printf("e_type=0x%x\n", elf_header.e_type);
    printf("e_machine=0x%x\n", elf_header.e_machine);
    printf("e_version=0x%x\n", elf_header.e_version);
    printf("e_entry=0x%x\n", elf_header.e_entry);
    printf("e_phoff=0x%x\n", elf_header.e_phoff);
    printf("e_shoff=0x%x\n", elf_header.e_shoff);
    printf("e_flags=0x%x\n", elf_header.e_flags);
    printf("e_ehsize=0x%x\n", elf_header.e_ehsize);
    printf("e_phnum=0x%x\n", elf_header.e_phnum);
    printf("e_shentsize=0x%x\n", elf_header.e_shentsize);
    printf("e_shnum=0x%x\n", elf_header.e_shnum);
    printf("e_shstrndx=0x%x\n", elf_header.e_shstrndx);
    fclose(fd);
  }

  return 0;
}
```

C言語は久しぶりなので、合っていますでしょうか。
試しにこのプログラム自身で試すと、とりあえず表示はできました。
共有オブジェクトファイルなんですね。実行ファイルだと思っていました。
あと、e_flagsは値が入っていないですね。

次はSectionを見ていきます。

### Section
オブジェクトファイルのセクションヘッダテーブルを使用すると、すべてのファイルセクションを見つけることができます。ELFヘッダのe_shoffはセクションヘッダテーブルの始点からのオフセットです。e_shnumはセクションヘッダテーブルに含まれるエントリの数です。e_shentsizeは各エントリのByteサイズです。
一部のセクションヘッダテーブルインデックスは予約されています。

#### 特別セクションインデックス

- SHN_UNDEF  
  未定義セクション:0
- SHN_LORESERVE  
  予約インデックスの始点:0xff00
- SHN_LOPROC  
  プロセッサ固有の始点:0xff00
- SHN_BEFORE  
  Order section before all others (Solaris):0xff00 (分かりません)
- SHN_AFTER  
  Order section before all others (Solaris):0xff01 (分かりません)
- SHN_HIPROC  
  プロセッサ固有の終点:0xff1f
- SHN_LOOS  
  OS固有の始点:0xff20
- SHN_HIOS  
  OS固有の終点:0xff3f
- SHN_ABS  
  シンボルなどの対応する参照の絶対値を指定:0xfff1
- SHN_COMMON  
  共通シンボルの指定??:0xfff2
- SHN_XINDEX  
  余分のテーブルインデックス:0xffff
- SHN_HIRESERVE  
  予約インデックスの終点:0xffff

SectionはEFLヘッダ、プログラムヘッダテーブル、セクションヘッダテーブルを除くオブジェクトファイルのすべてのアクションの記載を含みます。

#### Section Header

```c:elf.h
typedef struct
{
  Elf32_Word	sh_name;		/* Section name (string tbl index) */
  Elf32_Word	sh_type;		/* Section type */
  Elf32_Word	sh_flags;		/* Section flags */
  Elf32_Addr	sh_addr;		/* Section virtual addr at execution */
  Elf32_Off	sh_offset;		/* Section file offset */
  Elf32_Word	sh_size;		/* Section size in bytes */
  Elf32_Word	sh_link;		/* Link to another section */
  Elf32_Word	sh_info;		/* Additional section information */
  Elf32_Word	sh_addralign;		/* Section alignment */
  Elf32_Word	sh_entsize;		/* Entry size if section holds table */
} Elf32_Shdr;

typedef struct
{
  Elf64_Word	sh_name;		/* Section name (string tbl index) */
  Elf64_Word	sh_type;		/* Section type */
  Elf64_Xword	sh_flags;		/* Section flags */
  Elf64_Addr	sh_addr;		/* Section virtual addr at execution */
  Elf64_Off	sh_offset;		/* Section file offset */
  Elf64_Xword	sh_size;		/* Section size in bytes */
  Elf64_Word	sh_link;		/* Link to another section */
  Elf64_Word	sh_info;		/* Additional section information */
  Elf64_Xword	sh_addralign;		/* Section alignment */
  Elf64_Xword	sh_entsize;		/* Entry size if section holds table */
} Elf64_Shdr;
```

- sh_name  
  セクション名(文字列テーブルインデックス)。
- sh_type  
  - SHT_NULL  
	未使用セクションヘッダテーブル始点:0
  - SHT_PROGBITS  
	プログラムデータ:1
  - SHT_SYMTAB  
	シンボルテーブル:2
  - SHT_STRTAB  
	文字列テーブル:3
  - SHT_RELA  
	再配置エントリ:4
  - SHT_HASH  
	シンボルハッシュテーブル:5
  - SHT_DYNAMIC  
	ダイナミックリンク情報:6
  - SHT_NOTE  
	ノート:7
  - SHT_NOBITS  
	データなしのプログラムスペース(bss):8
  - SHT_REL  
	再配置エントリ:9
  - SHT_SHLIB  
	予約済み:10
  - SHT_DYNSYM  
	ダイナミックリンクシンボルテーブル:11
  - SHT_INIT_ARRAY  
    コンストラクタの配列:14
  - SHT_FINI_ARRAY  
    デストラクタの配列:15
  - SHT_PREINIT_ARRAY  
	プリコンストラクタの配列:16
  - SHT_GROUP  
    Sectionグループ:17
  - SHT_SYMTAB_SHNDX  
	拡張Sectionインデックス:18
  - SHT_NUM  
	定義された型の番号
  (以下省略)
- sh_flags  
  セクションの属性のフラグ。
  - SHF_WRITE  
	書き込み可能:0x1(1<<0)
  - SHF_ALLOC  
	プロセス実行中にメモリを占有:0x2(1<<1)
  - SHF_EXECINSTR  
	実行可能:0x4(0<<2)
  - SHF_MERGE  
    マージされる可能性がある:0x8(0<<3)
  - SHF_STRINGS  
    NULL文字を含む文字列:0x16(0<<4)
  (以下省略)
- sh_addr  
  実行時のSection仮想アドレス
- sh_offset  
  Sectionのオフセット
- sh_size  
  SectionのByteサイズ
- sh_link  
  他のSectionへのリンク
- sh_info  
  追加Section情報
- sh_addralign  
  Sectionアライメント
- sh_entsize  
  Sectionテーブルを持つ場合のエントリサイズ

では、セクションヘッダを実際に見てみます。

```c:sh_header.c
#include <stdio.h>
#include <elf.h>

int main(int argc, char *argv[]) {
  int i = 0;
  Elf64_Ehdr elf_header;
  Elf64_Shdr sh_header;

  FILE *fd = fopen(argv[1], "rb");
  if (fd) {
    // elf header
    fread(&elf_header, 1, sizeof(elf_header), fd);
    printf("e_shoff=0x%x\n", elf_header.e_shoff);
    printf("e_shentsize=0x%x\n", elf_header.e_shentsize);
    printf("e_shnum=0x%x\n", elf_header.e_shnum);
    printf("e_shstrndx=0x%x\n", elf_header.e_shstrndx);

    // section header
    fseek(fd, elf_header.e_shoff, SEEK_SET);
    /* for (i=0; i<elf_header.e_shnum; i++) { */
    for (i=0; i<2; i++) {
      fread(&sh_header, 1, sizeof(sh_header), fd);
      printf("---------------------\n");
      printf("sh_name=0x%x\n", sh_header.sh_name);
      printf("sh_type=0x%x\n", sh_header.sh_type);
      printf("sh_flags=0x%x\n", sh_header.sh_flags);
      printf("sh_addr=0x%x\n", sh_header.sh_addr);
      printf("sh_offset=0x%x\n", sh_header.sh_offset);
      printf("sh_size=0x%x\n", sh_header.sh_size);
      printf("sh_link=0x%x\n", sh_header.sh_link);
      printf("sh_info=0x%x\n", sh_header.sh_info);
      printf("sh_addralign=0x%x\n", sh_header.sh_addralign);
      printf("sh_entsize=0x%x\n", sh_header.sh_entsize);
      printf("---------------------\n");
    }
    fclose(fd);
  }

  return 0;
}
```

すべて出力すると多かったので、2番目まで出力するようにしました。
elfヘッダにはセクションヘッダテーブルの始点とセクション数、セクションエントリサイズなどが格納されているので、それを利用しました。
一番初めのセクションヘッダテーブルには0しか格納されていませんでした。
セクション名(sh_name)には文字列テーブルへのインデックス番号が格納されていて、参照するとセクション名が得られるようです。

この他にもプログラムヘッダテーブルや文字列テーブル、シンボルテーブルなどが存在しますが、先へ進みたいのでここまでにします。必要になったら適宜参照していきます。

## まとめ
1. ELFには3つのオブジェクトファイルが存在する
2. ELFヘッダにはファイルの全体的な情報が格納されている
3. Sectionヘッダには各セクション情報が格納されている