使用スクリプトを実行可能ファイルに保存する#
この章では、脱殻後の 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 editor を開きます。
pe editor 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
関数
上記の保存には 1 つの問題があります。プログラムが実行されると、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 加壳プログラムの脱殻を学び、全体の脱殻プロセスを理解しました。