スクリプトを使用して実行可能ファイルに保存する#
この章では、脱殻後の 2 つのステップ、実行可能ファイルへの保存とインポート関数テーブルの再構築について説明します。
OEP
前の章で OEP がデバッグされました。OEP に移動し、ida でプログラムを再分析して、保存操作を実行する準備をします。以下の idc スクリプトを使用して保存します。
static Byte(ea) {
return (ea) & 0xff;
}
static main()
{
auto fp, ea;
fp = fopen("dump.bin", "wb"); // 書き込むファイルを開く
for (ea=0x400000; ea < 0x40b200; ea++) // 指定されたアドレス範囲をループ
{
fputc(Byte(ea), fp); // 指定されたアドレスのデータをファイルに書き込む
}
fclose(fp); // ファイルを閉じる
}
注:実戦の過程で、ida7.7 で上記のコードを使用して保存した内容には問題があり、保存された内容は正常ではありません
そこで、ここでは python3 のスクリプトを使用して保存します。スクリプトは以下の通りです:
import idaapi
import idc
import struct
start_ea = 0x400000
end_ea = 0x40b200
step = 4 # 各アドレスのデータは4バイトを占める
file_path = "dump.bin"
with open(file_path, "wb") as f:
for ea in range(start_ea, end_ea, step):
# 指定されたアドレスの4バイトデータを読み取り、リトルエンディアンに変換
bin_data = struct.pack("<L", idaapi.get_32bit(ea))
f.write(bin_data)
ファイルの基準アドレスは 0x400000 で、ida のセグメントタブから保存された最高アドレスを探します。下の図から Overlay ブロックの終わり 0x40b200 が見えます。
その後、メニューバーから - ファイル - スクリプトファイルの実行を開きます。この機能は idc スクリプトと python スクリプトの両方で使用できます。idc スクリプトを実行すると、idc の現在のディレクトリに bin ファイルが生成されます。
bin ファイルのファイルヘッダーは以下の図のように正常です。
dump.bin ファイルをバックアップし、.exe の実行可能ファイルに名前を変更します。
このファイルのアイコンは表示されていないため、まだ解決すべき問題があります。
次に pe エディタを開きます。
pe エディタ 1.7
sections をクリックして、セクションビューを開きます。
セクションビュー
各セクションを右クリックし、dumpfixer を選択します。
この時、アイコンが正常に表示されましたが、プログラムはまだ実行できません。なぜなら IAT が修正されていないからです。
IAT とは何か#
IAT(インポートアドレステーブル)は、Windows PE フォーマットにおける重要なデータ構造で、プログラムの実行時に外部 DLL(動的リンクライブラリ)のシンボルを動的にロードおよびリンクするために使用されます。IAT は、外部 DLL 内のすべての呼び出す関数の名前とアドレスを保存し、プログラムの動的リンカーが実行時に使用します。
IAT はプログラムが実行するすべてのインポート関数のアドレスを保存します。
加壳ファイルの解読後の IAT
元のファイルの IAT
上の 2 つの図は 0x403238 アドレスを示しており、内容はほぼ同じに見えます。
元のファイル
の IDA の左下隅には、このメモリアドレスに対応するファイルオフセットが 0x1038 であることが表示されています。上の図のように、ここで 16 進エディタを開いてその内容を確認できます。
010editor で元のファイルの 0x1038 のバイトを開く
上の図で0x1038
の値は0x355e
です。
もし0x355e
に基準アドレス0x400000
を加えると0x40355e
になります。このアドレスの内容を確認するには、ida でファイル内のすべてのセクションを手動で再ロードする必要があります(元のファイルをロード
)。
0x40355e の内容
すべてのセクションをロードすることを選択し、0x40355e に移動すると、右側にも api の関数名GetModuleHandleA
が表示されます。
これらのオフセットの値に基準値を加えることで、対応する関数名の文字列を見つけることができます。この文字列を使用して、これらの関数のシステム上のネイティブアドレスを取得します。本例ではGetModuleHandleA
のアドレスを取得し、5E 35 00 00
バイトをネイティブアドレスに置き換えます。
システムは初期値 0x355e に基準アドレスを加えて対応する関数名の文字列を取得し、関数のネイティブアドレスを 0x403238 に保存します。
次に、別の ida ウィンドウを開いて保存したファイルdump - bak.exe
をロードし、0x403238
に移動します。
上の図では、ファイルのオフセットアドレスが 0x3238 で元のファイルと一致しないことがわかります。主な理由は、dumpfixer がファイルの占有(rawsize)をメモリの占有(rawsize)と同じに変更したため、オフセットが変わったからです。
010editor でdump - bak.exe
を開き、0x3238 に移動します。
ここは api 関数のネイティブアドレスであり、api 名の文字列を指すオフセットではありません。
保存時にプログラムはすでに api 関数のネイティブアドレスを取得し、それを IAT に保存しています。したがって、加壳プログラム内のGetModuleHandleA API
のネイティブアドレスはこのアドレスに保存されています。
GetModuleHandleA API
関数
上記の保存には問題があります。プログラムが実行されると、IAT からオフセットを読み取り、基準アドレスを加算して API 関数名の文字列を取得し、システムからこれらの API 関数のネイティブアドレスを取得して IAT に保存します。しかし、保存時には関数の実際のアドレスが保持されているため、プログラムはこのプロセスに従って IAT を正しく埋めることができず、最終的にプログラムがクラッシュします。
IAT の再構築#
IAT を再構築する考え方は、これらの api 関数名の文字列を指すオフセット値を復元することです。IAT を修正するには Scylla ツールが必要です。
scylla を実行し、アクティブなプロセスにアタッチする中で、ida で OEP の加壳プログラムのプロセスを一時停止します。
加壳プログラムが OEP に到達
OPE の値を 00401000 に入力し、IAT 自動検索をクリックします。
上の図では、開始アドレスが 0x403184 で、サイズが 0x108 であることが表示されています。
Get Imports をクリックします。
上の図では、22 箇所が未修正であることが表示されています。
右クリックして未認識の関数を選択し、scylla plugins-pecompact v2.x を選択して修正します。
未認識の関数が正常に修正されました
いくつかの疑わしい関数があり、show suspect を選択して 0x403278 の関数が正常に修正されたか確認します。
上の図では、正常に修正されたことが確認でき、問題がなければ scylla の右下の FIX DUMP をクリックします。
以前に保存した.exe ファイルを選択し、修正された IAT を書き込みます。
修正されたプログラムは正常に実行され、修正が成功したことを示しています。
IDA で脱殻し、IAT 修正を完了したファイルをロードすると、プログラムは OEP、つまり 0x401000 から実行され、修正されたものは元のファイルと同じです。
IDA で IAT 修正後の実行可能ファイルをロード
修正された IAT
この章では、セクションの内容を保存し、IAT 関数テーブルを再構築する方法を主に紹介し、最後の脱殻作業を完了しました。この upx 加壳プログラムの脱殻を学び、全体の脱殻プロセスを理解しました。