11 KiB
11 KiB
name, description, capabilities
| name | description | capabilities | |
|---|---|---|---|
| re-windbg | WinDbg 调试(Windows 用户态+内核)。触发词:WinDbg、windbg、内核调试、kd、!analyze |
|
WinDbg 动态调试(Windows 用户态 + 内核)
何时使用 / 何时不用
- 用:Windows 用户态进程调试(启动/附加、断点、单步、寄存器);崩溃/异常分析(
!analyze -v);内核调试(双机/VM/本地 kd);驱动与内核模块分析(配合 re-kernel);崩溃 dump(minidump/full dump)分析 - 用:时间旅行调试(TTD)——
ttd.exe录制 .run 轨迹后任意回放,适合「复现一次慢慢查」的校验/解密逻辑(见 commands TTD 节) - 不用:Linux 目标(走 re-gdb);macOS 目标(re-lldb);只需轻量 GUI 调试(re-x64dbg 更快)
- 不用:只做静态分析/反编译(re-ida / re-ghidra / re-binaryninja);无需与运行态互动的纯静态流程不需要调试器
- 不用:WSL 内跨边界 attach Windows 进程(re-analyze/platform-tips WSL 分支:跨边界走 Windows 侧工具)
工具准备
参考 re-analyze/platform-tips Windows 分支——attach 需要管理员权限;调试属动态执行,默认沙箱内进行(re-analyze/platform-tips 最高原则)。
WinDbg(微软商店版 / Windows SDK / WDK)
- 商店版(新 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输出版本号 - 版本差异: 商店版(WinDbg 1.x,Chromium UI)与经典版(WinDbg 10.0.x)命令兼容,UI 与附加流程不同;dump 分析两者通用(见 gotchas)
Windows 符号(Windows SDK 符号 / 公共符号服务器)
- 符号路径:
.sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols(或设环境变量_NT_SYMBOL_PATH) - 内核模块符号: 内核调试下
.reload /f nt(强制从符号服务器拉取) - 验证:
!analyze -v输出中出现函数名而非+0x1a裸偏移
双机/虚拟机内核调试环境
- 目标机(被调试):
bcdedit /debug on+bcdedit /dbgsettings serial debugport:1 baudrate:115200(串口)或bcdedit /dbgsettings net hostip:<宿主机IP> port:50000 key:<kdnet.exe生成的key>(NET 网卡,Windows 10+ 推荐) - VM 场景: VMware/Hyper-V 给目标 VM 添加串口设备(Named Pipe
\\.\pipe\com_1/-vmconnect),宿主 WinDbgFile > Kernel Debug > COM填管道名 - 本地内核调试(仅测试机,需测试签名):
bcdedit /set testsigning on重启后,WinDbgFile > 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 节)
操作步骤
-
附加/启动用户态目标:
- 启动:
File > Open Executable选目标 exe,停在初始断点(ntdll 加载完成处) - 附加:
File > Attach to Process按 PID/进程名选目标(管理员权限启动 WinDbg 才能附加高权限进程) - 命令行:
windbg -p <pid>/windbg 目标.exe;-g跳过初始断点直接运行
- 启动:
-
断点 / 单步 / 寄存器:
- 符号断点:
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*)——不确定精确符号名时模糊匹配
- 符号断点:
-
异常分析
!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 码与故障模块
- 目标崩溃停下后:
-
内核调试(双机 / 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(仅测试机,不影响生产机的用法)
- 目标机配置见「工具准备」;配置后重启,宿主
-
扩展命令:
!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
-
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)
- 断点前移/后移:
-
伪寄存器与脚本化(简单自动化):
- 伪寄存器:
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,内核模块同理(先确认符号服务器可达) - 内核调试配置错 → 目标机蓝屏死等或连不上:现象——目标机重启后停在 debug 等待或直接进系统未中断;原因——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里直接取现场 - 蓝屏 dump 打不开:现象——
Open Crash Dump报格式错;原因——minidump 不完整(磁盘满)或用的 32 位 WinDbg 读 64 位 dump;对策——目标机确认内核页交换文件(crash dump 需要 pagefile)足够大,改用同位数 WinDbg - RPC 调用统一入口 NdrStubCall3(svchost 功能逆向):现象——要拦/分析某个系统功能(如 Toast 通知),不知道它经什么通道进 svchost;原因——功能实现经 RPC 到 svchost 进程,入口难找;对策——Process Monitor 看目标进程加载 rpcrt4.dll + RpcView 确认 RPC 目标进程;所有 RPC 服务端 stub 统一走 rpcrt4 的 NdrStubCall3(x64;NdrStubCall2 是旧版 x86,ReactOS/Wine 源码可印证)——在其下断
kvn看调用来源;筛选目标调用:第三参数(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