SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

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

Windows実行ファイルのバイナリ概要

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

  • X ポスト
  • このエントリーをはてなブックマークに追加

PEヘッダ

 PEヘッダとは、EXEファイルの先頭部分の情報のことです。前項で解説したデータの一部はこのPEヘッダに格納されています。PEとは「Portable Executable」の略で、持ち運び可能な実行ファイルという意味です。同一プラットフォーム上であれば、どこからでも動作可能なことを考えると納得できるネーミングですね。なお、PEヘッダ自体はUnixなどで昔から採用されており、Windowsに対応するEXEが持ち合わせるPEヘッダは、Microsoftが拡張定義したものになっています。

EXEファイルのロード方法

 OSは、先ほど説明したMZシグネチャ、マシンタイプ、ネイティブコードなどの各情報をもとに、EXEファイルをメモリ上にロードし実行します。OSがEXEファイルを実行するまでの手順は次のようになります。

  1. シグネチャ、マシンタイプなどを確認します。EXEファイルが正規のフォーマットで記載されているかをチェックします(なお、このタイミングでデータが間違っているとデバッグすら実行しません)。
  2. EXEイメージをメモリ上にコピーします。
  3. PEヘッダに記載されているデータをもとに、EXEイメージの初期化を行います。
  4. PEヘッダで指定されたスタートポジション(エントリポイント)からプログラムを実行します。

重要なメモリ指定概念「RVA」

 EXEファイルに記載されるデータの中に、RVAというものがよく出現します。EXEファイルイメージは実行前にプロセスメモリ上の任意の場所にロードされますが、その任意の場所、すなわち、EXEイメージが読み込まれたメモリの先頭位置を「イメージベース」と呼びます。RVAとは「Relative Virtual Address」の略で、イメージベースからの相対オフセット値のことを言います。

 ここで気を付けたいのが、ファイル上に配置されるデータと、プロセスメモリ上に呼び出されたデータの格納場所が異なるという点です。つまり、EXEファイルを実行すると、ファイルポインタで読み込みの対象となるデータ位置を指定し、RVAで実際に読み出されたプロセスメモリ上のデータを指し示すことになります。

 EXEファイルに存在するデータのアドレスを、RVAを用いて指定する場合は、次の計算式が常に利用されます。

イメージベース + RVA = アドレス

PEヘッダよりも前にある情報

 実は先ほど筆者は、分かりやすさを優先させるために、少々正しくない表現で解説していました。「PEヘッダがEXEファイルの先頭にある」という表現をしていましたが、厳密には、PEヘッダの前にはMS-DOSのデータと互換性を持たせるためのデータが存在します。

 したがって、EXEファイルの先頭には、正しくは次のような順序で各種ヘッダデータが格納されています。

  1. IMAGE_DOS_HEADER
  2. MS-DOS用スタブ
  3. NULL空間
  4. PEヘッダ

 次項より、これらを一つずつ解説していきます。

IMAGE_DOS_HEADER構造体

 IMAGE_DOS_HEADER構造体はMS-DOSで認識可能なデータフォーマットです。もともとWindowsはMS-DOSの上位OSとして設計されてきた経緯があるため、Windows XPが主流の現在でも、データの内部には遺物として残っているようです。このIMAGE_DOS_HEADER構造体は、EXEファイルの0バイト目から保存されています。

 ちなみに、Visual C++をお持ちの方は、「winnt.h」でIMAGE_DOS_HEADER構造体の定義を見ることができますので、参照してみてください。次にその定義を示します。

0バイト目から保存されている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;

 IMAGE_DOS_HEADER構造体の中にある情報は、現在主流であるWindowsであまり扱われません。そのため、ほとんどが意味を持ちません。そもそも、e_lfanew以外のメンバはすべて16ビットです。これでは、アドレス空間の値などを指定することができません。しかし、次に挙げる情報は重要なので、チェックしておきましょう。

  • e_magicメンバ …… 必ず0x5A4Dを保持しています
  • e_lfanewメンバ …… PEヘッダが格納されているファイル位置を保持します

MS-DOS用スタブ

 MS-DOS用スタブは、万が一、MS-DOS上でプログラムを動作させてしまった場合に、MS-DOSのプログラムを実行するためのネイティブコードを保存できるスペースです。MS-DOS用スタブは、IMAGE_DOS_HEADERの直後に配置されます。

 当たり前のことですが、PEヘッダはWindowsに対応しているデータなので、MS-DOSでは読み込むことができません。MS-DOSで解釈可能なのは、IMAGE_DOS_HEADER構造体と、それに続くネイティブコード、つまり、このMS-DOS用スタブになります。

 このスタブデータは、一般的には「This program cannot be run in DOS mode.」という文字列を表示して終了するプログラムが保持されていますが、MS-DOS用にカスタマイズされたプログラムをこのスペースに格納しておいても問題はありません。ということは、Windows用と、MS-DOS用のソフトウェアを一つのEXEファイルに持たせることができるのか? という疑問を持たれる方もいるかもしれませんが、残念ながら筆者は実験したことがありません(しかしながら、このコード空間がそろそろ必要ない感じてしまうのは筆者だけでしょうか……)。

NULL空間

 MS-DOS用スタブの後にPEヘッダが配置されますが、PEヘッダの先頭位置は8バイト境界である必要があります。一般的には、0x0100バイト目までNULL空間が存在し、0x0100バイト目からPEヘッダが配置されます。

 EXEファイルには、このようなNULL空間が多数存在します。それは同時に、EXEファイルの内部には明確なバイト境界が要求されていることの裏付けとなります。

おわりに

 今回は、EXEファイルの概要として、MZシグネチャ、マシンタイプ、ネイティブコード、リソース、デバッグ情報などについて説明しました。これらの一部はPEヘッダと呼ばれる場所に格納されています。また、そのPEヘッダより前にあるEXEファイルの先頭には、IMAGE_DOS_HEADER構造体、MS-DOS用スタブなどがあり、それらがWindowsの元となったMS-DOS用のものであることなどを説明しました。

 次回は、「EXEファイルの特性が書き込まれているPEヘッダ部分」について解説します。

 PEヘッダには、ターゲットマシン、アライメント情報、セクション管理情報、メモリサイズ情報などが格納されており、OSがプログラムをメモリにロードしてEXEファイルを実行する際には、必要なデータがこれらの情報に随時書き込まれています。次回は、このPEヘッダの核心に迫ります。

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Windows実行ファイル「EXE」の謎に迫る連載記事一覧

もっと読む

この記事の著者

山本 大祐(ヤマモト ダイスケ)

普段はActiveBasicと周辺ツールの開発を行っています。最近は諸先輩方を見習いながら勉強中の身。AB開発日記

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/403 2006/06/16 00:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング