従来のインメモリコードパッチングに代わる手法として、ハードウェアブレークポイント(CPU デバッグレジスタ)ベースの関数フッキングを実証するセキュリティ研究用の概念実証(POC)。
目的と範囲
このリポジトリは、Windows 内部構造に関する防御的セキュリティ研究、レッドチーム/パープルチーム教育、検知エンジニアリング、および学術研究のみを目的として公開されています。これは、攻撃者がプロセッサのデバッグレジスタを悪用してユーザーモードのセキュリティテレメトリを無効化する方法を実証すると同時に、同じくらい重要なこととして、そのような手法を検知するために防御側が何を監視すべきかを示すものです。作者は本コードのいかなる悪用についても責任を負いません。明示的な許可なくシステムに対してこの手法を使用することは違法であり、ほとんどの法域における関連するコンピュータ不正アクセス・濫用防止法に違反します。自身が所有していない、またはテストに対する明示的な書面による許可を持たない環境には、これを配備しないでください。
mora_hwbp.c は、ターゲットプロセス(例:PowerShell ホスト)にロード/インジェクションされると、プロセス内のすべてのスレッドのアーキテクチャデバッグレジスタ(DR0–DR7)に格納された CPU ハードウェアブレークポイントのみを介して、4 つのユーザーモード関数をフックする DLL を実装します。
| レジスタ | フック対象関数 | モジュール | 目的 |
|---|---|---|---|
DR0 | AmsiScanBuffer | amsi.dll | AMSI コンテンツスキャンを無効化 |
DR1 | WldpQueryDynamicCodeTrust | wldp.dll | WLDP の動的コード信頼を強制(Device Guard / WDAC) |
DR2 | WldpIsClassInApprovedList | wldp.dll | WLDP のクラス承認を強制(Device Guard / WDAC) |
DR3 | NtTraceEvent | ntdll.dll | ユーザーモード ETW ストリーム全体を遮断 |
プロセスごとのベクタード例外ハンドラ(VEH)が、デバッグレジスタによって発生する EXCEPTION_SINGLE_STEP(0x80000004)フォールトを受信し、例外コンテキストを書き換えることで元の関数の成功リターンパスをシミュレートし、実行を再開します — そのすべてが実行可能メモリを 1 バイトも変更することなく行われます。
これにより、この手法は攻撃と防御の両方の観点から特に興味深いものとなっています:
.text セクション(古典的なインラインフッキング、EAT/IAT パッチング、または Etwp* スタビング)を探す EDR/HIPS の整合性チェックを回避します。GetThreadContext/SetThreadContext のシステムコールパターン)を残し、検知に利用できます。従来のユーザーモードフッキング手法 — インラインディテア(5〜14 バイトの上書き)、インポートアドレステーブル(IAT)フッキング、エクスポートアドレステーブル(EAT)フッキング — は共通の弱点を抱えています:整合性スキャナや ETW が観測できるメモリを変更するという点です。
現代の AV/EDR 製品は以下を実装しています:
pageguard/guard-page トリック、PAGE_EXECUTE_READWRITE への VirtualProtect 遷移、セクションハッシュの不一致を検出するカーネルコールバックとユーザーモード整合性チェック。ハードウェアブレークポイントはこれらすべてを回避します:
.text 内にスキャンできるものは何もありません。SetThreadContext を介してスレッド単位で設定され、整合性スキャナーが使用する古典的な「メモリ変更」シグナルをトリガーしません。この POC は、現代の Windows セキュリティスタックで最も広く依存されているユーザーモードのセキュリティプリミティブである AMSI(Antimalware Scan Interface)、WLDP(Windows Lockdown Policy)、ETW(Event Tracing for Windows) に対するこの手法の有効性と検出可能性を探ります。
AMSI は、アプリケーション(PowerShell、Office、VBScript、.NET ホストなど)が登録されたマルウェア対策プロバイダーにコンテンツのスキャンを要求できるようにする Windows プラットフォームの統合ポイントです。注目すべき主要なエントリポイントは次のとおりです:
AmsiScanBuffer(HANDLE hamsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HANDLE hamsiSession, AMSI_RESULT *pResult)返される AMSI_RESULT を AMSI_RESULT_CLEAN (0) に強制することで、スクリプトエンジンはコンテンツが検査され安全であると判断し、実行が中断されることなく継続されます。
WLDP は Windows Defender Application Control(WDAC / Device Guard)のポリシー評価を実装しています。この POC は 2 つの WLDP エントリポイントをフックします:
WldpIsClassInApprovedList は、指定された COM クラス(GUID で識別)が現在のポリシー下で許可されているかどうかを回答します。AMSI は内部的に WLDP に問い合わせて、特定のスクリプト/コンテンツクラスが「信頼済み」(承認リスト内)かどうかを判断します。関数がクラスを承認済みと報告した場合、AMSI はそのコンテンツタイプに対する追加の精査をスキップする可能性があります。WldpQueryDynamicCodeTrust は、指定された動的生成コード(.NET JIT 出力やその他の方法で生成された実行可能メモリなど)が現在のロックダウンポリシー下で信頼されているかどうかを回答します。この DLL は WldpIsClassInApprovedList の出力パラメータ isApproved(RDX)を TRUE に設定して S_OK を返し、WldpQueryDynamicCodeTrust を強制的に S_OK (0) を返すようにします — これにより、評価対象のコンテンツクラスと照会された動的コードの両方が信頼済みであるように見せかけます。
ntdll.dll 内の NtTraceEvent は、ETW サブシステムへの最下層のユーザーモードエントリポイントです — 事実上すべての ETW イベント発行(EtwEventWrite や高レベルの Etw* API を含む)がこれを経由します。したがってこれをフックすると、ユーザーモードからの ETW ストリーム全体が遮断され、セキュリティ監視に関連する広範な副作用が生じます:
Microsoft-Windows-DotNETRuntime)この DLL は実際の関数を実行せずに単に STATUS_SUCCESS (0) を返します。
┌──────────────────────────────────────────────────────────────────────────┐
│ Target Process (e.g. powershell.exe) │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────────────────┐│
│ │ mora_hwbp.dll │ │ CPU / Windows ││
│ │ │ │ ││
│ │ DllMain / InstallHook │ │ Thread A Thread B ││
│ │ │ │ │ ┌────────┐ ┌────────┐ ││
│ │ ▼ │ │ │ DR0..3 │ │ DR0..3 │ ││
│ │ Resolve exports │ │ └────────┘ └────────┘ ││
│ │ (amsi/wldp/ntdll) │ │ ││
│ │ │ │ │ #DB (single-step) ││
│ │ ▼ │ │ exception ──► Windows Dispatch ││
│ │ AddVectoredException │ │ │ ││
│ │ Handler(VEH) │ │ ▼ ││
│ │ │ │ │ ┌─────────────────────────────┐ ││
│ │ ▼ │ │ │ VectoredHandler (ours) │ ││
│ │ SetHwbpOnThread(ALL) │ │ │ • match #DB address │ ││
│ │ │ │ │ │ • rewrite context (RIP/RSP)│ ││
│ │ ▼ │ │ │ • spoof return value (RAX) │ ││
│ │ MonitorThread ◄──┐ │ │ │ • continue execution │ ││
│ │ (re-hook every │ │ │ └─────────────────────────────┘ ││
│ │ 500ms) └──────┘ │ ││
│ └──────────────────────────┘ └──────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────┘
高レベルのフロー:
DLL_PROCESS_ATTACH 時(またはエクスポートされた InstallHook 経由)に、対象のエクスポートが GetProcAddress で解決されます(オプションで LoadLibraryW によるモジュールの強制ロードも可能)。AddVectoredExceptionHandler(1, ...))。CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) を介して列挙され、フックされます。#DB シングルステップ例外を発生させます。Windows はこれを VEH にディスパッチし、VEH は無害なリターンをシミュレートして実行を継続します。インジェクション前 — AMSI がテストベクターをブロックします:
PS> "AmsiTestSample:7e72c3ce-861b-4339-8740-0ac1484c1386"
This script contains malicious content and has been blocked by your antivirus software.