IATを列挙

# 諸注意
自分用なのでもしかしたら間違ってるかもしれない…

# IATの概要

## IATの場所
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/a3dcf7a1-418d-5178-0651-ba308a01de71.png)
インポートセクションのインポートテーブルは `IMAGE_IMPORT_DESCRIPTOR` 構造体の配列として表現されていて、インポートしているDLLごとに用意されてる( `IMAGE_IMPORT_DESCRIPTOR` の `Name` メンバを見れば何の名前のDLLの `IMAGe_IMPORT_DESCRIPTOR` なのかわかる)。
その中の `FirstThunk` メンバがIATのアドレスを保持している。

## IATの状態について
IATはプログラムがロードされる前と後で入ってるものが違う。

* ロード前：`IMAGE_THUNK_DATA` 構造体の配列でILTと同じ
* ロード後：`IMAGE_IMPORT_DESCRIPTOR->Name` のDLLからの各関数のアドレス 


# Visual Studio を立ち上げる
新しいプロジェクトの作成をしたあと、以下の 空のプロジェクト を選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/4d8e564a-c4de-fd52-93ca-a503daceed24.png)
次に、下記の所でx64用に変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/9ff28a94-d64b-aa12-e044-ba40492d458e.png)
次に、`ALT-Enter`を押してソリューションのプロパティ画面を表示し、`リンカー > システム` の所でサブシステムを `Windows ...` に変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/2b5f6cf0-21bc-53f4-a83f-f45cb152cbe9.png)
続いて同じプロパティ画面で、`詳細` を見て、文字セットを `マルチバイト文字セットを使用する` に変更する(これしないと文字化けした)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/b31446b6-5fe5-b4c1-1802-d8094f0e0620.png)
これで`OK`して閉じた後、C++コードを以下のようにして追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/f29aee1a-2415-2e58-d25d-c3964a37794c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/8a3d5202-da52-b1d1-a0b8-c297c153aa75.png)
これで準備が整った。


# 使ってるDLLの列挙
いきなりIATを列挙するのはキツイので、まずはインポートしてるDLLを列挙してみる。
IATの先ほどの内容を振り返ると、DLL事にインポートテーブルがあり、インポートテーブル( `IMAGE_IMPORT_DESCRIPTOR` )の `Name` メンバにそのDLLの名前がある。これを全て列挙する。

```c++:ListDll.cpp
#include <windows.h>
#include <dbghelp.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#pragma comment(lib, "dbghelp.lib")

int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow
) {
	ULONG cbSize;
	TCHAR szMyPath[MAX_PATH];
	HMODULE hModule;
	PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor = NULL;

	GetModuleFileName(NULL, szMyPath, _countof(szMyPath));
	hModule = GetModuleHandle((LPCSTR)szMyPath);

	pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &cbSize);

	if (pImageImportDescriptor == NULL)
		MessageBox(NULL, TEXT("error"), TEXT("null"), MB_OK);
	else {
		while (pImageImportDescriptor->Name != NULL) {
			LPCSTR pszDllName = (LPCSTR)((PBYTE)(hModule)+pImageImportDescriptor->Name);
			MessageBox(NULL, pszDllName, TEXT("DLL name"), MB_OK);
			pImageImportDescriptor++;
		}
	}

	CloseHandle(hModule);
	return 0;
}
```

ここで、最初の方に

```c++
GetModuleFileName(NULL, szMyPath, _countof(szMyPath));
hModule = GetModuleHandle((LPCSTR)szMyPath);
```

があるのは、実は先ほどFirstChunkにはIATへのアドレスがあるといったが、これは具体的には相対アドレスであり、最初の図の `OptionalHeader` の `ImageBase` からの相対アドレスになっている。なんで相対アドレスなのかというと、`ImageBase` はプログラムがメモリ上にロードされる際の先頭の位置であり、ここがいつも固定ではないため、ここからの相対アドレスとして全部指定するようになってる。
所々、`(LBYTE)hModule + ...` のような計算があるのはそういう理由。

実際の処理として大事で押さえておけばいい所は以下の２点だけ、

```c++
// IMAGE_IMPORT_DESCRIPTORのポインタの取得
 pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &cbSize);

// IMAGE_IMPORT_DESCRIPTORのNameメンバを取得
LPCSTR pszDllName = (LPCSTR)((PBYTE)(hModule)+pImageImportDescriptor->Name);
```

これをでは、Visual Studio上で、`Ctrl-b` でビルドし、ビルドすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/20a9a397-f8c0-5324-9c98-88ecaf5d6189.png)
こんな感じでどこに実行ファイルができたか書いてあるので、これを実行する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/64a7304e-d077-ab5c-ffd3-7a96fc7be13b.png)

こんな感じで何度も `MessageBox` でDLLの名前が列挙されるはず。



# IATの列挙（本題）
とりあえず、一番最初の `Kernel32.dll` からインポートされている関数のアドレスをIATから列挙したい。
ただ、アドレスだけ表示されても `Kernel32.dll` の何の関数かわからないので、関数名も列挙したい。
しかし、よく考えると、プログラムをロード後のIATにはアドレスはあっても関数の名前を表すメンバは存在しない。それなので関数名の方は、最初の図を思い出して、`FirstThunk` ではなく、`OriginalFirstThunk` が指しているILT(IMAGE_THUNK_DATAの集まり)の方の `Name` メンバから取ってくる。
実際のコードが以下。

```c++:List-IAT.cpp
#include <windows.h>
#include <dbghelp.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#pragma comment(lib, "dbghelp.lib")

int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow
) {
	ULONG cbSize;
	TCHAR szMyPath[MAX_PATH];
	HMODULE hModule;
	PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor = NULL;
	PIMAGE_THUNK_DATA pFirstThunk = NULL;
	PIMAGE_THUNK_DATA pOriginalFirstThunk = NULL;

	GetModuleFileName(NULL, szMyPath, _countof(szMyPath));
	hModule = GetModuleHandle((LPCSTR)szMyPath);
	pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &cbSize);
	pFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImageImportDescriptor->FirstThunk);
	pOriginalFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImageImportDescriptor->OriginalFirstThunk);
	
	LPCSTR pszDllName = (LPCSTR)((PBYTE)hModule + pImageImportDescriptor->Name);
	MessageBox(NULL, pszDllName, "will show imports func addr from this dll", MB_OK);

	while(pFirstThunk->u1.Function != NULL) {
		std::stringstream ss;
		ss << "0x" << std::hex << pFirstThunk->u1.Function << std::endl;

		PIMAGE_IMPORT_BY_NAME pImageImportByName = (PIMAGE_IMPORT_BY_NAME)((PBYTE)hModule + pOriginalFirstThunk->u1.AddressOfData);
		LPCSTR pszFuncName = (LPCSTR)&pImageImportByName->Name[0];

		MessageBox(NULL, ss.str().c_str(), pszFuncName, MB_OK);
		pFirstThunk++;
		pOriginalFirstThunk++;
	}

	CloseHandle(hModule);
	return 0;
}
```

注意するべきなのは、IATはロード後はアドレスが入っているので、`IMAGE_THUNK_DATA` 型を使うのは変なのでは？ と思うが、まぁプログラム上でこうしてるだけで、実際にIATの方から関数名を見つけようとしてもできなかったので、これであってる。

流れを把握する上で押さえておくべき行は以下。

```c++
// IMAGE_IMPORT_DESCRIPTORのFirstThunkとOriginalFirstThunkへのポインタを取得
pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &cbSize);
pFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImageImportDescriptor->FirstThunk);
pOriginalFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImageImportDescriptor->OriginalFirstThunk);

// IMAGE_IMPORT_DESCRIPTORのNameからDLL名を取得（今回は最初のKernel32.dllだけ）
LPCSTR pszDllName = (LPCSTR)((PBYTE)hModule + pImageImportDescriptor->Name);

// これがIATのアドレス（ロード後の各関数の配置されてるアドレス）
pFirstThunk->u1.Function

// ILTのIMAGE_THUNK_DATA構造体のAddressOfDataメンバにIMAGE_IMPORT_BY_NAMEという構造体があり、
// そのName属性が関数名になってる。（ILTに直接関数名があるわけではないので注意）
PIMAGE_IMPORT_BY_NAME pImageImportByName = (PIMAGE_IMPORT_BY_NAME)((PBYTE)hModule + pOriginalFirstThunk->u1.AddressOfData);
```

これで再びビルドして実行すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/93a275a1-ffd6-d572-30f0-0e5e425690d7.png)
こんな感じで関数とそのアドレスが列挙されるので、IATが列挙されていることを確認できる。