PEを理解したかったのでreadpeってものを作った

# はじめに
最近私は低レイヤーの勉強をしています.

ある時, IATがおかしいと実行時に怒られてしまいました.
私はその時, IATを直すことができなかったのでPEを理解したいと思い, 
`readpe`を自作しようと思いました.

`readpe`は**ELF**フォーマットを見やすくするための`readelf`の
**PE**版です.

今回実装したオプションは, 
`-h`, `-o`, `-S`です.


>私は普段`mingw-w64`というコンパイラを使っているのですが, 
>それを使うとMicrosoftのドキュメントの説明と
>異なる点があったため, `Visual Studio`のコンパイラを使いました.

# PEとは
Windowsで使用される実行ファイルのファイルフォーマット
COFFとも呼ばれる

## MS-DOS Header
前方互換をとるためのHeaderである

### DOS Header
- **Sigunature**<br>
&nbsp; &nbsp; 0x5A4D(MZ)が格納されている
- **Pointer to PE Header** (0x3C)<br>
&nbsp; &nbsp; PE Headerの先頭のアドレスが格納される

### DOS Stub
MS-DOS上で実行するためのコード
通常の場合は処理されず, MS-DOSの場合のみに処理される

#ここから
それではreadpeについて説明したいと思います.
`readpe`は**x86**/**x64**に対応しています.

**[ソースコード](https://github.com/3ts75/readpe)**はここから

## hello_x86.exe
今回の解析対象は`hello_x86.exe`です.
コンパイル前のファイルは下記の通りです.

```c:hello_x86.c
#include <stdio.h>

int main() {
    printf("Hello,World!\n");
    return 0;
}
```

これを`cl hello_x86.c -o hello_x86`コマンドでコンパイルします.

## -h

**COFF Header**を出力します.

これを見ることで, このファイルがいつコンパイルされたかなどの
基本的な情報がわかります.


このオプションでは見やすいようにするために,
フラグの部分を赤(1)と緑(0)で分けした.

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279138/e685eeae-eb2c-03ad-985a-13ae2d63046e.png)

<details><summary><strong>File Header</strong></summary><div>
- **Machine**
&nbsp; &nbsp; どのマシーンを対象として作られているかを示す
&nbsp; &nbsp; 基本的には0x8664(x64), 0x014C(x86)

- **NumberOfSections**
&nbsp; &nbsp; セクションの数

- **TimeDataStamp**
&nbsp; &nbsp; 1970年1月1日00:00からの秒数
&nbsp; &nbsp; ファイルが作成された日時を示す

- **PointerToSymbolTable**
&nbsp; &nbsp; 

- **NumberOfSymbols**
&nbsp; &nbsp; 

- **SizeOfOptionalHeader**
&nbsp; &nbsp; `Optionalheader`のサイズ

- **Characteristics**
&nbsp; &nbsp; ファイルの特性を示すフラグ
 - IMAGE_FILE_RELOCS_STRIPPED(0x0001)<br>
 &nbsp; &nbsp; ベース再配置情報が含まれないことを意味する
 - IMAGE_FILE_EXECUTABLE_IMAGE(0x0002)<br>
 &nbsp; &nbsp; 実行可能ファイルであることを意味する
 - IMAGE_FILE_LINE_NUMS_STRIPPED(0x0004)<br>
 &nbsp; &nbsp; ファイルから行番号が削除される
 - IMAGE_FILE_LOCAL_SYMS_STRIPPED(0x0008)<br>
 &nbsp; &nbsp; ファイルにシンボルテーブルが含まれない
 - IMAGE_FILE_AGGRESSIVE_WS_TRIM(0x0010)<br>
 &nbsp; &nbsp; Windows2000以降は廃止された
 - IMAGE_FILE_LARGE_ADDRESS_AWARE(0x0020)<br>
 &nbsp; &nbsp; 2GBを超えるアプリのアドレスをサポートする<br>
 &nbsp; &nbsp; x64の場合は既定でフラグが立っている
 - NULL(0x0040)
 - IMAGE_FILE_BYTES_REVERSED_LO(0x0080)
 - IMAGE_FILE_32BIT_MACHINE(0x0100) <br>
 &nbsp; &nbsp; x86の場合のみにフラグが立つ
 - IMAGE_FILE_DEBUG_STRIPPED(0x0200)<br>
 &nbsp; &nbsp; デバッグ情報を含まない
 - IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP(0x0400)<br>
 &nbsp; &nbsp; リムーバブルメディアから実行するときに, スワップファイル上にコピーして実行する
 - IMAGE_FILE_NET_RUN_FROM_SWAP(0x0800)<br>
 &nbsp; &nbsp; ネットワーク上から実行するときに, スワップファイル上にコピーして実行する
 - IMAGE_FILE_SYSTEM(0x1000)<br>
 - IMAGE_FILE_DLL(0x2000)<br>
 &nbsp; &nbsp; DLLファイルであるを意味する
 - IMAGE_FILE_UP_SYSTEM_ONLY(0x4000)<br>
 &nbsp; &nbsp; 単一のCPUのマシンでのみ実行可能である
 - IMAGE_FILE_BYTES_REVERSED_HI(0x8000)<br>

</div><details>

## -o

**Optional Header**を出力します.

このHeaderは実行時に必要な情報がたくさん含まれています.

**Optional Header**の中でもさらに3の主要な部分があります.
それを見やすくするために`[--<header part>--]`を付けました.

問題点として, これを出力した際にとても長くなってしまうので, 
さらにオプションを増やして, `header part`ごとに出力できるようにしたいです.

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279138/3c679ad4-f5a6-1a76-39e0-bbcfd193bab4.png)


### Standard Fields

実行ファイルをロードする際に必要な情報が含まれている

<details><summary><strong>Details</strong></summary><div>
- **Magic**<br>
&nbsp; &nbsp; 0x010Bの場合はx86, 0x020Bの場合はx64<br>
&nbsp; &nbsp; これからのサイズが変わる
- **MajorLinkerVersion**
- **MinorLinkerVersion**<br>
&nbsp; &nbsp; リンカーのバージョン
- **SizeOfCode**<br>
&nbsp; &nbsp; 実行可能なコードのサイズ
- **SizeOfInitializedData**<br>
&nbsp; &nbsp; 初期化済みのデータサイズ
- **SizeOfUninitializedData**<br>
&nbsp; &nbsp; 未初期化のデータサイズ
- **AddressOfEntryPoint**<br>
&nbsp; &nbsp; 実行を始めるEntry Pointのアドレス<br>
&nbsp; &nbsp; mainではない
- **BaseOfCode**<br>
&nbsp; &nbsp; コード領域のベースアドレス
- **BaseOfData**<br>
&nbsp; &nbsp; 初期化済みのデータの開始アドレス<br>
&nbsp; &nbsp; x86のみが持っている

</div></details>

###Windows-Specific Fields

リンカーとローダに必要な情報が含まれている

<details><summary><strong>Details</strong></summary><div>
- **ImageBase**<br>
&nbsp; &nbsp; ファイルがロードされると望ましいアドレス<br>
&nbsp; &nbsp; EXEでは0x004000000, DLLでは0x10000000
- **SectionAlignment**<br>
&nbsp; &nbsp; メモリにロードされるときに各セクションの開始アドレスが`SectionAlignment`の倍数になる
- **FileAlignment**<br>
&nbsp; &nbsp; ファイル内で各セクションの開始アドレスが`FileAlignment`の倍数になる
- **MajorOperatingSystemVersion**<br>
- **MinorOperatingSystemVersion**<br>
&nbsp; &nbsp; OSのバージョン
- **MajorImageVersion**<br>
- **MinorImageVersion**<br>
&nbsp; &nbsp; ファイルのバージョン
- **MajorSubsystemVersion**<br>
- **MinorSubsystemVersion**<br>
&nbsp; &nbsp; サブシステムのバージョン
- **Win32VersionValue**<br>
&nbsp; &nbsp; 今は使われていない
- **SizeOfImage**<br>
&nbsp; &nbsp; ファイルをロードした際のメモリを占めるサイズ
- **SizeOfHeaders**<br>
&nbsp; &nbsp; すべてのファイルヘッダーのサイズ
- **CheckSum**<br>
&nbsp; &nbsp; ロード時にファイルを確認する時に使う
- **Subsystem**<br>
&nbsp; &nbsp; ファイルのターゲットとなるサブシステム<br>
&nbsp; &nbsp; GUIなら0x02, CUIなら0x03
- **DllCharacteristics**<br>
 - IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA(0x0020)<br>
 &nbsp; &nbsp; 64bitのアドレス空間でASLRを有効<br>
 - IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE(0x0040)<br>
 &nbsp; &nbsp; ASLRを有効
 - IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY(0x0080)<br>
 &nbsp; &nbsp; コードのデジタル署名を強制的にする
 - IMAGE_DLLCHARACTERISTICS_NX_COMPAT(0x0100)<br>
 &nbsp; &nbsp; DEPが有効
 - IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION(0x0200)<br>
 &nbsp; &nbsp; アプリケーションの分離を持たない
 - IMAGE_DLLCHARACTERISTICS_ NO_SEH(0x0400)<br>
 &nbsp; &nbsp; 構造化例外ハンドラーを持たない
 - IMAGE_DLLCHARACTERISTICS_ NO_BIND(0x0800)<br>
 &nbsp; &nbsp; バインドできない
 - IMAGE_DLLCHARACTERISTICS_APPCONTAINER(0x1000)<br>
 &nbsp; &nbsp; AppContainer 上で実行される必要がある
 - IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER(0x2000)<br>
 &nbsp; &nbsp; WDMドライバーであることを表す
 - IMAGE_DLLCHARACTERISTICS_GUARD_CF(0x4000)<br>
 &nbsp; &nbsp; 制御フローガードが有効である
 - IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE(0x8000)<br>
 &nbsp; &nbsp; ターミナルサーバとの互換性がある
- **SizeOfStackReserve**<br>
&nbsp; &nbsp; スタック領域として予約するサイズ
- **SizeOfStackCommit**<br>
&nbsp; &nbsp; スタック領域としてコミットするサイズ
- **SizeOfHeapReserve**<br>
&nbsp; &nbsp; ヒープ領域として予約するサイズ
- **SizeOfHeapCommit**<br>
&nbsp; &nbsp; ヒープ領域として予約するサイズ
- **LoaderFlags**<br>
&nbsp; &nbsp; 今は使われていない
- **NumberOfRvaAndSizes**<br>
&nbsp; &nbsp; `Data Directories`のサイズ
</div></details>

###Data Directories
上位の4バイトはアドレスを指していて, 下位4バイトはサイズを表している

<details><summary><strong>Details</strong></summary><div>
- **Export Table**<br>
&nbsp; &nbsp; エクスポート情報(.edata)
- **Import Table**<br>
&nbsp; &nbsp; インポート情報(.idata)
- **Resouce Table**<br>
&nbsp; &nbsp; リソース(.rsrc)
- **Exception Table**<br>
&nbsp; &nbsp; 例外情報(.pdata)
- **Certificate Table**<br>
&nbsp; &nbsp; セキュリティ情報
- **Base Relocation Table**<br>
&nbsp; &nbsp; 再配置情報(.reloc)
- **Debug**<br>
&nbsp; &nbsp; デバッグ情報(.debug)
- **Architecture**<br>
&nbsp; &nbsp; 今は使われていない
- **Global Ptr**<br>
&nbsp; &nbsp; グローバルポインター<br>
&nbsp; &nbsp; 構造体サイズは0
- **TLS Table**<br>
&nbsp; &nbsp; TLS情報(.tls)
- **Load Config Table**<br>
&nbsp; &nbsp; ロード構成情報
- **Bound Import**<br>
&nbsp; &nbsp; バインドされたインポート情報
- **IAT**<br>
&nbsp; &nbsp; インポートアドレステーブル
- **Delay Import Descriptor**<br>
&nbsp; &nbsp; 遅延インポート情報
- **CLR Runtime Header**<br>
&nbsp; &nbsp; .NETメタデータ(.cormeta)
- **Reserved, must be zero**<br>
&nbsp; &nbsp; 今は使われていない
</div></details>

## -S

**Section Header**を出力します.


これを見ることで, 各セクションの位置や,
読み込み可能や書き込み可能などのフラグがわかります.

格子状にすることで見やすくなっています.
`charactorisics`の部分が見にくいのですがいい方法が思い浮かばなかったため,
とりあえずはそのままにしました.

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279138/4fcadedd-8dc1-fe04-aa92-104af5100ff7.png)

<details><summary><strong>Section Header</strong></summary><div>
- **Name**<br>
&nbsp; &nbsp; ASCIIで8バイト使う<br>
&nbsp; &nbsp; 余ると0x00が入る
- **VirtualSize**<br>
&nbsp; &nbsp; メモリにロードされた時に, このセクションがどれだけ占めるサイズ
- **VirtualAddress**<br>
&nbsp; &nbsp; メモリにロードされた時に, このセクションが配置されるアドレス
- **SizeOfRawData**<br>
&nbsp; &nbsp; ファイル内でのこのセクションサイズ
- **PointerToRawData**<br>
&nbsp; &nbsp; ファイル内でこのセクションのアドレス
- **PointerToRelocations**
- **PointerToLinenumbers**
- **NumberOfRelocations**
- **NumberOfLinenumbers**<br>
&nbsp; &nbsp; PEでは使われない
- **Charactorisics**
 - IMAGE_SCN_CNT_CODE (0x00000020)<br>
 &nbsp; &nbsp; 実行可能コードを含んでいる
 - IMAGE_SCN_CNT_INITIALIZED_DATA (0x00000040)<br>
 &nbsp; &nbsp; 初期化済みのデータが含まれている
 - IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x00000080)<br>
 &nbsp; &nbsp; 未初期化済みのデータが含まれている
 - IMAGE_SCN_LNK_INFO (0x00000200)<br>
 &nbsp; &nbsp; コメントやその他のデータを含まれている<br>
 &nbsp; &nbsp; .drectveセクションにある<br>
 &nbsp; &nbsp; オブジェクトファイルにのみ有効
 - IMAGE_SCN_LNK_REMOVE (0x00000800)<br>
 &nbsp; &nbsp; リンク時にイメージから取り除かれる<br>
 &nbsp; &nbsp; オブジェクトファイルにのみ有効
 - IMAGE_SCN_LNK_COMDAT (0x00001000)<br>
 &nbsp; &nbsp; COMDATデータが含まれる<br>
 &nbsp; &nbsp; オブジェクトファイルにのみ有効
 - IMAGE_SCN_GPREL (0x00008000)<br>
 &nbsp; &nbsp; グローバルポインターを介して参照するデータがある
 - IMAGE_SCN_ALIGN_1BYTES (0x00100000)
 - IMAGE_SCN_ALIGN_2BYTES (0x00200000)
 - IMAGE_SCN_ALIGN_4BYTES (0x00300000)
 - IMAGE_SCN_ALIGN_8BYTES (0x00400000)
 - IMAGE_SCN_ALIGN_16BYTES (0x00500000)
 - IMAGE_SCN_ALIGN_32BYTES (0x00600000)
 - IMAGE_SCN_ALIGN_64BYTES (0x00700000)
 - IMAGE_SCN_ALIGN_128BYTES (0x00800000)
 - IMAGE_SCN_ALIGN_256BYTES (0x00900000)
 - IMAGE_SCN_ALIGN_512BYTES (0x00A00000)
 - IMAGE_SCN_ALIGN_1024BYTES (0x00B00000)
 - IMAGE_SCN_ALIGN_2048BYTES (0x00C00000)
 - IMAGE_SCN_ALIGN_4096BYTES (0x00D00000)
 - IMAGE_SCN_ALIGN_8192BYTES (0x00E00000)<br>
 &nbsp; &nbsp; オブジェクトファイルにのみ有効
 - IMAGE_SCN_LNK_NRELOC_OVFL (0x01000000)<br>
 &nbsp; &nbsp; 拡張再配置が含まれている
 - IMAGE_SCN_MEM_DISCARDABLE (0x02000000)<br>
 &nbsp; &nbsp; 必要に応じて破棄できる
 - IMAGE_SCN_MEM_NOT_CACHED (0x04000000)<br>
 &nbsp; &nbsp; メモリ上でキャッシュできない
 - IMAGE_SCN_MEM_NOT_PAGED (0x08000000)<br>
 &nbsp; &nbsp; メモリ上でページングできない
 - IMAGE_SCN_MEM_SHARED (0x10000000)<br>
 &nbsp; &nbsp; メモリ上で共有できる
 - IMAGE_SCN_MEM_EXECUTE (0x20000000)<br>
 &nbsp; &nbsp; 実行可能
 - IMAGE_SCN_MEM_READ (0x40000000)<br>
 &nbsp; &nbsp; 読み込み可能
 - IMAGE_SCN_MEM_WRITE (0x80000000)<br>
 &nbsp; &nbsp; 書き込み可能
</div></details>

##まとめ
今回の学習により, 自分でPEを解析することができると思います.
次は**Packer**を作りたいと思います.



##参考
- [PE Format-Microsoft](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)
- [PEファイルフォーマットについて](https://qiita.com/cha1aza/items/f64dc4351517a2477ef1)
- [鷲ノ巣](https://tech.blog.aerie.jp/)
- [PE-OSDev Wiki](https://wiki.osdev.org/PE)