はじめに
PEについて少し勉強してみようと思ったので、ほぼ受け売りになりますが自分用メモとしてまとめてみました。
用語や各フィールドの詳細などについてはこちらにまとめます。
内容は勉強が進み次第随時更新・修正する予定です。
誤りや補足等ありましたら、コメント欄にお願い致します。
PE(Portable Executable)とは
現在主流となっている、実行ファイルのフォーマットの一つです。
データ構造はWindows SDKに含まれるwinnt.h
で定義されています。
MS-DOS HEADER
MS-DOS REAL-MODE STUB PROGRUM
MS-DOS HEADER
_IMAGE_DOS_HEADER
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
MS-DOSとの互換性をとるためのものです。
Windows上でこのファイルを実行した際には、e_magic
とe_lfanew
の2つのフィールドのみ使われます。
- e_magic
-
マジックナンバー
0x5A4D
(MZ)が格納される。 - e_lfanew
-
NT HEADER
の位置を示す。
MS-DOS REAL-MODE STUB PROGRUM
MS-DOS HEADER
とNT HEADER
の間に位置します。
デフォルトで付加されるDOSスタブでは、MS-DOS上でこのファイルを実行した際に"This program cannot be run in DOS mode."と表示させます。
サイズは可変長です。
NT HEADER
NT HEADERS
_IMAGE_NT_HEADERS
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
#ifdef _WIN64
typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
#endif
マジックナンバーとヘッダのまとまりです。
Signature
、FileHeader
、OptionalHeader
の3つを持っています。
- Signature
-
マジックナンバー
0x50450000
(PE\0\0)が格納される。
0x50450000
でない場合は、別のフォーマットである。 - FileHeader
-
FileHeader
(構造体)。 - OptionalHeader
-
OptionalHeader
(構造体)。
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;
#define IMAGE_SIZEOF_FILE_HEADER 20
- Machine
- このファイルがどのマシンを対象として作られたものか表す値。
- NumberOfSections
- セクションの数を示す値。
- TimeDateStamp
- ファイルのタイムスタンプ。
- PointerToSymbolTable
NumberOfSymbols -
PEファイルはシンボルテーブルを含まないので
0
がセットされる。 - SizeOfOptionalHeader
-
OptionalHeader
のサイズ。 - Characteristics
- ファイルの特性を示すフラグの論理和。
OPTIONAL HEADER
_IMAGE_OPTIONAL_HEADER
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
COFFでは不要なことからOPTIONALという名前がついたそうです。
しかしPEでは必須となります。
サイズは可変長です。
- Magic
- マジックナンバー
0x10b
(PE32)、0x20b
(PE+)、0x107
(ROM)のいずれかの値をとる。
通常は32bit用であることを示す0x10b
である。
0x20b
の場合は64bit用のファイルとなり、構造やフィールドのサイズが異なる。 - MajorLinkerVersion
MinorLinkerVersion - リンカのバージョン情報。
- SizeOfCode
- コード領域のサイズ。
- SizeOfInitializedData
- 文字列リテラルなどの値が確定しているデータ領域のサイズ。
- SizeOfUninitializedData
- グローバル変数などの値が確定していないデータ領域のサイズ。
- AddressOfEntryPoint
-
エントリポイントのRVA。
実際のエントリポイントのアドレスは後述のImageBase
にAddressOfEntryPoint
を加算することで得られる。 - BaseOfCode
-
コード領域のベースアドレス。
RVAで記述されている。 - BaseOfData
-
グローバルアドレスなどの値が確定していないデータ領域のベースアドレス。
RVAで記述されている。 - ImageBase
-
ファイルがロードされるアドレス。
Microsoft製の開発環境ではデフォルトでEXEファイルは0x00400000
、DLLファイルは0x10000000
と設定されている。
リンカオプション等で変更可能。
ロードに失敗した場合、再配置が行われる。 - SectionAlignment
-
各セクションがロードされる際のアラインメント。
各セクションの開始アドレスはSectionAlignment
の倍数になる。
次のセクションの開始アドレスまでの余りの領域はNULL
で埋められる。 - FileAlignment
-
ファイル内で各セクションが配置される際のアラインメント。
各セクションの開始アドレスはFileAlignment
の倍数になる。
次のセクションの開始アドレスまでの余りの領域はNULL
で埋められる。 - MajorOperatingSystemVersion
MinorOperatingSystemVersion - 対象とされるOSのバージョン情報。
- MajorImageVersion
MinorImageVersion - このファイルのバージョン情報。
- MajorSubsystemVersion
MinorSubsystemVersion - サブシステムのバージョン情報。
- Recerved1
- 使用しない。
- SizeOfImage
-
プログラムをロードする際に必要なサイズ。
セクションが必要とするバイト数をページ境界、SectionAlignment
境界に揃えた合計から算出される。
ロードされる前の実行ファイルのサイズとは異なるので注意。 - SizeOfHeader
-
FILE HEADER
、MS-DOS HEADER
、NT HEADER
、SECTION TABLES
を含むヘッダのサイズ。
FileAlignmentの倍数でなければならない。 - CheckSum
-
ロード時にファイルを確認するのに使われるチェックサム。
値はリンカによってセット・確認されている。
アルゴリズムはIMAGEHELP.DLL
にて確認できる。 - Subsystem
-
ファイルのターゲットとなるサブシステム。
IMAGE_SUBSYSTEM_NATIVE
(1)ならデバイスドライバ、
IMAGE_SUBSYSTEM_WINDOWS_GUI
(2)ならGUI、IMAGE_SUBSYSTEM_WINDOWS_CUI
(3)ならCUIと判断できる。 - DllCharacteristics
- DLLの特性を示すフラグの論理和。
- SizeOfStackReserve
- スタック領域として予約されるサイズ。
- SizeOfStackCommit
- スタック領域としてコミットされるサイズ。
- SizeOfHeapReserve
- ヒープ領域として予約されるサイズ。
- SizeOfHeapCommit
- ヒープ領域としてコミットされるサイズ。
- LoaderFlag
- 廃止。
- NumberOfRvaAndSize
-
IMAGE_DATA_DIRECTRY
の数。
通常はIMAGE_NUMBEROF_DIRECTRY_ENTRIES
(16)。 - DataDirectory
-
IMAGE_DATA_DIRECTORY
構造体の配列。
構造体は各エントリがある位置のRVAとサイズで構成されている。
配列の長さは16
で、DataDirectory[0]
にはエクスポートテーブル、DataDirectory[1]
にはインポートテーブルなど、インデックスによって保持されるエントリの意味が決まっている。
_IMAGE_DATA_DIRECTORY
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
SECTION TABLE
SECTION DATA
SECTION TABLE
SECTION DATA
_IMAGE_SECTION_HEADER
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
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;
#define IMAGE_SIZEOF_SECTION_HEADER 40
セクションテーブルはセクションヘッダの配列です。
セクションヘッダにはセクションの特性やデータのオフセットなどの情報が収められています。
また、PEでは同じ名前や特性のセクションは一つにまとめられています。
セクションヘッダのPointerToRawData
とSizeOfRawData
に値が入っている場合のみセクションデータが存在します。
- Name
-
セクション名。
- VirtualSize
- セクションのサイズ。
- VirtualAddress
- セクションのRVA。
- PointerToRawData
-
セクションが初期値の場合、ファイル内オフセットが格納されている。
み初期化変数などデータがないセクションでは0
が格納される。 - PointerToRelocations
- PEファイルでは使用しない。
- PointerToLinenumbers
-
行番号へのオフセット。
古い形式のデバック情報であるため、通常は0
が格納される。
clに/Z7オプションを付けることで生成される。 - NumberOfRelocations
-
再配置情報の数。
PEファイルでは0
が格納される。 - NumberOfLinenumbers
- 行番号情報の数。
- Characteristics
- セクションの特性を表すフラグの論理和。