CodeZine(コードジン)

特集ページ一覧

EXEファイルの内部構造(PEヘッダ)

Windows実行ファイル「EXE」の謎に迫る 第2回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/06/23 00:00

目次

IMAGE_OPTIONAL_HEADER32構造体

 次は、同じくIMAGE_NT_HEADERS32に定義されている、IMAGE_OPTIONAL_HEADER32構造体について解説します。IMAGE_OPTIONAL_HEADER32構造体の定義内容は次のとおりです。

IMAGE_OPTIONAL_HEADER32構造体
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;

IMAGE_OPTIONAL_HEADER32構造体の各メンバについて

  • Magic
  • Magicにはマジックナンバーとなる値を指定します。何度も繰り返しますが、EXE内部には、シグネチャやマジックナンバーなどを始めとした「お決まりのルール」が多くあります。ここには、0x10Bを指定しておけば間違いありません(MSDNには0x107を指定するとROMイメージになると記載されています)。
  • MajorLinkerVersion、MinorLinkerVersion
  • MajorLinkerVersionMinorLinkerVersionはリンカのバージョンです。例えば、Visual C++ 6.0などでコンパイルされたEXEファイルは、MajorLinkerVersion=6MinorLinkerVersion=0などとなっています。
  • SizeOfCode
  • SizeOfCodeにはコードセクションのサイズを格納します。コードセクションとは、すなわち、ネイティブコードを保持するセクションのことを言います。
  • SizeOfInitializedData
  • SizeOfInitializedDataは初期化されたデータセクションのサイズです。一般的には、int a=10;など、初期値があらかじめ格納されているグローバル変数領域の割り当て用のバッファを「初期化されたデータセクション」とします。
  • SizeOfUninitializedData
  • SizeOfUninitializedDataは、SizeOfInitializedDataとは逆に、初期化されていないデータセクションのサイズです。多くの場合、0が指定されています。
  • AddressOfEntryPoint
  • AddressOfEntryPoint、このメンバはかなり重要です。コードセクションのどの位置からプログラムを開始するのかのエントリーポイントを格納します。エントリーポイントは、イメージベースからの相対オフセット値となります。イメージベースについてはImageBaseメンバで解説します。
  • BaseOfCode
  • BaseOfCodeはメモリにロードされたときのコードセクションの位置です。ファイル位置ではないので、注意が必要です。
  • BaseOfData
  • BaseOfDataはメモリにロードされたときのデータセクションの位置です。こちらもファイル位置ではないので、注意が必要です。
  • ImageBase
  • ImageBaseにはイメージベースの値を指定します。イメージベースとは、プロセスメモリ(0x00000000~0xFFFFFFFF)のどの位置にEXEファイルのイメージをロードするのかを取り決めた値です。64KBの倍数でなければならないのですが、一般的なEXEファイルでは0x00400000を指定します。その他、次のような値が指定されることがあります。
    • 0x00400000 …… 一般的なWindowsのEXEファイル
    • 0x10000000 …… DLLファイル
    • 0x00010000 …… Windows CEのEXEファイル
  • SectionAlignment
  • SectionAlignmentには、各セクションがメモリにロードされるときの境界をバイト単位で指定します。大きすぎるとアライメントを行うためのNULL空間が大きくなってしまうので、0x1000などの適度の値を指定しておきます。
  • FileAlignment
  • SectionAlignmentメンバがメモリ内におけるアライメントを指定したのに対し、FileAlignmentではファイル上のアライメントを指定します。この値も0x1000などとしておくのが一般的ではあるのですが、2KBなどの極小EXEファイルを作りたい場合は、0x100などとすると効果的です。
  • MajorOperatingSystemVersion、MinorOperatingSystemVersion
  • MajorOperatingSystemVersionMinorOperatingSystemVersionは、必要なOSのバージョンです。OSのバージョンといっても、Windows 2000やWindows XPのみを指定するわけにはいかないので、時代をさかのぼってバージョンを指定しましょう。そもそも、Windows 95で動くアプリケーションはWindows XPで動作しても良いはずです。
    ここで指定するバージョンは、Windowsのバージョンというよりも、Win32そのもののバージョンと言った方が良いのかもしれません。Windows 95~Windows XPは、Windows 3.1の後継の命令セットを持っているということで、OSのバージョンは4.0と指定します。ほとんどのEXEファイルは、MajorOperatingSystemVersion=4MinorOperatingSystemVersion=0となっています。
  • MajorImageVersion、MinorImageVersion
  • MajorImageVersionMinorImageVersionはEXEファイルのバージョンです。アプリケーションのバージョンを指定しておけば問題ないでしょう。この値はアプリケーションの動作に支障をきたすことがないため、面倒臭がりなコンパイラを利用すると、デフォルトで0が格納されることがあります。
  • MajorSubsystemVersion、MinorSubsystemVersion
  • MajorSubsystemVersionMinorSubsystemVersionはサブシステムのバージョンです。先ほどのOSのバージョンとほとんど同じ意味なので、4.0を指定しておけば間違いありません。ちなみに、Windows XPのサブシステムのバージョンは5.1なので、これよりも大きな値(5.2など)を指定すると、「有効なWin32イメージではありません」というエラーが出て、アプリケーションが起動できなくなります。Visual C++では、「/SUBSYSTEM リンカオプション」で、この値を指定できます。
    数年前まで、Win32VersionValueReserveという「将来のために予約されたメンバ」でした。このメンバに関する詳しい資料が見つからないため、定かなことは言えませんが、Win32のバージョン番号を指定するようです。0を指定おいても問題ありません。
  • SizeOfImage
  • SizeOfImageはメモリ上にロードされたときのEXEイメージの総バイト数です。Windows 9xではこの値を評価することはあまり無いようなのですが、Windows 2000/XPなどではこの値を元にメモリ確保を行うので、正確な値を指定する必要があります。
  • SizeOfHeaders
  • SizeOfHeadersにはヘッダ情報の総バイト数を指定します。
  • CheckSum
  • CheckSumにはチェックサムを指定します。一般的なEXEファイルではチェックが行われないので0を指定しておいても問題ありません。ブート時にロードされるものがDLL、DLLサーバーの場合には、正確なチェックサムを指定する必要があるようです。
  • Subsystem
  • Subsystemには表3で示す値のいずれかを指定します。この値一つでCUI、GUI、またはその他アプリであるのかが決まります。
    表3 サブシステムを示すフラグ
    名前説明
    IMAGE_SUBSYSTEM_UNKNOWN0未知のサブシステムです。
    IMAGE_SUBSYSTEM_NATIVE1デバイスドライバ、およびWindows NTネイティブのプロセスのために使用されます。
    IMAGE_SUBSYSTEM_WINDOWS_GUI2Windowsグラフィカルユーザーインタフェース(GUI)サブシステム内で実行されるイメージです。
    IMAGE_SUBSYSTEM_WINDOWS_CUI3Windowsの文字サブシステム内で実行されるイメージです。
    IMAGE_SUBSYSTEM_POSIX_CUI7Posix文字サブシステム内で実行されるイメージです。
    IMAGE_SUBSYSTEM_WINDOWS_CE_GUI9Windows CE上で実行されるイメージです。
  • DllCharacteristics
  • DllCharacteristicsはDLL特性に関する値を指定するものですが、一般的には0で問題ないようです。
  • SizeOfStackReserve、SizeOfStackCommit
  • SizeOfStackReserveSizeOfStackCommitには、それぞれスタックサイズ、コミットするスタックサイズを指定します。ローカル変数などで大きな値を指定するとプログラムが落ちてしまうという現象は、SizeOfStackReserveメンバやSizeOfStackCommitメンバが関係しています。
  • SizeOfHeapReserve、SizeOfHeapCommit
  • SizeOfHeapReserveSizeOfHeapCommitには、それぞれローカルヒープスペースのサイズ、コミットするローカルヒープスペースのサイズを指定します。
  • LoaderFlags
  • LoaderFlagsは現在は使われていないメンバです。0を指定して問題ありません。
  • NumberOfRvaAndSizes
  • NumberOfRvaAndSizesにはIMAGE_OPTIONAL_HEADER32構造体の直下に配置されるデータ ディクショナリエントリの数を指定します。一般的には16です。
  • DataDirectory
  • DataDirectoryにはデータディクショナリを指定します。詳細は次項の「データディクショナリ」で解説しています。

  • LINEで送る
  • このエントリーをはてなブックマークに追加

修正履歴

  • 2006/09/20 13:36 誤字修正:SectionAlignment⇒FileAlignment

  • 2006/06/12 17:59 「まとめ」を追加しました。

バックナンバー

連載:Windows実行ファイル「EXE」の謎に迫る

もっと読む

著者プロフィール

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5