Vectored Overloading PE Injection

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

Tip

Windows 11 の LFH heap shaping と VMware Workstation PVSCSI (vmware-vmx) の escape techniques を探していますか?

{{#ref}} vmware-workstation-pvscsi-lfh-escape.md {{#endref}}

手法の概要

Vectored Overloading は、古典的な Module Overloading を Vectored Exception Handlers (VEHs) と hardware breakpoints と融合させた Windows PE injection primitive です。独自のローダや LoadLibrary のパッチを行う代わりに、攻撃者は以下を行います。

  1. 正規の DLL(例: wmp.dll)にバックされた SEC_IMAGE セクションを作成する。
  2. マップされたビューを完全にリロケートされた悪意ある PE で上書きするが、セクションオブジェクトはディスク上の正規イメージを指したままにする。
  3. VEH を登録し、デバッグレジスタをプログラムして NtOpenSectionNtMapViewOfSection、(オプションで NtClose)へのすべての呼び出しがユーザーモードのブレークポイントを発生させるようにする。
  4. LoadLibrary("amsi.dll")(または他の任意の正規ターゲット)を呼ぶ。Windows ローダがこれらの syscall を呼ぶと、VEH はカーネル遷移をスキップし、準備した悪意あるイメージのハンドルとベースアドレスを返します。

ローダは要求された DLL をマップしたと信じているため、セクションのバックファイルだけを参照するツールは wmp.dll を見続けますが、実際のメモリは攻撃者のペイロードで上書きされています。一方で imports/TLS コールバックは本物のローダによって解決されるため、攻撃者が維持すべきカスタムな PE 解析ロジックの量が大幅に減ります。

Stage 1 – Build the disguised section

  1. Create and map a section for the decoy DLL
NtCreateSection(&DecoySection, SECTION_ALL_ACCESS, NULL,
0, PAGE_READWRITE, SEC_IMAGE, L"\??\C:\\Windows\\System32\\wmp.dll");
NtMapViewOfSection(DecoySection, GetCurrentProcess(), &DecoyView, 0, 0,
NULL, &DecoySize, ViewShare, 0, PAGE_READWRITE);
  1. Copy the malicious PE into that view section by section, honouring SizeOfRawData/VirtualSize and updating protections afterwards (PAGE_EXECUTE_READ, PAGE_READWRITE, etc.).
  2. Apply relocations and resolve imports exactly as a reflective loader would. Because the view is already mapped as SEC_IMAGE, section alignments and guard pages match what the Windows loader expects later.
  3. Normalize the PE header:
  • If the payload is an EXE, set IMAGE_FILE_HEADER.Characteristics |= IMAGE_FILE_DLL and zero the entry point to keep LdrpCallTlsInitializers from jumping into EXE-specific stubs.
  • DLL payloads can keep their headers unchanged.

この時点でプロセスは、バックオブジェクトがまだ wmp.dll のままでありながら、メモリ内のバイトを攻撃者が制御する RWX 対応のビューを所有しています。

Stage 2 – Hijack the loader with VEHs

  1. Register a VEH and arm hardware breakpoints: Dr0(または他のデバッグレジスタ)に ntdll!NtOpenSection のアドレスを設定し、DR7 をセットして実行ごとに STATUS_SINGLE_STEP を発生させる。後で NtMapViewOfSection(およびオプションで NtClose)についても同様に行う。
  2. Trigger DLL loading with LoadLibrary("amsi.dll")LdrLoadDll は最終的に NtOpenSection を呼んで実際のセクションハンドルを取得する。
  3. VEH hook for NtOpenSection:
  • [out] PHANDLE SectionHandle 引数のスタックスロットを特定する。
  • そのスロットに事前に作成した DecoySection ハンドルを書き込む。
  • カーネルが呼ばれないように ret 命令まで RIP/EIP を進める。
  • 次に NtMapViewOfSection を監視するようハードウェアブレークポイントを再設定する。
  1. VEH hook for NtMapViewOfSection:
  • [out] PVOID *BaseAddress(およびサイズ/保護出力)を、既にマップされた悪意あるビューのアドレスで上書きする。
  • 以前と同様に syscall 本体をスキップする。
  1. (Optional) VEH hook for NtClose は、偽のセクションハンドルがクリーンアップされていることを検証し、リソースのリークを防ぎ最終的な整合性チェックを提供する。

syscall が実行されないため、カーネルコールバック(ETWti、minifilter 等)は疑わしい NtOpenSection/NtMapViewOfSection イベントを観測せず、テレメトリが大幅に低下します。ローダの観点ではすべてが成功したように見え、amsi.dll はメモリ上に存在すると判断して、インポート/TLS を攻撃者のビューに対して続行します。

PoC 実装ノート (2025)

公開されている PoC は、再実装時に見落としやすい実用的な詳細をいくつか示しています:

  • HWBPs are per-thread. PoC は CONTEXT_DEBUG_REGISTERSLoadLibrary を呼ぶ前に現在のスレッドにセットしているため、VEH はローダをトリガーするのと同じスレッド上で実行されなければならない。
  • Syscall emulation: VEH は RAX = 0 を設定し、ntdll スタブ内の ret0xC3 をスキャン)まで RIP を進めることでカーネル遷移を発生させず、その後 NtContinue で再開する。
  • Output parameters: NtMapViewOfSection の場合、VEH は返される BaseAddressViewSize、および Win32Protect 出力を上書きしてローダがマッピングに成功したと信じ、攻撃者のビューを使って imports/TLS を続行するようにする。

PoC で使われる最小限の HWBP セットアップ (x64):

CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
ctx.Dr0 = (DWORD64)NtOpenSection;
ctx.Dr7 = 1;
SetThreadContext(GetCurrentThread(), &ctx);
AddVectoredExceptionHandler(1, VehHandler);

ステルス変種

最近の VEH に関する研究では、ハンドラが AddVectoredExceptionHandler を呼び出す代わりに manually manipulating the VEH list によって登録できることが示されており、監視やフックの対象になり得るユーザーモードAPIへの依存を減らせます。これは Vectored Overloading に必須ではありませんが、観測可能な API 活動を減らすために組み合わせることができます。

Stage 3 – Execute the payload

  • EXE payload: リロケーションが完了すると、インジェクタは単に元のエントリポイントへジャンプします。ローダが DllMain を呼ぶと思っている箇所で、代わりにカスタムコードが EXE スタイルのエントリを実行します。
  • DLL payload / Node.js addon: 意図したエクスポートを解決して呼び出します (Kidkadi は JavaScript に名前付き関数を公開します)。モジュールが既に LdrpModuleBaseAddressIndex に登録されているため、以降のルックアップではそれを無害な DLL として扱います。

Node.js ネイティブアドオン(.node ファイル)と組み合わせると、Windows の内部処理はすべて JavaScript レイヤーの外に残るため、脅威アクターは同じローダを多様な難読化された Node ラッパーと共に配布しやすくなります。

References

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする