読者です 読者をやめる 読者になる 読者になる

PE ファイルについて (4) - IMAGE_SECTION_HEADER

第4回。
NT ヘッダーの順番通りなら今回は IMAGE_DATA_DIRECTORY なんですが、これは手を付けると長いので、ちょっと飛ばして先に IMAGE_SECTION_HEADER を説明しちゃいます。

PE ファイルの構造のおさらい

第 1 回に載せた図を再掲。

MS-DOS 領域
NT ヘッダー
セクション データ
その他のデータ

前回まで説明していた NT ヘッダーの後にはセクション データが続きます。
これまで毎回名前だけは出ていましたが、セクション データはヘッダーの後の PE ファイルのデータ本体で、セクションとは、その本体領域を用途ごとにいくつかに区切ったものです。
というわけで、先の図は、もうちょっと細かく書くとこうなります。

MS-DOS領域
NT ヘッダー
セクション データセクション 1
セクション 2
セクション n
その他のデータ

で、1 つのセクションにつき、1 つのセクション ヘッダーがあります。これが IMAGE_SECTION_HEADER 構造体です。
この構造体は、IMAGE_NT_HEADERS 構造体には含まれていません。ので、厳密には NT ヘッダーの一部ではないのですが、上の図では NT ヘッダーに含めてしまっています。
これもちゃんと書くと、こうなります。

MS-DOS領域IMAGE_DOS_HEADER
MS-DOS プログラム
NT ヘッダー
IMAGE_NT_HEADERS
NT シグネチャ
IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER
データ ディレクトリIMAGE_DATA_DIRECTORY[0]
IMAGE_DATA_DIRECTORY[1]
IMAGE_DATA_DIRECTORY[15]
セクション ヘッダーIMAGE_SECTION_HEADER[0]
IMAGE_SECTION_HEADER[1]
IMAGE_SECTION_HEADER[n]
セクション データセクション 1
セクション 2
セクション n
その他のデータ

セクションがいくつあるかは、第 2 回でやりましたが、IMAGE_FILE_HEADER::NumberOfSections に書かれています。
セクション ヘッダーは NT ヘッダーの直後にありますが、最初のセクション ヘッダーの位置を取得するために、IMAGE_FIRST_SECTION というマクロが定義されています。
これを使って、すべてのセクション ヘッダーを走査するコードは、こんな感じになります。

// 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);

// 最初のセクション ヘッダー
auto pSectionHeaders = IMAGE_FIRST_SECTION(pNtHeaders);

for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i)
{
	// i 番目のセクションヘッダー
	auto pSectionHeader = pSectionHeaders[i];
	// ...
}

IMAGE_SECTION_HEADER

退屈なメンバー逐次解説のお時間です。

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[8];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Name

各セクションは ASCII 文字で 8 文字までのユニークな名前を持ちます。
名前が 8 文字に満たない場合、余った部分はゼロで埋められますが、8 文字ぴったりの場合は NULL 終端文字がありませんので、文字列として取り扱う場合は注意が必要です。
リンカーオプション /SECTION や、#pragma section で制御できます。

PhysicalAddress

このセクションのファイル上でのアドレスだと思うのですが、PointerToRawData との違いは不明です。
また、VirtualSize との共用体になっており、どちらを使うかを見分ける方法もわかりません。
PE ファイルでは利用しないのかもしれません。

VirtualSize

PE ファイルがロードされた時に、このセクションが占めるメモリ上のサイズです。
SizeOfRawData より大きい場合、余剰領域はゼロで埋められます。

VirtualAddress

PE ファイルがロードされた時に、このセクションが配置されるメモリ上の位置です。
PE ファイルの先頭からの相対アドレスで示されます。

SizeOfRawData

このセクションのファイル中でのサイズです。

PointerToRawData

このセクションのファイル中での位置です。

PointerToRelocations

このセクションの再配置情報のアドレスです。
PE ファイルでは使用されません。

PointerToLinenumbers

このセクションの行番号情報のアドレスです。
PE ファイルでは使用されません。

NumberOfRelocations

このセクションの再配置情報の個数です。
PE ファイルでは使用されません。

NumberOfLinenumbers

このセクションの行番号情報の個数です。
PE ファイルでは使用されません。

Characteristics

いろいろなフラグです。

IMAGE_SCN_CNT_CODE (0x00000020)
このセクションが実行可能なコードを含むことを意味します。
IMAGE_SCN_CNT_INITIALIZED_DATA (0x00000040)
このセクションが初期化済みデータを含むことを意味します。
IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x00000080)
このセクションが未初期化データを含むことを意味します。
IMAGE_SCN_LNK_INFO (0x00000200)
このセクションがコメントやその他のデータを含むことを意味します。
COFF ファイルでのみ有効なフラグです。PE ファイルでは利用されません。
IMAGE_SCN_LNK_REMOVE (0x00000800)
このセクションがリンク時にイメージから取り除かれることを意味します。
COFF ファイルでのみ有効なフラグです。PE ファイルでは利用されません。
IMAGE_SCN_LNK_COMDAT (0x00001000)
このセクションが COMDAT データを含むことを意味します。
COFF ファイルでのみ有効なフラグです。PE ファイルでは利用されません。
IMAGE_SCN_NO_DEFER_SPEC_EXC (0x00004000)
このセクションに関する TLB エントリー中の投機的例外ハンドリングビットをリセットします?
よくわかりません。TLB というのは Translation Lookaside Buffer のことでしょうか。
IMAGE_SCN_GPREL (0x00008000)
このセクションにグローバル ポインター(GP)によって参照されるデータが含まれることを意味します。
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)
セクションデータが何バイト境界にアラインメントされるかを示します。
COFF ファイルでのみ有効なフラグです。PE ファイルでは利用されません。
IMAGE_SCN_LNK_NRELOC_OVFL (0x01000000)
このセクションが拡張再配置情報を含むことを意味します。
NumberOfRelocations フィールドは 16bit ですが、再配置情報の数が 16bit の上限を超える場合、このセクションに拡張再配置情報が置かれます。NumberOfRelocations フィールドは 0xffff に設定され、実際の再配置情報の個数は VirtualAddress フィールドに入ります。
IMAGE_SCN_MEM_DISCARDABLE (0x02000000)
このセクションがメモリ上で破棄可能とマークされます。
IMAGE_SCN_MEM_NOT_CACHED (0x04000000)
このセクションはメモリ上でキャッシュ出来ません。
IMAGE_SCN_MEM_NOT_PAGED (0x08000000)
このセクションはメモリ上でページングできません。
IMAGE_SCN_MEM_SHARED (0x10000000)
このセクションは複数のプロセスで共有されます。
IMAGE_SCN_MEM_EXECUTE (0x20000000)
このセクションは実行可能です。
IMAGE_SCN_MEM_READ (0x40000000)
このセクションは読み取り可能です。
IMAGE_SCN_MEM_WRITE (0x80000000)
このセクションは書き込み可能です。