mcpField
R
re-windbg

re-windbg

github.com/dslsdzc開発20658 回インストール2 週間前に更新
リポジトリ

re-windbg について

re-windbgは、Windowsのユーザーモードとカーネルのリバースエンジニアリング向けWinDbgワークフローです。クラッシュダンプ、カーネルデバッグ、TTDトレース再生に加え、ブレークポイント、レジスター、例外の分析手順を扱います。

主な機能

  1. 01ユーザーモードの起動、アタッチ、ブレークポイント
  2. 02クラッシュダンプと例外の分析
  3. 03ローカル、VM、二台構成でのカーネルデバッグ
  4. 04TTDトレースの記録と再生
  5. 05シンボル、レジスター、メモリーの確認

活用シーン

  1. 01Windowsプロセスのクラッシュ原因の特定
  2. 02minidumpやカーネルクラッシュダンプの分析
  3. 03ドライバーとカーネルモジュールの実行状態の確認
  4. 04トレース再生による検証・復号ロジックの調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dslsdzc/rev-skills --skill re-windbg

インストール後、Claude Code で/re-windbgを入力して読み込まれたか確認します。

同梱ファイルreferences/commands.mdreferences/gotchas.md

詳細

WinDbg の動的デバッグ(Windows ユーザーモード+カーネル)

使用する場合/使用しない場合

  • 使用する:Windows ユーザーモードプロセスのデバッグ(起動/アタッチ、ブレークポイント、ステップ実行、レジスター);クラッシュ/例外の分析(!analyze -v);カーネルデバッグ(2 台構成/VM/ローカル kd);ドライバーとカーネルモジュールの分析([[re-kernel]] と併用);クラッシュダンプ(ミニダンプ/フルダンプ)の分析
  • 使用する:タイムトラベルデバッグ(TTD)——ttd.exe で .run トレースを記録して任意の位置から再生できます。「一度再現して、じっくり調べる」のに適した検証/復号ロジックに向いています([[commands]] の TTD 節を参照)
  • 使用しない:Linux のターゲット([[re-gdb]] を使用);macOS のターゲット([[re-lldb]]);軽量な GUI デバッグのみが必要な場合([[re-x64dbg]] のほうが高速です)
  • 使用しない:静的解析/逆コンパイルのみを行う場合([[re-ida]]/[[re-ghidra]]/[[re-binaryninja]]);実行中のプロセスとやり取りしない純粋な静的ワークフローでは、デバッガーは不要です
  • 使用しない:WSL 内から境界を越えて Windows プロセスにアタッチする場合([[re-analyze/platform-tips]] の WSL 分岐:境界を越える場合は Windows 側のツールを使用します)

ツールの準備

[[re-analyze/platform-tips]] の Windows 分岐を参照してください——アタッチには管理者権限が必要です。デバッグは動的実行にあたるため、デフォルトでサンドボックス内で行います([[re-analyze/platform-tips]] の最優先原則)。

WinDbg(Microsoft Store 版/Windows SDK/WDK)
  • Store 版(新しい UI の WinDbg):winget install Microsoft.WinDbg(または Microsoft Store で "WinDbg" を検索)
  • クラシック版:Windows SDK インストーラーで "Debugging Tools for Windows" にチェックを入れます;choco install windbg
  • WDK(カーネルデバッグツール kd.exe/kdnet.exe を含む):winget install Microsoft.WindowsWDK または Visual Studio インストーラーで WDK にチェックを入れます
  • 確認:WinDbg を開き、File > Open Executable から任意の exe を読み込んで初期ブレークポイントで停止できることを確認します。コマンドウィンドウで ver を実行すると、バージョン番号が出力されます
  • バージョンによる違い:Store 版(WinDbg 1.x、Chromium UI)とクラシック版(WinDbg 10.0.x)はコマンドに互換性がありますが、UI とアタッチ手順は異なります。ダンプ分析はどちらでも同じように行えます([[gotchas]] を参照)
Windows シンボル(Windows SDK シンボル/パブリックシンボルサーバー)
  • シンボルパス:.sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols(または環境変数 _NT_SYMBOL_PATH を設定します)
  • カーネルモジュールのシンボル:カーネルデバッグ中に .reload /f nt を実行します(シンボルサーバーから強制的に取得します)
  • 確認:!analyze -v の出力に、+0x1a のようなオフセットではなく関数名が表示されることを確認します
2 台構成/仮想マシンのカーネルデバッグ環境
  • ターゲットマシン(デバッグ対象):bcdedit /debug on と bcdedit /dbgsettings serial debugport:1 baudrate:115200(シリアル)または bcdedit /dbgsettings net hostip:<宿主機IP> port:50000 key:<kdnet.exe生成的key>(NET NIC、Windows 10+ で推奨)を設定します
  • VM の場合:ターゲット VM にシリアルデバイス(名前付きパイプ \\.\pipe\com_1/-vmconnect)を追加し、ホスト側の WinDbg で File > Kernel Debug > COM を開き、パイプ名を入力します
  • ローカルカーネルデバッグ(テストマシンのみ。テスト署名が必要):bcdedit /set testsigning on を実行して再起動した後、WinDbg で File > Kernel Debug > Local を選択します
  • 確認:ターゲットマシンの再起動後に "Waiting to reconnect" で停止するか、デバッグ待機状態になったら、ホスト側の WinDbg でアタッチできることを確認します。g の後に Ctrl+Break で中断でき、lm でモジュールが一覧表示されることを確認します
TTD(タイムトラベルデバッグ、Windows 10 1903+)
  • 記録:ttd.exe -out C:\traces target.exe(または -attach <pid> でアタッチして記録;-monitor <进程名> で起動を監視)
  • 開く:WinDbg で File > Open を選択して .run トレースファイルを開きます(または Open Trace File)
  • 確認:ttd.exe -help で出力が表示されることを確認します。記録した後、WinDbg で開き、!tt で任意の位置に移動でき、g- で逆方向に実行できることを確認します(詳しくは [[commands]] の TTD 節を参照)

操作步骤

  1. 附加/启动用户态目标:

    • 启动: File > Open Executable 选目标 exe,停在初始断点(ntdll 加载完成处)
    • 附加: File > Attach to Process 按 PID/进程名选目标(管理员权限启动 WinDbg 才能附加高权限进程)
    • 命令行: windbg -p <pid> / windbg 目标.exe;-g 跳过初始断点直接运行
  2. 断点 / 单步 / 寄存器:

    • 符号断点: bp kernel32!CreateFileW;地址断点: bp 0x401000;bl 列表、bd/be 禁用/启用、bc * 清空
    • 未加载模块的符号用 bu(unresolved,模块加载后自动绑定)——目标 DLL 按需加载时 bu 是正解
    • 命令断点: bp 地址 "r rax; k; gc"——命中时打印现场后自动继续,日志式观察不打断流程
    • 单步: g 继续、p 步过、t 步入、gu 执行到当前函数返回
    • 寄存器: r 全部、r rax 查看、r eax=0 修改(绕过校验常用)
    • 内存/反汇编: u 0x401000 反汇编、dd/db 读内存、dps rsp 看栈内容
    • 搜索: s -d 0x0 L?10000000 0xdeadbeef(值)、s -a 0x0 L?10000000 "text"(ASCII 串)——找硬编码常量/密钥用
    • 符号搜索: x 模块!*关键字*(如 x kernel32!*Virtual*)——不确定精确符号名时模糊匹配
  3. 异常分析 !analyze -v:

    • 目标崩溃停下后: !analyze -v → 读 EXCEPTION_CODE、FAULTING_IP、STACK_TEXT、PROCESS_NAME
    • 内核蓝屏对应 bugcheck 码(BUGCHECK_CODE,如 0xD1 驱动访问无效内存)——先记码再往下查,常见码含义要能对上故障方向
    • 现场恢复: 从 STACK_TEXT 取 EXCEPTION_RECORD 地址 .exr <addr>(异常记录),再取 CONTEXT 地址 .cxr <addr> 恢复寄存器现场,之后 k 出真实调用栈(栈损坏时唯一可靠的栈回溯方式)
    • dump 现场更简单: 打开 dump 后直接 .ecxr(自动用异常上下文设寄存器现场)→ k——minidump 分析标准开局
    • dump 分析: File > Open Crash Dump 打开 .dmp(用户态用 .dump /ma 生成;内核蓝屏用 %SystemRoot%\Minidump\*.dmp 或完全 dump),同样 !analyze -v 出 bugcheck 码与故障模块
  4. 内核调试(双机 / VM / 本地 kd):

    • 目标机配置见「工具准备」;配置后重启,宿主 File > Kernel Debug 按所用传输(COM/NET)连接
    • VM 管道串口流程: 目标 VM 加串口 Named Pipe → 目标机 bcdedit /dbgsettings serial → 宿主 File > Kernel Debug > COM 填管道名(\\.\pipe\com_1)→ 目标机重启等连
    • 连接后: g 让目标继续跑;Ctrl+Break 随时中断回调试器;!process 0 0 确认会话活着
    • 本地调试: bcdedit /set testsigning on 重启后 File > Kernel Debug > Local(仅测试机,不影响生产机的用法)
  5. 扩展命令:

    • !process 0 0(全部进程列表)、!process <EPROCESS> 1(单进程详情+线程);.process /p <EPROCESS> 切到目标进程上下文后再 k/!thread
    • !teb(当前线程 TEB)、!peb(当前进程 PEB——命令行、加载模块、环境块)
    • lm(模块列表,行尾 (deferred)=符号未加载、(no symbols)=无符号)、lmv m <模块>(版本+路径)、lmi <模块>(镜像头)
    • kv(带符号完整栈回溯,驱动分析常用)、!analyze -v 后按 STACK_TEXT 逐帧核对
    • 驱动专项: lmv m 驱动名 拿基址 → .reload /f 驱动名 加载符号 → 深挖转 [[re-kernel]]
  6. TTD 回放(校验/解密逻辑慢查):

    ttd.exe -out C:\traces target.exe     # 录制
    WinDbg 打开 .run → g- / t- / p-       # 往回跑
    !tt 50                                 # 跳到轨迹 50% 处
    !tt 7213:36                            # 跳指定位置(事件序号:步数)
    
    • 断点前移/后移: !tt br 在某寄存器值变化处停;!tt bm 在内存访问处停
    • 首次打开轨迹先 !tt.index 建索引(之后打开跳过);回放中途也可断点 bp(在轨迹上工作)
    • 查询 API: dx @$cursession.TTD.Calls("模块!函数") 统计调用次数/参数(如 ...Calls("ntdll!mem*"))
    • TTD 限制: 需管理员运行、用户态专用、轨迹文件很大(数 GB)、回放只读不能写内存(见 [[gotchas]])
  7. 伪寄存器与脚本化(简单自动化):

    • 伪寄存器: r $t0 = 0 起临时变量;$$ > <文件> 把命令输出重定向到文件(如 $$ > c:\log.txt)
    • 条件执行: .if (rax==0) { ... } .else { ... }——配合 gc 做断点内逻辑
    • !runaway 看线程 CPU 时间(找忙等/死循环线程);!locks 查锁状态(挂起排查)

跨域联合

  • [[re-binary-core]]:工作流第 6 步 Windows 调试器(用户态+内核)
  • [[re-kernel]]:内核调试与驱动/rootkit 运行时验证固定调用本技能
  • [[re-anti-analysis]]:反调试对抗(内核级检测点)、脱壳后验证
  • [[re-malware]]:恶意样本崩溃 dump 分析与内核组件
  • [[re-cracking]]:授权校验崩溃定位(!analyze -v 快速找崩溃原因)
  • 与 [[re-x64dbg]] 互补:轻量 GUI 断点调试用 x64dbg,内核/异常现场恢复/轨迹回放用 WinDbg

よくある落とし穴と注意点

  • シンボルが設定されていない → オフセットだけで関数名が表示されない:現象——lm の行末に (no symbols)、k の出力が ntdll!+0x1a になります。原因——シンボルパスが空、または _NT_SYMBOL_PATH が設定されていません。対策——.sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols の後に .reload /f を実行します。カーネルモジュールも同様です(まずシンボルサーバーにアクセスできることを確認してください)。
  • カーネルデバッグの設定ミス → ターゲットマシンがブルースクリーンのまま応答しない、または接続できない:現象——ターゲットマシンの再起動後、デバッグ待ちで停止するか、中断されずにそのままシステムが起動します。原因——bcdedit の転送方式/ポートがホストと一致していないか、物理マシン単体でのローカルデバッグに制限があります(PatchGuard)。対策——VM のシリアルパイプを使う方法が最も安定しています(両側で \\.\pipe\com_1 を指定します)。bcdedit /dbgsettings で設定を再確認し、物理マシンでは KDNET を優先してください。
  • 32/64 ビットでのコマンドとレジスターの違い:現象——64 ビットのターゲットで r eax を実行すると誤った値が変更される、!teb のフィールドが一致しません。原因——x64 のコンテキストでは rax/rbx... を使います(下位 32 ビットは eax などです)。wow64 のターゲットスレッドのスタックは x86 層にあります。対策——まずターゲットのビット数を確認します。wow64 の場合は .load wow64exts と !wow64exts.sw で x86 コンテキストに切り替えてください。
  • スタックが破損しているときの k は当てになりません:現象——コールスタックが乱れています(アドレスがコードらしくありません)。原因——スタックポインターが破損しているか、メモリが上書きされており、通常のスタックトレースは信頼できません。対策——.exr で例外レコードを取得し、.cxr で CONTEXT を復元してから k を実行します。必要に応じて !analyze -v の STACK_TEXT から直接、現場の情報を取得してください。
  • ブルースクリーンダンプを開けない:現象——Open Crash Dump で形式エラーが表示されます。原因——minidump が不完全(ディスク容量不足)か、32 ビット版 WinDbg で 64 ビットのダンプを読み込んでいます。対策——ターゲットマシンでカーネルページファイル(クラッシュダンプには pagefile が必要)が十分なサイズであることを確認し、同じビット数の WinDbg を使用してください。
  • RPC 呼び出しの共通入口 NdrStubCall3(svchost の機能をリバースエンジニアリングする場合):現象——Toast 通知などの特定のシステム機能をフック/分析したいが、どの経路を通って svchost に到達するのかわかりません。原因——機能の実装は RPC 経由で svchost プロセスに到達するため、入口を見つけにくいです。対策——Process Monitor で対象プロセスが rpcrt4.dll を読み込んでいることを確認し、RpcView で RPC の接続先プロセスを特定します。すべての RPC サーバースタブは rpcrt4 の NdrStubCall3 を経由します(x64。NdrStubCall2 は旧版の x86 用です。ReactOS/Wine のソースコードで確認できます)。ここにブレークポイントを設定し、kvn で呼び出し元を確認します。対象の呼び出しを絞り込むには、第 3 引数(r8)が指す RPC_MESSAGE を確認します(+0x1C は ProcNum メソッド番号、+0x28 はインターフェイス記述子ポインター RpcInterfaceInformation → RPC_SERVER_INTERFACE の InterfaceId は +0x04)。opnum は MIDL によってメソッドの宣言順に 0 から割り当てられます(例:PostNotification3 が 0x29 番目なら opnum=0x29)。ObjectStublessClientN では rax=N が ProcNum です。注意点:NdrStubCall3 は svchost 内のすべての RPC インターフェイスを処理するため、必ず InterfaceId と ProcNum の両方で絞り込み、対象のメソッドだけをフックしてください(誤ってフックすると、Edge などのほかの機能が停止します)。
  • TTD の制限、バージョンの違い、シンボルサーバーの詳細については [[gotchas]] を参照してください。

コメント