はじめに
皆さんは、アプリケーションを起動させるEXEファイルの中身について考えてみたことはありますか? 本稿では、EXEファイルの内部構造について解説していきます。特にEXEファイルに関する日本語の資料が少ないのが現状です。そのため、解析に手を出してみたいと思っても挫折してしまった方も少なくないのではないでしょうか。本稿は、筆者自身の経験を踏まえ、実際にEXEファイルの解析ができるようになるための資料となるよう解説していきます。
前回、第1回では、EXEファイルの概要として、MZシグネチャ、マシンタイプ、ネイティブコード、リソース、デバッグ情報などについて説明しました。これらの一部はPEヘッダと呼ばれる場所に格納されています。また、そのPEヘッダより前にあるEXEファイルの先頭には、IMAGE_DOS_HEADER
構造体、MS-DOS用スタブなどがあり、それらがWindowsの元となったMS-DOS用のものであることなどを説明しました。
今回は、EXEファイルの特性が書き込まれているPEヘッダ部分について解説します。
PEヘッダのフォーマット
PEヘッダ(Portable Executable)ヘッダは、IMAGE_NT_HEADERS32
という構造体にその内容が定義されています。IMAGE_NT_HEADERS32
構造体は「winnt.h」に定義されているので、Visual C++の開発環境のある方は参照してみてください。次にその定義を示します。
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
Signatureメンバ
どうやらEXEファイルは、シグネチャが好きなようです。EXEの先頭2バイトに「MZ」 というシグネチャが出てきたにもかかわらず、PEヘッダの先頭部分にも、やはりシグネチャが存在します。ここには、0x00004550「PE00」という値が保持されます。ちょっと注意が必要なのは、「MZ」と異なり、4バイトであるという点です。PEヘッダ以降のデータは、32ビットのWindows OS(以下、Win32)を意識して決定付けられているため、4バイトデータの出現率が高くなります。
ちょっと余談になりますが、実はEXEファイルは大きな構造体だと言っても過言ではありません(既にお気づきの方もいるかもしれませんね)。普段皆さんが利用する構造体と大きく異なるのは、可変長のデータが多いといったところでしょうか。ネイティブコードのサイズがどれほどのものになるかは、各アプリケーションごとに異なるので、単純な構造体としてEXEファイルを定義することはできませんが、確定的なデータの並び、メンバの並びに準じたフォーマットであることは間違いないようです。
IMAGE_FILE_HEADER構造体
IMAGE_NT_HEADERS32
に定義されている、IMAGE_FILE_HEADER
構造体について解説します。どんどん深い内容に入っていきますが、頑張っていきましょう。IMAGE_FILE_HEADER
構造体の定義内容は次のとおりです。
typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
IMAGE_FILE_HEADER構造体の各メンバについて
Machine
メンバではEXEファイルがどのようなマシン上での動作を想定しているのか、次のフラグから指定されます。
名前 | 値 | 説明 |
IMAGE_FILE_MACHINE_UNKNOWN | 0x0 | あらゆるマシンタイプに適用可能だと仮定される内容 |
IMAGE_FILE_MACHINE_I386 | 0x14c | Intel 386以降およびその互換プロセッサ |
IMAGE_FILE_MACHINE_R3000 | 0x162 | |
IMAGE_FILE_MACHINE_R4000 | 0x166 | MIPS(r)リトルエンディアン |
IMAGE_FILE_MACHINE_R10000 | 0x168 | |
IMAGE_FILE_MACHINE_ALPHA | 0x184 | Alpha AXP(tm) |
IMAGE_FILE_MACHINE_M68K | 0x268 | Motorola 68000シリーズ |
IMAGE_FILE_MACHINE_POWERPC | 0x1f0 | Power PC、リトルエンディアン |
IMAGE_FILE_MACHINE_SH3 | 0x1a2 | 日立SH3 |
IMAGE_FILE_MACHINE_SH4 | 0x1a6 | 日立SH4 |
IMAGE_FILE_MACHINE_ARM | 0x1c0 |
- NumberOfSections
- TimeDateStamp
- PointerToSymbolTable、NumberOfSymbols
- SizeOfOptionalHeader
- Characteristics
NumberOfSections
にはEXEファイルが保持するセクションの数が保持されます。セクションについての詳細は後述します。TimeDateStamp
にはEXEファイルが作成された日時が保持されます。PointerToSymbolTable
、NumberOfSymbols
にはそれぞれ、シンボルテーブルのファイル位置、シンボルの数が保持されます。シンボルテーブルを使用しない一般的なEXEファイルでは、0が保持されています。SizeOfOptionalHeader
には、この後に出てくるIMAGE_OPTIONAL_HEADER32
構造体のサイズが保持されます。当たり前の話になってしまいますが、sizeof(IMAGE_OPTIONAL_HEADER32)
です。Characteristics
にはファイルの属性値を指定します。主に、次のようなフラグが使用されることが多いです(より詳しい情報は、MSDNを検索してみると良いでしょう)。名前 | 値 | 説明 |
IMAGE_FILE_EXECUTABLE_IMAGE | 0x0002 | イメージのみを示します。イメージファイルが有効であり、実行され得ることを示します。このフラグがセットされていない場合は、一般にリンカのエラーです。 |
IMAGE_FILE_LINE_NUMS_STRIPPED | 0x0004 | COFF行番号が削除されています。 |
IMAGE_FILE_LOCAL_SYMS_STRIPPED | 0x0008 | ローカルシンボルのためのCOFFシンボルが削除されています。 |
IMAGE_FILE_32BIT_MACHINE | 0x0100 | 32ビットアーキテクチャのマシン。 |
IMAGE_FILE_DLL | 0x2000 | イメージファイルは、ダイナミックリンクライブラリ(DLL)です。これらのファイルはほとんどあらゆる目的で実行ファイルと見なされますが、直接実行することはできません。 |