PEファイルのバイナリ構造(7) インポート関数の列挙

# インポート関数

エクスポート関数に対して、インポート関数は幾つか種類が存在しています。
具体的には、

|値|定義名|説明|
|:--|:--|:--|
|1|IMAGE_DIRECTORY_ENTRY_IMPORT|インポートディレクトリ|
|11|IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT|境界インポートディレクトリ|
|12|IMAGE_DIRECTORY_ENTRY_IAT|インポートアドレステーブル|
|13|IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT|遅延インポートテーブル|

の4つとなりますが、ここでは他のファイルとの依存関係を確認できるインポートと遅延インポートを対象とします。

# IMAGE_DIRECTORY_ENTRY_IMPORT

セクションに関しては、エクスポート関数の定義位置の取得で行ったのとほぼ同じ手順で、配列の添え字だけ「IMAGE_DIRECTORY_ENTRY_IMPORT」として確認します。

```cpp
IMAGE_DATA_DIRECTORY& tData = tNT.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
DWORD nOffset = tData.VirtualAddress;
```
# IMAGE_IMPORT_DESCRIPTOR

エクスポートの時と同じ手順でセクションまで確認後、IMAGE_IMPORT_DESCRIPTORのポインタへとキャストします。

```cpp:
IMAGE_IMPORT_DESCRIPTOR* pImport = (IMAGE_IMPORT_DESCRIPTOR*)&(pFile[nOffset - delta]);
```

IMAGE_IMPORT_DESCRIPTORは以下の様に定義されています。

```cpp:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
	union {
		DWORD   Characteristics;
		DWORD   OriginalFirstThunk;
	} DUMMYUNIONNAME;
	DWORD   TimeDateStamp;
	DWORD   ForwarderChain;
	DWORD   Name;
	DWORD   FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
```

今回使用するのは以下の2つ。

|メンバ||
|:--|:--|
|Characteristics|配列の終端フラグ。0であれば終了。|
|Name|インポートしているモジュール名。|
|OriginalFirstThunk|インポート関数の名前テーブルへのアドレス。|

この2つはunionで定義されている上に同じサイズですので、全く同じ物になります。

実際の情報はIMAGE_IMPORT_DESCRIPTORの配列として格納されていますので、終端フラグを確認しながら列挙します。
また、インポートしているモジュールの名前もここで取得できますが、例によってANSI文字列ですので変換しておきます。

```cpp:
for (int i = 0; pImport[i].Characteristics != 0; ++i)
{
	IMAGE_IMPORT_DESCRIPTOR& tImport = pImport[i];
	CString szName = CA2T((char*)&(pFile[tImport.Name - delta]));
	_tprintf(TEXT("\n%s"),(LPCTSTR)szName);
}
```

## IMAGE_THUNK_DATA

次に実際の関数名を取得していきますが、こちらは32bitと64bitで別の構造体となっていますので、モジュールのアーキテクチャに対応した方を使って列挙していきます。

今回使用するのは以下の3つとなります。

|メンバ||
|:--|:--|
|u1.AddressOfData|インポート関数の名前情報が含まれる構造体へのアドレス情報。0ならば終了。|
|u1.Function|インポート関数のへのアドレス情報。こちらも0ならば終了。|
|u1.Ordinal|インポート関数の序数。|

AddressOfDataには次の「IMAGE_IMPORT_BY_NAME」へのアドレスが含まれています。
ただ名前が無い場合もあるので、以下の方法で名前があるかどうかを確認してから情報を取得します。

インポート関数では名前でのインポートと序数でのインポート（名前無しのインポート）の2つがありますが、その判定にはOrdinalを使用します。
具体的には先頭のビットが立っているかどうかで判定できるのですが、それを補助する為にIMAGE_SNAP_BY_ORDINAL32、IMAGE_SNAP_BY_ORDINAL64というマクロが用意されています。
これらの判定用のビットを落とす為にIMAGE_ORDINAL32、IMAGE_ORDINAL64というマクロも用意されていますので、正しい序数を取得する為にそちらを使用します。

## IMAGE_IMPORT_BY_NAME

インポート関数の名前はこちらの構造体で取得します。

|メンバ||
|:--|:--|
|Name|インポート関数の名前。ANSIで保存。|
|Hint|インポート関数の序数。|

上記を踏まえたサンプルコードは以下の様な形になります。

32bitの場合

```cpp:
IMAGE_THUNK_DATA32* pINT = (IMAGE_THUNK_DATA32*)&(pFile[tImport.OriginalFirstThunk - delta]);
IMAGE_THUNK_DATA32* pIAT = (IMAGE_THUNK_DATA32*)&(pFile[tImport.FirstThunk - delta]);
for (int j = 0; pINT[j].u1.AddressOfData != 0 && pIAT[j].u1.Function != 0; ++j)
{
	if (IMAGE_SNAP_BY_ORDINAL32(pINT[j].u1.Ordinal))
	{
		_tprintf(TEXT("\n\t(NONAME)[%d]"), (int)IMAGE_ORDINAL32(pINT[j].u1.Ordinal));
	}
	else
	{
		IMAGE_IMPORT_BY_NAME& tName = (*(IMAGE_IMPORT_BY_NAME*)&(pFile[pINT[j].u1.AddressOfData - delta]));
		_tprintf(TEXT("\n\t%s[%d]"), (LPCTSTR)CA2T(tName.Name), tName.Hint);
	}
}
```

64bitの場合

```cpp:
IMAGE_THUNK_DATA64* pINT = (IMAGE_THUNK_DATA64*)&(pFile[tImport.OriginalFirstThunk - delta]);
IMAGE_THUNK_DATA64* pIAT = (IMAGE_THUNK_DATA64*)&(pFile[tImport.FirstThunk - delta]);
for (int j = 0; pINT[j].u1.AddressOfData != 0 && pIAT[j].u1.Function != 0; ++j)
{
	if (IMAGE_SNAP_BY_ORDINAL64(pINT[j].u1.Ordinal))
	{
		_tprintf(TEXT("\n\t(NONAME)[%d]"), (int)IMAGE_ORDINAL64(pINT[j].u1.Ordinal));
	}
	else
	{
		IMAGE_IMPORT_BY_NAME& tName = (*(IMAGE_IMPORT_BY_NAME*)&(pFile[pINT[j].u1.AddressOfData - delta]));
		_tprintf(TEXT("\n\t%s[%d]"), (LPCTSTR)CA2T(tName.Name), tName.Hint);
	}
}
```

これでインポートしているモジュールの列挙は完了です。
しかし、ここには遅延ロードしているモジュールの名前が存在していません。

と言う事で次回は遅延ロードしている関数の情報を取得したいと思います。