リソースセクション(.rsrc)
リソースとは
リソースは、EXEファイルに埋め込まれるアイコンや、ビットマップ画像、音楽など、実行コードに直接的に関係のない特徴的なデータを管理しています。リソースデータとしては、一般的に次のようなデータが書き込まれることがあります。もちろん、この他のデータについては、カスタムリソース(バイナリデータ)という扱いにして、EXEファイル内に埋め込むことができます。
- ビットマップ
- アイコン
- カーソル
- ダイアログ ボックス デザイン
- メニュー
- 文字列
リソースディレクトリテーブル
リソースセクションは、リソースがどのような構成になっているのかを示すリソースディレクトリテーブル、各リソースの生データが格納されるバイナリ部分の大きく2つに分かれます。
まずはリソースの全体構造を示すためのリソースディレクトリを説明します。リソースディレクトリは3階層のツリー構造で構成され、各階層には次のようなデータが格納されます。
- 1階層目 …… データの型
- 2階層目 …… ID(または名前)
- 3階層目 …… 言語
どの階層もディレクトリ表記のフォーマットは同一です。1つの階層のリソースディレクトリは次の構造体で表現できます。
IMAGE_RESOURCE_DIRECTORY
構造体 …… 各階層の先頭に1つだけ存在します。IMAGE_RESOURCE_DIRECTORY_ENTRY
構造体 …… 指定するデータの数だけ存在します。
これは筆者の感想ですが、リソースセクションにおけるデータ群は、EXEファイル内に存在する各種構造体の中でも、一番理解しにくいものになっています。文章では表現しづらい部分もあるので、大よその構造は以下の図を参考にしてください。
次に、IMAGE_RESOURCE_DIRECTORY
構造体の定義内容を示します。
typedef struct _IMAGE_RESOURCE_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; WORD NumberOfNamedEntries; WORD NumberOfIdEntries; // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; } IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
Characteristics
は予約されているので0です。TimeDateStamp
、MajorVersion
、MinorVersion
はそれぞれ作成時刻、バージョンになりますが、プログラム的にはほとんど意味を持ちません。多くのアプリケーションでは0を格納しています。
この構造体で重要なのは、NumberOfNamedEntries
、およびNumberOfIdEntries
です。後に続くIMAGE_RESOURCE_DIRECTORY_ENTRY
構造体の数を示します。一般的には、各データをIDで識別することが多いため(データの量も少なくて済みます)、NumberOfNamedEntries
に0をセットし、NumberOfIdEntries
に値をセットしているケースを多く見かけます。
次に、IMAGE_RESOURCE_DIRECTORY_ENTRY
構造体の定義内容を示します。
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { union { struct { DWORD NameOffset:31; DWORD NameIsString:1; }; DWORD Name; WORD Id; }; union { DWORD OffsetToData; struct { DWORD OffsetToDirectory:31; DWORD DataIsDirectory:1; }; }; } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
Name
またはId
には任意のデータを指定できます。任意のデータとは、先ほど解説した階層ごとのデータ型、ID、言語などのことです。
OffsetToData
には、接続される次の階層のデータの位置をRVAで指定します。上位ビットが1のときは次のデータはリソースディレクトリテーブル内であることが、0のときはリソースデータエントリ(バイナリデータに直結するデータ)であることが示されます。
リソースディレクトリテーブル1階層目(データの型)
IMAGE_RESOURCE_DIRECTORY_ENTRY
構造体のName
メンバには次に挙げる値のいずれかを指定します。
- RT_CURSOR
- RT_BITMAP
- RT_ICON
- RT_MENU
- RT_DIALOG
- RT_STRING
- RT_FONTDIR
- RT_FONT
- RT_ACCELERATOR
- RT_RCDATA
- RT_MESSAGETABLE
- RT_GROUP_CURSOR
- RT_GROUP_ICON
- RT_VERSION
- RT_DLGINCLUDE
- RT_PLUGPLAY
- RT_VXD
- RT_ANICURSOR
- RT_ANIICON
- RT_HTML
リソースディレクトリテーブル2階層目(ID)
ここには、ツリー構造の最終的なリーフを見分けることができる個別のIDを指定します。
リソースディレクトリテーブル3階層目(言語)
リソースデータの言語を指定します。日本語であれば0x0411を、英語であれば0x409を指定します。
リソースデータエントリ
リソースデータエントリは、リソースディレクトリテーブルの次に配置されます。各バイナリデータの概要を、IMAGE_RESOURCE_DATA_ENTRY
構造体を羅列することで表現しています。
typedef struct _IMAGE_RESOURCE_DATA_ENTRY { DWORD OffsetToData; DWORD Size; DWORD CodePage; DWORD Reserved; } IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
OffsetToData
には対象となるリソースバイナリの位置をRVAで指定します。Size
にはそのバイナリのサイズを指定します。CodePage
にはコードページの値を指定しますが、あまり使われるシーンはないようです。最後の、Reserved
は0を指定します。
バイナリデータ
3階層からなるリソースディレクトリテーブル、バイナリの位置とサイズを保持するリソースデータエントリを経て、やっと、バイナリデータにたどり着きます。このバイナリデータに、ビットマップやアイコンなどのファイルデータが直接書き込まれることになります。
おわりに
今回はセクションヘッダと、インポート・リソースなどの実践的なセクションの生データに関する解説を行いました。ここまで本稿を熟読された方、お疲れ様でした。EXEファイルの大まかなデータフォーマットに関しての解説はここで一区切りです。
ということは、自動的にある1つの目標がチラつくはずです。それは……
「EXEファイルを自前で生成してしまうモジュールを作ってしまおう!」
そうです! ここまでご紹介した内容を活用すれば、EXEファイル内に存在する構造体、その他データを自分で作成したプログラムに任意に持たせることができます。それらのデータをファイルに書き込めば完全オリジナルのEXEファイルが生成できるのです。
次回は、コンパイラでもリンカでもないのにEXEファイルを生成できてしまうという、これまた摩訶不思議なプログラムを自作してみます。ご期待ください!