スクリプトを使用して実行可能ファイルに保存する#
この章では、脱殻後の 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 加壳プログラムの脱殻を学び、全体の脱殻プロセスを理解しました。