この記事は Windows & Microsoft技術 基礎 Advent Calendar 2015 の 23 日目です。
今度は期日前に書き上げましたよ!
前回は肝心なところで終わってしまいました。
今回は続きの NT ヘッダー編から。
と言ってもですね、NT ヘッダーを全部解説し終わったら、PE ファイルの解説なんて 9 割方終わったようなもんです。
なので、しばらくは NT ヘッダーの解説が続きます。
dumpbin
ヘッダー構造の解説の前に dumpbin ツールについて紹介します。
dumpbin は PE ファイル*1の構造をダンプ出来るツールで、Windows SDK に含まれています。
dumpbin /headers <PEファイル>
と打つと、PE ファイルのヘッダーを表示することができます。
NT ヘッダー
NT ヘッダーは IMAGE_NT_HEADERS という構造体で表される構造をしています。
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
3 つのメンバーから成りますが、うち 2 つはさらに構造体になっています。
順番に解説していきますが、今回は最初の 2 つまでにします。
そうそう。前回載せたコードの一部を再掲しますが、NT ヘッダーは(ファイルの先頭を起点として)IMAGE_DOS_HEADER::e_lfanew が指す位置にあります。
// pvBase はメモリマップドファイルの先頭を指すポインターだとする auto pDosHeader = static_cast<IMAGE_DOS_HEADER *>(pvBase); if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { printf("Invalid MS-DOS signature."); return; } // ここが NT ヘッダー auto pNtHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(static_cast<LPBYTE>(pvBase) + pDosHeader->e_lfanew);
Signature
NT ヘッダーの先頭にある 4 バイトです。
ASCII コードで言うと "PE\0\0" という 4 文字です(\0 は NUL 文字)。
これは winnt.h 中で IMAGE_NT_SIGNATURE という名前で定義されています。
前回紹介した IMAGE_DOS_SIGNATURE と併せて、このファイルが PE ファイルかどうかを判定するのに使います。
FileHeader
IMAGE_FILE_HEADER 構造体で表されます。
PE ファイルは COFF (Common Object File Format)という形式を拡張しており、IMAGE_FILE_HEADER 構造体は COFF ファイルのヘッダーでもあります。
ちなみに COFF 形式は VC++ でソースコードをコンパイルした後、リンクする前の *.obj ファイルの形式としても利用されています。
以下、各メンバーの解説。
Machine
この PE ファイルがターゲットとする CPU のアーキテクチャーを表します。
x86(32bit)の場合は 0x014c、x64(64bit)の場合は 0x8664 です。
それ以外にも IA64 や ARM から MIPS、ALPHA、PowerPC、SH5 など、様々な値が定義されています。
VC++ では、リンカーオプション /MACHINE で指定します。
NumberOfSections
このファイルに含まれるセクションの数を表します。
セクションとは何かについては、次々回あたりで説明します。
簡単に言うと、ヘッダーの後にあるデータ本体領域を用途別に区切ったものです。
TimeDateStamp
このファイルの作成時刻です。
32bit の Unix 形式で表されます。
PointerToSymbolTable
COFF シンボルテーブル(よくわかんない)へのポインターです。
PE ファイルの場合は 0 のようです。
NumberOfSymbols
COFF シンボルテーブルのシンボルの数です。
PE ファイルの場合は 0 のようです。
SizeOfOptionalHeader
オプショナル ヘッダーのサイズです。
次回取り上げますが、IMAGE_OPTIONAL_HEADER 構造体のサイズになります。
PE ファイルとしては必須のヘッダーですが、Optional という名前なのは、COFF ファイルでは必須ではないからです。
*.obj ファイルではこのメンバーは 0 になります。
Characteristics
いろいろなフラグです。
- IMAGE_FILE_RELOCS_STRIPPED (0x0001)
- このファイルにベース再配置情報が含まれないことを意味します。
- 再配置についてはそのうち取り上げます。*2
- VC++ ではリンカーオプション /FIXED を有効にするとこのフラグが立ちます。
- IMAGE_FILE_EXECUTABLE_IMAGE (0x0002)
- このファイルが実行可能であることを意味します。
- PE ファイルでは通常 ON です。
- IMAGE_FILE_LINE_NUMS_STRIPPED (0x0004)
- このファイルに COFF 行番号情報が含まれないことを意味します。
- PE ファイルでは OFF のようですが、おそらく COFF ファイルの場合にのみ意味があるフラグだと思います。
- IMAGE_FILE_LOCAL_SYMS_STRIPPED (0x0008)
- このファイルに COFF シンボルテーブルが含まれないことを意味します。
- PE ファイルでは OFF のようですが、おそらく COFF ファイルの場合にのみ意味があるフラグだと思います。
- IMAGE_FILE_AGGRESIVE_WS_TRIM (0x0010)
- このファイルが動作中にメモリ(ワーキング セット)を積極的に縮小することを意味する…んでしょうか?
- 廃止された(Obsolete)フラグです。
- IMAGE_FILE_LARGE_ADDRESS_AWARE (0x0020)
- アプリケーションが 2GB を超えるアドレスをサポートしていることを意味します。
- 64bit アプリケーションでは既定で ON になります。
- VC++ ではリンカーオプション /LARGEADDRESSAWARE で制御できます。
- IMAGE_FILE_BYTES_REVERSED_LO (0x0080)
- よくわかりません。
- 廃止されたフラグです。
- IMAGE_FILE_32BIT_MACHINE (0x0100)
- 32bit 用のファイルであることを意味します。
- x86 用のファイルではこのフラグが立ちます。
- IMAGE_FILE_DEBUG_STRIPPED (0x0200)
- このファイルにデバッグ情報が含まれないことを意味します。
- VC++ では *.pdb ファイルを生成しない設定にしても、このフラグは立たないようです。
- IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP (0x0400)
- このファイルを CD-ROM などのリムーバブルメディアから実行する時に、スワップファイル上にコピーしてから実行します。
- 通常、PE ファイルはメモリマップドファイルとしてロードされますが、一旦スワップファイル上にコピーすることで高速に動作するようになります。
- VC++ ではリンカーオプション /SWAPRUN:CD で有効にできます。
- IMAGE_FILE_NET_RUN_FROM_SWAP (0x0800)
- このファイルをネットワーク上から実行する時に、スワップファイル上にコピーしてから実行します。
- VC++ ではリンカーオプション /SWAPRUN:NET で有効にできます。
- IMAGE_FILE_SYSTEM (0x1000)
- システムファイルであることを表します。
- どういう意味があるのかはよくわかりません。
- IMAGE_FILE_DLL (0x2000)
- このファイルが EXE ではなく DLL であることを意味します。
- IMAGE_FILE_UP_SYSTEM_ONLY
- このファイルが単一 CPU のマシンでのみ実行できることを意味します。
- VC++ ではリンカーオプションの /DRIVER:UPONLY で制御できそうですが、EXE ファイルでは出番はないと思います。
- IMAGE_FILE_BYTES_REVERSED_HI (0x8000)
- よくわかりません。
- 廃止されたフラグです。
*1:COFF ファイルも可
*2:kekyoさんの記事にちらっと出てくる「リロケーション」もこれのことです。