本記事は学習用です。作成には ChatGPT と GitHub Copilot を使用しています。
1. 導入:APIフックの定義と重要性
APIフック(API Hook) とは、あるモジュールが呼び出す API 関数(例:MessageBoxA や ReadFile)の呼び出し先を別の関数に差し替え、挙動を変更する技術である。代表的な手法に IAT Hook(Import Address Table)、EAT Hook(Export Address Table)、Inline Hook(トランポリン/Detours) がある。
- なぜ重要か:デバッグ・互換レイヤー・モニタリングなど正当な用途がある一方、マルウェアや不正ツールはこれを用いて情報窃取や検出回避を行う。防御側は仕組みを理解し、検出ルールや整合性チェックを実装する必要がある。
用語定義
- PE(Portable Executable):Windows の実行ファイル形式。
- IAT(Import Address Table):実行モジュールが依存する DLL 関数の実行時アドレスを保持するテーブル。
- EAT(Export Address Table):DLL が他モジュールへ公開する関数の名前・序数と、その関数実装の RVA を管理するテーブル。
- トランポリン(trampoline):Inline Hook で、上書きした先頭命令を実行可能に退避させる実行領域。
2. 背景と仕組み(なぜ動くのか)
2.1 Windows ローダーとテーブルの関係
- 実行時、PE ローダーが Import Directory を参照し、IAT に各依存関数の実行アドレスを書き込む。呼び出し側は
CALL [IAT_entry]のように IAT 経由で関数を呼ぶことが多い。これが IAT 書き換えでフックできる理由である。 - EAT は DLL 自体が何をエクスポートしているかを管理するテーブル。プロセス内メモリ上の EAT を変更すると、同一プロセス内でその DLL を参照する呼び出しに影響する。オンディスクのバイナリを改変すれば全クライアントに波及するが、強く非推奨であり検出リスクが非常に高い。
- Inline Hook は関数の先頭命令を直接書き換え、以降の呼び出しを別関数へ転送する。x86/x64 で命令長可変や RIP 相対アドレッシングがあるため注意が必要。
2.2 攻撃・防御の視点
- 攻撃者は「プロセス内での挙動変更」「検出回避」「機密データ窃取」などを目的にフックを利用。
- 防御者は「呼び出し元/先の整合性」「IAT/EAT の不正変更」「メモリ上の不審な実行可能領域」を検出指標とする。
3. IAT Hook:手順・実装例・注意点
3.1 手順
- 対象モジュール(フックを入れたい EXE/DLL)のベースアドレスを取得(
GetModuleHandleなど)。 - Import Directory(
IMAGE_IMPORT_DESCRIPTOR)を探索して、ターゲット DLL(例:user32.dll)のエントリを見つける。 - IAT(通常は
FirstThunkが示すポインタ配列)を走査し、対象関数(例:MessageBoxA)のエントリを特定する。 VirtualProtectでページ保護を一時的に書き込み可能(例:PAGE_READWRITE)へ変更し、該当エントリをフック関数のアドレス(VA)で上書きする。最後に保護を戻し、FlushInstructionCacheを呼ぶ。
3.2 実装例(学習用・エラーチェック省略)
// IAT_hook_example.c(概念実装)
#include <windows.h>
#include <stdio.h>
typedef int (WINAPI *MSGBOXA)(HWND, LPCSTR, LPCSTR, UINT);
MSGBOXA g_origMessageBoxA = NULL;
int WINAPI hook_MessageBoxA(HWND h, LPCSTR t, LPCSTR c, UINT f){
// lpText(2番目)はそのまま、lpCaption(3番目)を差し替える
return g_origMessageBoxA(h, t, "HOOKED", f);
}
void HookIATForModule(HMODULE hModule, const char* targetDll, const char* funcName, void* newFunc){
// 1. DOS/NT ヘッダーから Import Directory を取得(例:ImageDirectoryEntryToData)
// 2. IMAGE_IMPORT_DESCRIPTOR を走査して targetDll を見つける
// 3. FirstThunk (IAT) を走査して、該当 IAT エントリを探す
// 4. VirtualProtect -> 書き換え -> FlushInstructionCache -> 保護復帰
}
3.3 注意点
- IAT はモジュールごと(各 EXE/DLL)に存在するため、全プロセスに影響を及ぼすには複数モジュール/複数プロセスを個別に処理する必要がある。
- ASLR やコード整合性/署名検証の厳格なプロセスでは、書き換えがクラッシュや検出につながることがある。
4. EAT Hook:手順・実装例・運用上の留意点
4.1 手順
- 対象 DLL のベースアドレスを取得。
IMAGE_EXPORT_DIRECTORYを取得し、AddressOfNames/AddressOfFunctions/AddressOfNameOrdinalsを解析して、目的のエクスポート関数のインデックスを特定する。AddressOfFunctions[index]に格納されているのは関数実装の RVA(モジュール相対アドレス)である。書き換える場合は、モジュール内の実行可能領域に配置したスタブ(RVA)へ更新する。任意の他モジュールや任意 VA を直接指定することはできないため(エクスポートフォワーダーを除く)、転送用スタブを用意してジャンプさせる。
4.2 実践的注意
- メモリ上の EAT 書き換えは当該プロセス内の呼び出しにのみ影響する。オンディスク改変は全クライアントへ波及するが、極めて高リスクで非推奨。
- 多くの DLL は共有可能ページとしてロードされるが、書き込み時にはコピーオンライトが発生し、効果はプロセス単位となる。
- 署名検証や整合性チェックの対象となりやすく、EAT 書き換えは検出されやすい。
5. Inline Hook(トランポリン/Detours):詳細実装と落とし穴
5.1 実装の全体像
- ターゲット関数先頭の命令列を逆アセンブルして、安全に切り出せるバイト数 N を計算する(命令境界を尊重)。
VirtualAllocで実行可能なメモリを確保し、先頭 N バイトをコピーしてトランポリンを作成する。- トランポリンの末尾に、元の関数
target_addr + Nへ戻るジャンプ命令を付与する。 - 元の関数先頭をジャンプ命令で上書きして、フック先へ分岐させる(
VirtualProtectで保護変更し、FlushInstructionCacheを実行)。 - フック先は必要に応じてトランポリンを呼び出して元の動作を維持できる。
5.2 x86 と x64 の主な差異
- x86:相対ジャンプ等で扱いやすいが、命令長可変は依然注意点。5 バイト以上の書き換えが一般的。
- x64:RIP 相対アドレッシングが多く、単純コピーで参照が壊れる可能性が高い。さらに近距離(rel32)の範囲(±2GB)に収まらない場合があり、その場合は同一範囲内に中継領域を確保する、あるいはロングジャンプ実現のための中継コードを用いる。
5.3 実装上よくある失敗と回避法
- 命令を不完全に切り出して命令境界を壊しクラッシュする → 解決:ディスアセンブルして完全命令単位で切り出す。
- RIP 相対参照のリロケーションを忘れる → 解決:参照先の再計算(relocation)や、既存ライブラリ(MinHook、Detours)を使う。
- スレッド競合で書き換え中に呼び出される → 解決:安全なタイミングでパッチ適用(必要なら一時停止やガード)し、適用後は
FlushInstructionCacheを必ず実行する。
5.4 既存ライブラリの活用
- MinHook:軽量で x86/x64 対応。トランポリン生成・スレッド同期などを自動化。
- Microsoft Detours:商用レベルの堅牢な実装。複雑なケースにも対応。
独力実装は学習のため有益だが、実運用や互換性重視の実験ではこれらライブラリ活用を推奨する。
6. 検出・防御・運用手順
6.1 検出指標
- メモリ保護の変更:
VirtualProtectによる実行可能ページの保護変更(例:PAGE_EXECUTE_READWRITEへの切り替え)。 - 外部プロセスからのアクセス:
OpenProcess(高権限)→WriteProcessMemory→CreateRemoteThreadの組み合わせ。 - IAT/EAT の不整合:期待されるエクスポートアドレスと IAT エントリが一致しない、または EAT の RVA が想定外。
- 実行可能だがディスク上に対応するセクションを持たない領域(メモリ上のトランポリン等)が存在する。
6.2 Sysmon/EDR での運用例
- Sysmon の Event ID 10(ProcessAccess)を監視し、不審なプロセスハンドル取得をアラート化。
- Sysmon の Event ID 8(CreateRemoteThread)や、
WriteProcessMemory相当の挙動に対応する EDR ルールを作成し、プロセスシーケンスを相関分析する。 - 定期的なメモリ整合性チェック:プロセス起動後に IAT/EAT のハッシュを検査し、不変だと期待される値との差分を調査する。
6.3 インシデント対応の手順
- 影響範囲の特定:どのプロセス・どのモジュールにフックが入っているかを特定(プロセスダンプ/メモリダンプ)。
- メモリダンプを取りオフライン解析:トランポリンやフック先のアドレス、PE ヘッダー情報を解析。
- ネットワーク活動確認:注入モジュールが外部通信(C2)を行っていないか確認。
- 復旧・再発防止:該当ホスト隔離、バイナリの再展開、脆弱性修正、EDR ルール強化。
7. 実験
7.1 準備
- 作業環境:ホストにハイパーバイザー上の仮想マシン(スナップショットを事前作成)。
- ツール:Visual Studio、x64dbg、Process Explorer/Process Hacker、PE 解析ツール(PE-bear 等)、MinHook(選択肢)。
7.2 低リスクの検証シナリオ(IAT フック)
- サンプル EXE を作る:
main.exeがMessageBoxAを呼ぶ簡単なプログラム。 - フック DLL を作る:IAT を書き換えてメッセージのタイトル(キャプション)を変える DLL。
- インジェクタまたはプロセス内ロードで DLL を適用。動作と IAT の差分を x64dbg や Process Hacker で確認。
7.3 Inline Hook の検証
- MinHook を使って
CreateFileA等をフックする例を実行。 - x64 環境では RIP 相対参照やジャンプ範囲の扱いを確認。
- フック適用後にプロセスを操作して安定性を確認(多スレッド呼び出しがある関数は重点確認)。
8. よくある誤り・現場での Tips
- 命令境界の無視でクラッシュする → 必ずディスアセンブルして命令単位で処理。
- x64 の RIP 相対参照未対応 → トランポリン作成時にアドレス再計算を行うかライブラリを使う。
- スレッド安全性を無視 → 書き換えは可能なら安全なタイミングで行い、適用後は
FlushInstructionCacheを実行。 - テストを本番で行う → 絶対に NG。必ず隔離ラボで実行する。
9. まとめ
- IAT/EAT/Inline Hook はそれぞれ目的と適用範囲が異なる。IAT はモジュール単位、EAT はエクスポート側(プロセス内)に波及、Inline は関数レベルで強力だが実装が難しい。
- 実装は細かい PE の理解(RVA/VA、Import/Export Directory)と OS API の扱いを要する。
- 防御は多層的に行う(最小権限・署名・EDR/ログ解析・整合性チェック)。
- 学習はまず既存ライブラリ(MinHook/Detours)で動作を理解し、その後低レイヤーを学ぶルートを推奨する。
10. 付録:図表・コード断片
フロー図
flowchart LR
subgraph IAT
A[Caller] -->|CALL IAT| B[Function in DLL]
A -->|IAT modified| C[HookFunc]
end
subgraph Inline
D[Caller] -->|CALL target| E[target function jmp overwritten]
E -->|jmp| C
end
重要 API 早見表
| API | 用途 |
|---|---|
| GetModuleHandle / LoadLibrary | モジュールベースアドレス取得 |
| VirtualProtect | メモリ保護の変更(書き込み許可) |
| VirtualAlloc / VirtualFree | トランポリン用メモリ確保/解放 |
| WriteProcessMemory / ReadProcessMemory | リモートプロセス書き込み/読み取り(インジェクション時) |
A. 参考サイト
- Microsoft Docs — Windows フック(SetWindowsHookEx 等)
- Microsoft Docs — PE Format(Portable Executable の概説・ヘッダー)
- MinHook (GitHub) — 軽量フックライブラリ
- Microsoft Detours — ライブラリ(公式)
- MITRE ATT&CK — T1055 (Process Injection)

コメント