概要
以下のプログラムをコンパイルして、 UPX 3.96 でパッキングし、それを手動でアンパックする。
#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で見てみた結果が以下。
おそらく pestudio のこのインポートの情報は、IAT(プログラムロード前はIATにはインポートする関数の名前等が書いてある)から得ていて、この情報が圧縮されているため sleep-upx.exe では Sleep
の文字が見当たらない(ILTから得ているのかもしれないが…)。
しかし、パッキングは実行してる時には自分自身を解凍していて、かつインポートしている関数のアドレスがIATにセットされているので(プログラムロード後はIATにはインポート関数の実際のアドレスだけが格納される)、このタイミングでメモリダンプすればどんなコードなのかとか、インポートしてる関数は何かなどがわかり、Sleep
関数のアドレスもわかる。
また、パッキングされている場合、例えば sleep-upx.exe の Sleep
をIATフックしたい場合、前回のフックのプログラムを見てもわかるが、Sleep
のアドレスがIATにあったとしても、どのアドレスが Sleep
のアドレスなのかというのは、ILT(OriginalFirstThunk)は圧縮された状態のインポート情報のままなので、わからないのでフックができない。実際にやってみる。
sleep.exe の Sleep をフックした場合(成功) ↓
sleep-upx.exe の Sleep をフックした場合(失敗) ↓
よって、実際に実行してみてIATがセットされた後、この情報をもとにIATを再構築して、全てのインポートの関数の情報を復元する必要がある。
OEPを見つける
パッキングされた実行ファイルのエントリーポイントには、オリジナルのコードを解凍する処理が並んでいるわけだが、オリジナルのコードの本来のエントリーポイントまでまずは実行する必要がある。ちなみにOEPにブレークポイントを打ってそこでブレークさせた状態でIAT再構築しないと上手く行かない。
注意として、UPXアンパックで調べると、pushad
と popad
に注目してOEPを見つける方法があるが、どうやら UPX 3.96 では pushad
とかは使われていないので、別の方法で見つけないといけない。
とりあえず今回はオリジナルの sleep.exe も手元にあるので、これのエントリーポイント付近のアセンブラを覚えておく。
なんか sub rsp,28
から始まり、二つの mov
、二つの call
が続くみたいに覚えておく。
そして、まずは sleep-upx.exe
を何もブレークポイントも打たないでただ実行する。そして、適当なタイミングで一時停止して、その付近で同じようなアセンブラ命令が続く場所を探す。
なんかどうやら sleep.exe
のエントリーポイントと同じ場所にあるっぽい。
それなので、0x4014eがOEPだとわかったので、ここにハードウェアブレークポイントをセットする(普通のソフトウェアブレークポイントだと別のアドレスにOEPが来てしまう… 検知されてるのか…?)。
この状態になったらそのまま、Plugin > Scylla
を開き、File > Dump Memory
して、Dump PE
を押し、適当な場所に sleep-upx-dump.exe とかの名前で保存する。
その後、Scyllaの画面に戻り、IAT Autosearch
を押した後、Get Imports
を押し、Fix Dump
を押して、先ほどの sleep-upx-dump.exe を選択する。
そうすると、同じディレクトリに sleep-upx-dump_SCY.exe という名前でファイルができているので、これで再構築完了!
ちゃんとできてるか確認
実行できてることを確認した後、ではちゃんとインポートの情報が復元できているか再びpestudioで確認する。
完璧にオリジナルの sleep.exe と同じようなインポート情報に復元されている。