Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/zypherion-technologies/hallwatch
防御ツールマルウェア分析バイナリ解析脅威インテリジェンスインシデントレスポンス
GitHubzypherion-technologies/hallwatch

HallWatch

間接システムコールを捕捉するユーザーモード検出器。Hell's Hall、Tartarus' Gate、RecycledGate、VEH syscalls、その他多数をトラップします。

リポジトリを見る
87102ヶ月前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

HallWatch

License: GPL v3 Website Discord Telegram X

Copyright (C) 2026 Adam Zypherion <[email protected]> — GPL-3.0 のもとでライセンスされています。


間接システムコールは、一度見れば簡単です。ローダーは ntdll をウォークし、Nt* プロローグから SSN を読み取り、さらに 2 バイト先の 0F 05 を見つけ、自身で r10/rax/rdx/r8/r9 を設定し、その 2 バイトに jmp します。Syscall は ntdll 内部から発生します。kernel32 のエクスポートに対するフックは触れられません。ntdll のエクスポートに対するフックも触れられません。SYSCALL の瞬間の [RSP] はローダーの RWX ページを指していますが、外部から見ると呼び出しに異常は見えません。

root@kitploit:~
HellHall proc
    mov  r10, rcx
    mov  eax, dwSSN
    jmp  qword ptr [qAddr]
    ret
HellHall endp

これらの変種には名前があります。Tartarus' Gate と RecycledGate は、あるスタブに属する syscall 命令を使用しますが、別のスタブの SSN を使用するため、フックが報告するスタブ名をログに記録しても、それは嘘になります。VEH システムコールは意図的にアクセス違反をトリガーし、自身の VEH を使用してコンテキストを書き換え、RIP がレジスタを準備済みの状態で ntdll の syscall 命令に着地するようにします。Hell's Gate は ntdll をまったく使用しません。ローダーは自身の RWX ページに 0F 05 を書き込み、そこから実行します。

カーネルモード (KM) の答えはドライバーです。時間が経てば実際に取り組み、プロジェクトをリリースするかもしれませんが、すぐには行われません :D


PAGE_GUARD は機能しそうに見えます。Syscall バイトを保持するページを PAGE_GUARD でマークし、VEH で STATUS_GUARD_PAGE_VIOLATION をキャッチし、検査し、プライベートトランポリンにリダイレクトし、トラップフラグを設定し、シングルステップで抜け、ページを再アームします。ローダーがどのようにそこに到達したかに関係なく、Nt 呼び出しごとに 1 つのトラップが発生します。これは Hell's Gate 以外のすべてに対して機能します。

問題は OS が協力しないことです。PAGE_GUARD はワンショットです。つまり、発火するたびにビットがクリアされ、再度設定する必要があります。ハンドラーは syscall(ガードを戻すための NtProtect、再開するための NtContinue)を実行し、これらの syscall にはスタブがあり、そのスタブはガードしたばかりのページ上にあります。私は、所有する別のページ上にプライベートな syscall スタブを構築することで(RWX を割り当て、mov r10,rcx; mov eax,SSN; syscall; ret を書き込み、RX にロックし、ntdll のものには触れない)、その大部分を回避しましたが、それは脆弱でした。Windows のマイナーバージョンが変わるたびにタイミングが変わりました。サンプルが生成するスレッドごとに、ガードを再構築する整合性ワーカーとの競合が発生しました。デモの成功率は、同じマシンでの 10 回の実行で約 30 ~ 50 パーセントでした。

結局、PAGE_GUARD を思い通りに動作させるように Windows を説得するのをやめ、代わりにバイトを上書きすることにしました。


理解できない場合は、このビデオをご覧ください :)

https://github.com/user-attachments/assets/0cb670fd-e51b-413e-bf00-08f9297888ed


Syscall 命令のバイトは 0F 05 です。最初のバイト (0F) だけは、SYSCALL、CPUID、RDTSC を含む 2 バイトオペコードのファミリーのプレフィックスです。単独では実行可能ではありません。CPU は 2 番目のバイトを必要とします。最初のバイトを 0xCC (INT3) に置き換えると、ペアは CC 05 になり、CPU はこれを INT3 と、到達しないストレイバイトとしてデコードします。そのアドレスに着地するコードパスはすべて EXCEPTION_BREAKPOINT を発生させます。

これが全体のメカニズムです。VEH はブレークポイントをキャッチし、アドレスがどのスタブに属するかを調べ(初期化時に ntdll のエクスポートを列挙してマップを構築します)、現れたスタブに対して 3 つのチェックを実行し、Context->Rip を実際の syscall を実行して戻るプライベートトランポリンに設定します。バイトは CC のままです。次の呼び出し元も同じようにヒットするため、ページ保護の切り替えは発生しません。

明確にしておきます。はい、これはバイト上書きによるフックです。ただし、人々が通常「ntdll フック」で意味するバイトではありません。従来の EDR フックは、スタブの 最初の バイトを JMP <my_func> で上書きします。

root@kitploit:~
ntdll!NtAllocateVirtualMemory:
  E9 ?? ?? ?? ??       jmp my_hook     ; "mov r10, rcx" を上書き
  ...
  0F 05                syscall
  C3                   ret

これはまさに間接システムコールが回避するものです。ローダーは SSN を自身で読み取り、0F 05 に直接ジャンプするため、プロローグ(およびあなたの jmp)は決して実行されず、フックは発火しません。私たちが行うのは、syscall バイト自体 を上書きすることです。

root@kitploit:~
ntdll!NtAllocateVirtualMemory:
  4C 8B D1             mov r10, rcx     ; 未変更
  B8 18 00 00 00       mov eax, 18h     ; 未変更
  CC 05                int3 / 05        ; 以前は 0F 05、0F を CC に書き換え
  C3                   ret

これで、どのようにそのアドレスに到達したかは関係なくなります。プロローグを通じて、プロローグをスキップする間接ジャンプを通じて、RIP をそこに落とす VEH システムコールコンテキスト書き換えを通じて、関係ありません。CPU がそのバイトを実行すると、トラップが発生します。従来のフックと同じテクニックファミリーですが、場所が異なり、カバレッジが完全に異なります。

トランポリンは次のようになります。

root@kitploit:~
F3 0F 1E FA              endbr64
49 89 CA                 mov r10, rcx
B8 <stub SSN>            mov eax, ssn
0F 05                    syscall
C3                       ret

3 つのチェックは PAGE_GUARD バージョンから変更されていません。なぜなら、それらは正しい 3 つのチェックであり、信頼できる場所に存在する必要があったからです。

リターンアドレス。[RSP] は syscall が戻るはずだった場所です。正規の呼び出しでは、それは ntdll、kernel32、kernelbase、または関連するランタイム DLL の内部です。Hell's Hall の場合、それはローダーが実行している RWX ページの内部です。初期化時に GetModuleHandle を呼び出してこれらの名前を取得し、PE ヘッダーから .text 範囲を読み取ることで構築された、信頼できるリターンターゲットの短いリストがあります。

SSN。スタブのプロローグは syscall バイトに到達する前に実行されているため、eax にはロードされた値が保持されています。ローダーが Tartarus スワップを行った場合、その値は、列挙時にこの同じスタブから読み取った SSN と一致しません。不一致をログに記録し、トランポリンは自身の syscall の前に正しい SSN を書き込むため、実行されるカーネル関数はローダーが意図したものではなく、バイトに属するものになります。このテクニックは同じステップでログ記録され、無効化されます。

スタックウォーク。現在のコンテキストから RtlVirtualUnwind を使用して、5 フレーム上に移動します。各フレームの RIP は既知のモジュール内にあり、RUNTIME_FUNCTION エントリを持つ必要があります。シェルコードや ROP ガジェットは、[RSP] 自体が信頼できるように見えても(ローダーは呼び出し元がメモリ内のどこにいるかを大まかに予測し、もっともらしいリターンアドレスを偽造できるため)、これに失敗します。

いずれかのチェックが失敗した場合、ロックフリーリングに小さな構造体をプッシュし、ドレインスレッドが次に起動したときにそれを出力します。

root@kitploit:~
[!! hallwatch !!] indirect syscall (untrusted caller, wrong ssn for this stub)
    syscall      : NtAllocateVirtualMemory
    syscall rip  : 0x00007FF827660372
    return addr  : 0x00007FF7EED719D6
    rax (ssn)    : 0x0000000F (stub encodes 0x00000018)
    thread       : 26388

Hell's Gate は INT3 が役に立たなくなるケースです。ローダーの RWX ページが存在することを知らなかったため、パッチを当てることはありませんでした。

代わりに、動作する最も単純なスキャナーを使用します。250 ミリ秒ごとに (基本的に頻度が高すぎ、CPU サイクルを浪費しますが、PoC のためです)、整合性ワーカーは VirtualQuery でアドレス空間をウォークし、実行可能なページ保護を持つ MEM_COMMIT 領域を探します。領域がロード済みモジュール内にある場合はスキップされます。自身のトランポリンプール内にある場合もスキップされます。残りは外部の実行可能メモリです。最大 64 KB をスキャンして 0F 05 バイトペアを探し、アドレスで重複排除し、一意のヒットごとに 1 回ログに記録します。これは愚かで CPU サイクルを浪費しますが、PoC であり、状況を軽視すべきではありませんが、現時点ではそうなっています。

これにより、新たな RWX 割り当て内の Hell's Gate と、シャドウ ntdll (ローダーが ntdll.dll に対して NtMapViewOfSection を実行して新しいアドレスにプライベートコピーを取得する) の両方を捕捉します。シャドウのケースは興味深いものです。マッピングはモジュールスナップショットに含まれていないため、その syscall 命令は外部の実行可能バイトとして表示されますが、ディスク上の完全に正当な署名済み DLL から取得されたものです。

別の変種: 攻撃者は 0F 05 を自身のバイナリの .text に焼き付けるため、syscall は RWX ページではなくロード済みモジュール内に存在します。Foreign-RWX はロード済みモジュールをスキップするため、以前はこれがすり抜けていました。そこで、各モジュールの実行可能バイトをウォークし、ntdll、win32u、wow64cpu (syscall 命令を正当に持つ 3 つのモジュール) をスキップし、0F 05 の後に 3 バイト以内に C3 がある場合のみ報告する別のスキャナーを用意しました。ret チェックがこれを使用可能にします。0F 05 だけでは、コンパイラ出力でより大きな命令の一部として頻繁に出現しますが、C3 に隣接することは決してありません。実際の syscall スタブは常にそうなっています。ログ:

root@kitploit:~
[hallwatch] scan: module-text syscall stub at 0x00007FF7FFC9227C in HellsHall-Demo.exe -- Hell's Gate inside loaded module .text

整合性ワーカーには他に 2 つの小さな仕事があります。ティックごとに、パッチが適用された各スタブのバイトを読み取り、それが CC でない場合は CC を書き戻します。これは可能な限り単純なアンフック方法です。サンプルは元の 0F をどこか (新しい ntdll マッピング、ディスク上の PE、既知の正常なバイトテーブル) から読み取り、パッチを元に戻します。次のティックでそれを検出し、再度パッチを当てます。

50 ティックごと (約 12.5 秒) に、ワーカーは VEH をチェーンの先頭に再度インストールし、以前のハンドルを削除します。サンプルは LdrpVectorHandlerList をウォークして登録を解除できます。ワーカーはそのギャップが開いたままになる時間を制限します。12.5 秒は、攻撃されている場合は長いウィンドウであり、サンドボックス内でマルウェアを実行している場合は短いウィンドウです。より頻繁に実行するコストを測定したら、おそらく短縮するでしょう。

ワーカースレッドは TLS フラグを設定して実行され、これを VEH がチェックします。ワーカー自身の syscall (パッチ用の NtProtect、スキャン用の NtQuery) は他のものと同様にブレークポイントをトリガーしますが、フラグによって VEH は検出ロジックをスキップし、静かにトランポリンを通じてリダイレクトするように指示されます。


クリティカルセクションは、誰かが最初に呼び出したときに、3 状態の比較交換 (0 = 未初期化、2 = ビジー、1 = 準備完了) を介して自身を初期化します。これは醜いですが、DLL 内で静的コンストラクターを必要としないため、Windows ではローダーロックなどを含む別の問題セットが発生するのを回避します。

注目すべき ABI の点が 1 つあります。INT3 はトラップであり、Context->Rip は VEH が呼び出されたときに 次の 命令を指し、トラップ自体ではありません。0x7FF827660372 のバイトにパッチを当てた場合、Context->Rip は VEH に 0x7FF827660373 として到着します。Record->ExceptionAddress はトラップを指しますが、トランポリンにリダイレクトする前に Context->Rip = ExceptionAddress にリセットする必要があります。そうしないと、トランポリンが 1 バイト遅れて開始され、syscall は何も有用なことを行いません。これは、見つけるのに多くの時間を費やしたバグだったのでお伝えしておきます。


4 つのエクスポートがあります。IscInitialize は検出器をアームします。DllMain が自動的に呼び出しますが、戻り値が必要な場合はホストプロセスから呼び出すこともできます。IscGetDetectionCount は単調増加するカウンターを返します。IscShutdown はインフライトのハンドラーを待ち、0F バイトを復元します。IscFlush はリングを同期的にドレインします。これは、サンプルが終了する前にイベントを出力する必要があるサンドボックスに検出器を埋め込む場合に便利です。

最小限の統合は LoadLibrary です。DllMain が初期化を処理し、そこから両方のバックグラウンドスレッドを開始します。

Isc = 間接システムコール


まだ捕捉していないもの。

整合性チェックを行うサンプル。

パッチを適用していないスタブを使用するサンプル。現在の許可リストは約 40 の名前で、攻撃的なメモリ、プロセス、スレッド、セクション、トークン、ファイルプリミティブをカバーしています。DllMain が迅速に完了する限り、追加は増分的です。ローダーロック内から 488 すべてのスタブにパッチを適用するのは... ちょっとね。

カーネル権限で実行されるサンプル。ユーザーモードの問題ではありません。


INT3 を最終的に選択しましたが、興味深いのはトラップメカニズムではありません。PAGE_GUARD よりも優れている理由は、トラップが OS との継続的な交渉を必要としないことです。バイトは CC です。CC のままです。OS は ntdll 内にどのバイトが存在するかについて意見を持ちません。

ツールをダウンロード