x64dbgによるIAT再構築（UPXのアンパック）

# 概要
以下のプログラムをコンパイルして、 UPX 3.96 でパッキングし、それを手動でアンパックする。

```c++:sleep.cpp
#include <windows.h>
#include <stdio.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	for(int i=0; 1; i++) {
		if(i%5==0) printf("%d\n", i);
		Sleep(1000);
	}
	return TRUE;
}

```

# IAT再構築をやる意味

パッキングすると、表層解析においてどんな Windows API がインポートされているのかの情報がかなり削減される。上記のプログラムをコンパイルした sleep.exe と、それを `upx sleep.exe -o sleep-upx.exe` でパックしたプログラム sleep-upx.exe のインポートをpestudioで見てみた結果が以下。

sleep.exe ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/e97d0a83-b5c6-2c84-069a-869115d560ad.png)

sleep-upx.exe ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/bf530b53-0086-95ac-2460-6e391c910e27.png)

おそらく pestudio のこのインポートの情報は、IAT(プログラムロード前はIATにはインポートする関数の名前等が書いてある)から得ていて、この情報が圧縮されているため sleep-upx.exe では `Sleep` の文字が見当たらない(ILTから得ているのかもしれないが…)。

しかし、パッキングは実行してる時には自分自身を解凍していて、かつインポートしている関数のアドレスがIATにセットされているので(プログラムロード後はIATにはインポート関数の実際のアドレスだけが格納される)、このタイミングでメモリダンプすればどんなコードなのかとか、インポートしてる関数は何かなどがわかり、`Sleep` 関数のアドレスもわかる。

また、パッキングされている場合、例えば sleep-upx.exe の `Sleep` をIATフックしたい場合、[前回のフックのプログラム](https://qiita.com/KazuMatsuHack9981/items/3685e55f12969b56bede)を見てもわかるが、`Sleep` のアドレスがIATにあったとしても、どのアドレスが `Sleep` のアドレスなのかというのは、ILT(OriginalFirstThunk)は圧縮された状態のインポート情報のままなので、わからないのでフックができない。実際にやってみる。

sleep.exe の Sleep をフックした場合(成功) ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/74eb43d4-a073-da3c-4196-9edc53c6c117.png)

sleep-upx.exe の Sleep をフックした場合(失敗) ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/dd1ebf37-ee8f-ee73-d6cc-0a5c5e3ba5e5.png)

よって、実際に実行してみてIATがセットされた後、この情報をもとにIATを再構築して、全てのインポートの関数の情報を復元する必要がある。


# OEPを見つける
パッキングされた実行ファイルのエントリーポイントには、オリジナルのコードを解凍する処理が並んでいるわけだが、オリジナルのコードの本来のエントリーポイントまでまずは実行する必要がある。ちなみにOEPにブレークポイントを打ってそこでブレークさせた状態でIAT再構築しないと上手く行かない。

注意として、UPXアンパックで調べると、`pushad` と `popad` に注目してOEPを見つける方法があるが、どうやら UPX 3.96 では `pushad` とかは使われていないので、別の方法で見つけないといけない。

とりあえず今回はオリジナルの sleep.exe も手元にあるので、これのエントリーポイント付近のアセンブラを覚えておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/8aabb2ed-03b3-62fc-e1ba-54a91cbec653.png)
なんか `sub rsp,28` から始まり、二つの `mov` 、二つの `call` が続くみたいに覚えておく。

そして、まずは `sleep-upx.exe` を何もブレークポイントも打たないでただ実行する。そして、適当なタイミングで一時停止して、その付近で同じようなアセンブラ命令が続く場所を探す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/3f73815c-cb0f-1cba-82a1-93079f53c264.png)
なんかどうやら `sleep.exe` のエントリーポイントと同じ場所にあるっぽい。

それなので、0x4014eがOEPだとわかったので、ここにハードウェアブレークポイントをセットする(普通のソフトウェアブレークポイントだと別のアドレスにOEPが来てしまう… 検知されてるのか…?)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/9cf234be-4a3c-1a3c-9b6a-394e8598a6e6.png)
この状態になったらそのまま、`Plugin > Scylla` を開き、`File > Dump Memory` して、`Dump PE` を押し、適当な場所に sleep-upx-dump.exe とかの名前で保存する。

その後、Scyllaの画面に戻り、`IAT Autosearch` を押した後、`Get Imports` を押し、`Fix Dump` を押して、先ほどの sleep-upx-dump.exe を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/e39478e0-fc5a-45ce-29f2-0ce0ff8bbb68.png)
そうすると、同じディレクトリに sleep-upx-dump_SCY.exe という名前でファイルができているので、これで再構築完了！

# ちゃんとできてるか確認

動作確認として、ちゃんと実行できるかまず確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/18b56919-b4f1-0494-640c-feb38f8798b5.png)

実行できてることを確認した後、ではちゃんとインポートの情報が復元できているか再びpestudioで確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/2d0029d1-8a4d-7cec-2cb4-e77f7d4acf68.png)
完璧にオリジナルの sleep.exe と同じようなインポート情報に復元されている。

それでは最後に API Hook ができるようになってるか確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/0478dd58-b9b1-7df6-436c-720490316fbd.png)
フックできるようになった！