reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
120
vendor-new/rev-skills/.claude/skills/re-windbg/SKILL.md
Normal file
120
vendor-new/rev-skills/.claude/skills/re-windbg/SKILL.md
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
name: re-windbg
|
||||
description: >
|
||||
WinDbg 调试(Windows 用户态+内核)。触发词:WinDbg、windbg、内核调试、kd、!analyze
|
||||
capabilities: [debugging]
|
||||
---
|
||||
|
||||
# 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`),宿主 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`,内核模块同理(先确认符号服务器可达)
|
||||
- **内核调试配置错 → 目标机蓝屏死等或连不上**:现象——目标机重启后停在 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]]
|
||||
@@ -0,0 +1,123 @@
|
||||
# WinDbg 命令速查与操作序列
|
||||
|
||||
WinDbg 命令三类:普通命令(`.exr`/`g`/`r`,点开头)、扩展命令(`!analyze`/`!process`,叹号开头、来自扩展 DLL)、伪寄存器(`$<名>`)。符号语法 `模块!符号`(`nt!KeWaitForSingleObject`)。官方参考: Microsoft Learn 调试器命令参考(debuggercmds)。
|
||||
|
||||
## 命令族速查
|
||||
|
||||
### 会话与运行
|
||||
|
||||
- `windbg -p <pid>` / `windbg <exe>`;`-g` 跳过初始断点;`-z <dump>` 打开 dump;`-k` 内核调试连接
|
||||
- `g` 继续;`gh`/`gn` 继续(异常交给程序/忽略);`Ctrl+Break` 中断;`q` 退出
|
||||
- `.restart` 重启目标;`.detach` 脱离;`.kill` 终止
|
||||
|
||||
### 断点
|
||||
|
||||
- `bp <模块!符号|地址>`;`bu <符号>` 未解析符号断点(模块加载后自动生效——目标 DLL 未加载时首选)
|
||||
- `bm <通配>` 批量断点(如 `bm nt!Io*`);`bl` 列表;`bd`/`be` 禁用/启用;`bc *` 清空
|
||||
- 条件断点: `bp addr ".if (rax==0) { } .else { gc }"`——不满足条件自动继续
|
||||
- 命令断点: `bp addr "r; k; gc"` 命中时执行命令后继续(日志式观察)
|
||||
|
||||
### 单步与栈
|
||||
|
||||
- `p` 步过;`t` 步入;`pa <addr>` 步过到地址;`ta <addr>` 步入到地址;`gu` 执行到返回;`pt` 执行到 call 返回
|
||||
- `k` / `kb` / `kv` 栈回溯(参数/符号帧);`kn` 带帧号;`dps rsp` 按指针看栈内容
|
||||
- `.frame <n>` 切栈帧;`.ecxr` 设异常上下文(dump 分析开局);`.exr <addr>` 显示异常记录;`.cxr <addr>` 手动设上下文
|
||||
|
||||
### 寄存器与内存
|
||||
|
||||
- `r` / `r rax` / `r eax=0`;`r $t0=...` 伪寄存器(脚本临时变量)
|
||||
- `db`/`dd`/`dq`/`du` 读内存(字节/双字/四字/Unicode);`dps` 按指针解释
|
||||
- `eb`/`ed` 写内存(`eb 0x401000 90`);`u <addr>` 反汇编(`u 地址 L数量`)
|
||||
- 搜索: `s -d 0x0 L?10000000 0xdeadbeef`(值)、`s -a 0x0 L?10000000 "text"`(ASCII 字符串)——在进程虚拟地址空间找常量/串
|
||||
- 内存映射: `!address <addr>` 区域信息(权限/映射);`!vad` 用户态 VAD 树
|
||||
|
||||
### 符号
|
||||
|
||||
- `.sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols`;`.symfix` 用默认符号服务器
|
||||
- `.reload /f [模块]` 强制重载符号;`lm`/`lmv m <模块>`/`lmi <模块>` 模块与镜像信息
|
||||
- `x <模块>!*<关键字>*` 搜索符号(如 `x nt!*ExAlloc*`)
|
||||
|
||||
### 扩展命令(常用子集)
|
||||
|
||||
- 崩溃: `!analyze -v`(EXCEPTION_CODE/FAULTING_IP/STACK_TEXT/PROCESS_NAME)
|
||||
- 进程/线程: `!process 0 0`;`!process <EPROCESS> 1`;`!thread`;`!teb`;`!peb`
|
||||
- 句柄/堆: `!handle <句柄>`;`!heap -a`;`!pool <地址>`(内核池块)
|
||||
- 内核对象: `!object <地址>`;`!devobj`/`!drvobj`(设备/驱动对象);`!irp <地址>`;`!stacks 0`(全栈汇总)
|
||||
- 其他: `!dlls`(进程模块树)、`!runaway`(线程 CPU 时间)、`!locks`(锁)
|
||||
|
||||
### dump 与转储
|
||||
|
||||
- 用户态全量: `.dump /ma C:\x.dmp`(含句柄/内存,取证标准);`.dump /f`(full)
|
||||
- 内核: 目标机 `%SystemRoot%\Minidump\*.dmp`(小转储);完全 dump 需配置内核 dump 设置
|
||||
- `.writemem <文件> <起> <止>` 导出内存区段;`.readmem` 读回
|
||||
|
||||
### TTD(时间旅行调试,需管理员)
|
||||
|
||||
- 录制: `ttd.exe -out C:\traces target.exe args`;`ttd.exe -attach <pid> -out ...`;`-monitor <名>`;`-ring`(环形 2GB 上限)
|
||||
- 打开: WinDbg `File > Open` 选 `.run`;索引: `!tt.index`(首次慢,之后快)
|
||||
- 回放: `g-` 往回跑;`t-`/`p-` 往回单步;`!tt 50` 跳百分比;`!tt 位置` 跳 `事件:步数`
|
||||
- 断点: `!tt br <寄存器> [值]`(值变化处停,`br-` 往回找);`!tt bm <地址范围>`(内存访问处停)
|
||||
- 数据模型: `dx @$cursession.TTD.Calls("模块!函数")`;`dx @$cursession.TTD.Memory(起, 止, "rwec")`(内存访问记录)
|
||||
- `ttd.exe -stop all` 停止所有录制;`-help` 全参数
|
||||
|
||||
## 常用操作序列(组合套路)
|
||||
|
||||
### 1. 崩溃 dump 分析(minidump 标准开局)
|
||||
|
||||
```
|
||||
windbg -z C:\dump.dmp
|
||||
.sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols; .reload /f
|
||||
.ecxr # 异常上下文设现场(WER dump 的当前上下文在 WerpReportFault 里不可信)
|
||||
k # 真实栈回溯
|
||||
!analyze -v # 自动化分析:FAULTING_IP / STACK_TEXT 逐帧核对
|
||||
```
|
||||
|
||||
### 2. 校验绕过(断 API → 改返回 → 继续)
|
||||
|
||||
```
|
||||
windbg 目标.exe(-g 可跳过初始断点)
|
||||
bp kernel32!CreateFileW # 或目标校验 API
|
||||
g → 命中后:
|
||||
r rax=0 # 改返回值
|
||||
gu; g # 出函数后继续
|
||||
```
|
||||
|
||||
### 3. 驱动/内核模块符号与深度分析
|
||||
|
||||
```
|
||||
!process 0 0 → 找目标进程 EPROCESS
|
||||
.process /p <EPROCESS>; .reload /f
|
||||
lmv m <驱动名> # 基址/版本/路径
|
||||
bp <驱动名>!<函数> # 驱动内符号断点
|
||||
!analyze -v(内核崩溃时出 bugcheck + 故障驱动)
|
||||
```
|
||||
|
||||
### 4. TTD 录制 → 回放找解密/校验点
|
||||
|
||||
```
|
||||
ttd.exe -out C:\traces target.exe # 管理员命令行录制
|
||||
WinDbg 打开 .run → !tt.index
|
||||
dx @$cursession.TTD.Calls("模块!可疑函数") # 看调用次数/参数分布
|
||||
!tt br <寄存器> → 定位值变化点 → 前向/后向单步看上下文
|
||||
```
|
||||
|
||||
### 5. 栈损坏现场恢复(live 会话)
|
||||
|
||||
```
|
||||
命中崩溃 → .exr <EXCEPTION_RECORD 地址>(!analyze -v 的 STACK_TEXT 里取)
|
||||
.cxr <CONTEXT 地址> → k → kv
|
||||
```
|
||||
|
||||
## 实现教训(内化)
|
||||
|
||||
- `!analyze -v` 是起点不是终点——`STACK_TEXT` 后必须 `.ecxr` + `k` 手工核对真实调用链
|
||||
- 符号未加载时一切输出都是裸偏移——先 `.symfix` + `.reload /f`,断点用 `bu`(未解析也可设)
|
||||
- 条件断点用 `.if/.else + gc` 语法(不是 gdb 的 `if` 后缀);命令断点实现日志式观察不中断
|
||||
- `s` 搜索在用户态可用 `L?<范围>`;内核态地址空间搜索量大,先缩小范围
|
||||
- TTD 的 `!tt bm`/`br` 是「找内存/寄存器事件」的利器——比反复重跑目标快得多
|
||||
- 栈损坏场景 `.ecxr`/`.cxr` 是唯一可靠回溯路径,别信 `k` 的直接输出
|
||||
|
||||
## 使用注意
|
||||
|
||||
- 全部在沙箱内执行([[re-analyze/platform-tips]] 最高原则);attach/内核调试需管理员权限
|
||||
- 结论写 [[re-analyze/analysis-contract]];dump 产物 sha256 与 [[re-triage]] 初勘值对照;驱动场景配合 [[re-kernel]]
|
||||
@@ -0,0 +1,46 @@
|
||||
# WinDbg 工具特有坑与边界
|
||||
|
||||
## 版本差异(经典版 vs 商店版 vs WDK)
|
||||
|
||||
- **经典版(WinDbg 10.0.x)**:随 Windows SDK "Debugging Tools for Windows" 安装;传统 Win32 UI,`windbg.exe`/`kd.exe`/`cdb.exe` 同包
|
||||
- **商店版(WinDbg 1.x,WinDbg Next)**:`winget install Microsoft.WinDbg` 或 Store;Chromium 新 UI,命令与经典版兼容(同一套调试引擎),附加/断点/内存视图的 UI 入口不同——教程截图对不上时按命令窗口操作,命令是通用的
|
||||
- **WDK**:装内核调试工具链(kd.exe/kdnet.exe、`bcdedit` 相关脚本、驱动测试工具)——只做用户态调试不需要装整个 WDK
|
||||
- **32/64 位 WinDbg 分开**:经典版有 x86/x64 两个可执行文件——64 位 dump 用 32 位 WinDbg 打开会报格式错;商店版自动处理
|
||||
- **命令兼容性**:`!` 扩展命令由扩展 DLL 提供(`ntsdexts`/`ext` 等),版本旧时个别扩展缺失——`!analyze` 等核心命令在所有版本都有
|
||||
|
||||
## 符号服务器坑
|
||||
|
||||
- **未配置符号 → 全裸偏移**:`lm` 行尾 `(no symbols)`、`k` 输出 `模块!+0x1a`——`.sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols` 后 `.reload /f`
|
||||
- **符号服务器不可达/代理**:内网环境拉不到符号会挂起(重试很久)——`.sympath` 指向本地符号缓存优先,配 `_NT_SYMBOL_PATH` 时先确认网络可达
|
||||
- **私有符号(未公开)**:系统 DLL 的公有符号只到函数粒度,行号/局部变量需要私有符号——分析系统组件逻辑时以反汇编为准,别依赖 `dv` 的局部变量
|
||||
- **`bu` vs `bp`**:`bu`(unresolved)对尚未加载的模块符号有效(模块加载后自动绑定)——设 DLL 内断点用 `bu`,`bp` 会因符号未解析失败
|
||||
- **过期符号缓存**:符号缓存陈旧与二进制不匹配(服务包更新后)——`!analyze -v` 出现乱地址时清掉 `C:\symbols` 里对应条目重新拉
|
||||
|
||||
## 内核调试坑
|
||||
|
||||
- **PatchGuard(内核保护)限制**:本地内核调试(`File > Kernel Debug > Local`)仅测试机可用且能力受限(PatchGuard 干扰断点/单步);正式内核分析用双机/VM 传输
|
||||
- **传输配置必须两边一致**:串口参数(debugport/baudrate)或 NET 参数(hostip/port/key)在宿主与目标必须完全匹配——`bcdedit /dbgsettings` 重查配置;VM 管道串口用 `\\.\pipe\com_1` 两边同名
|
||||
- **kdnet key 有效期**:`kdnet.exe` 生成的 key 与目标网卡绑定,换网卡/重装后需重新生成
|
||||
- **内核调试不能暂停全部内核线程**:`g` 后系统在跑,断点只在事件时刻停——时序类 bug 用 `!analyze -v` 或 TTD(用户态)思路替代
|
||||
- **wow64 内核调试**:32 位进程线程栈在 x86 层,`!process` 字段按位数对——跨位数核对时先确认进程位数
|
||||
|
||||
## 异常/栈现场坑
|
||||
|
||||
- **WER dump 的当前上下文不可信**:Windows Error Reporting 抓的 dump 停在 `WerpReportFault` 报告路径里——直接 `k` 看到的是报告代码的栈;必须 `.ecxr` 恢复异常上下文后再 `k`(`.exr -1` 先看异常记录也行)
|
||||
- **`.cxr` vs `.ecxr`**:`.cxr <addr>` 手动指定 CONTEXT 地址;`.ecxr` 自动取当前异常上下文——dump 分析用 `.ecxr`,live 现场用 `.exr` 拿地址后 `.cxr`
|
||||
- **栈损坏时 `k` 假象**:栈指针被踩后 `k` 输出乱地址——`.exr`/`.cxr` 恢复现场是唯一可靠路径;`!analyze -v` 的 `STACK_TEXT` 里直接取现场地址
|
||||
- **`!analyze -v` 是辅助不是结论**:它按启发式给 FAULTING_IP/STACK_TEXT,混淆/罕见场景会指向错误位置——逐帧核对 `STACK_TEXT`,必要时手工 `.exr`/`k`
|
||||
|
||||
## TTD 限制
|
||||
|
||||
- **管理员权限必需**:ttd.exe 录制需要管理员;非管理员运行静默失败或录不出轨迹
|
||||
- **仅用户态**:不能录内核/驱动;PPL 保护进程无法注入录制
|
||||
- **轨迹文件巨大**:数分钟可到数 GB(无上限)——`-maxfile <MB>` 限制或 `-ring`(2GB 环形覆盖);索引 `.idx` 约两倍轨迹大小
|
||||
- **回放只读**:轨迹里不能写内存/改寄存器——「验证假设」在回放中做不了,只能观察;要改就重新录制或转常规调试
|
||||
- **录制开销高**:录制期间目标明显变慢(数倍);`-numvcpu` 可调;个别框架(Electron 类)录制时可能死锁/崩溃——录不了就退回常规断点调试
|
||||
- **`.run` 文件含敏感信息**:轨迹含进程内存/文件路径/寄存器值——分享轨迹等于分享数据,注意脱敏(与 [[re-malware]] 报告同原则)
|
||||
|
||||
## 使用注意
|
||||
|
||||
- 全部在沙箱内执行([[re-analyze/platform-tips]] 最高原则);attach/内核调试需管理员权限
|
||||
- 版本相关行为(扩展命令集、UI 入口)以实际安装版本为准;命令窗口是跨版本最稳定的界面
|
||||
Reference in New Issue
Block a user