A!die Software Studio Welcome to A!Die Software Studio

WinDbg 命令参考

by adie
2016-12-25

命令行

WinDbg -I 将 WinDBG 配置为 JIT 调试器
WinDbg -o process_path 自动附加到进程创建的子进程上
WinDbg -k com:pipe,port=\\.\pipe\pipename 通过命名管道连接虚拟机进行内核调试
WinDbg -kl 本地内核调试
WinDbg -z filename.dmp 打开 dump 文件
WinDbg -y symbol_path 指定符号路径, 这里的路径将会被附加到环境变量后面
WinDbg -c "commands" 启动时运行的初始调试器命令.
ntsd -d process_path 将调试器的控制传递给内核, 使得可以通过内核调试器调试用户态进程
ntsd -d -p pid 同上
gflags /p /enable process_path /debug "debug_cmdline" 设置 IFEO, 启动进程时先启动调试器来对进程进行调试
gflags /p /disable process_path 取消 IFEO 设置

快捷键

Alt+1 打开命令输入窗口
Alt+2 打开 Watch 窗口
Alt+3 打开 Locals 窗口
Alt+4 打开寄存器窗口
Alt+5 打开内存窗口
Alt+6 打开调用堆栈窗口
Alt+7 打开反汇编窗口
Alt+8 打开笔记窗口
Alt+9 打开进程和线程窗口
Ctrl+N 打开命令浏览器窗口
F5 恢复运行 (g: go)
F10 单步跳过 (p: step over)
F11 单步进入 (t: step in)
Shift + F11 执行到返回 (gu: go up)
Ctrl + Break 中断运行, 既可以中断目标进程的运行, 也可以中断循环运行中的脚本

表达式示语法

@@(expression) 将 () 内的表达式按当前默认语法外的另外一种语法来进行解析.
@@C++(expression) 将 () 内的表达式按 C++ 语法来进行解析.
@@masm(expression) 将 () 内的表达式按 MASM 语法来进行解析.
@"text" 原始字符串, 不翻译转义字符
MASM语法: 数值默认进制可以用 n 命令查看, n Radix 设置
0nNumber 十进制
0xNumber 十六进制
0tNumber 八进制
0yNumber 二进制
xxxxxxxx`xxxxxxxx 64 位 16 进制数, 重音符号会阻止符号扩展.
. 当前指令的地址
!ModuleName 代表了模块的基地址, 模块名不包含扩展名和路径. 如果模块名中包含了非法字符, 比如 -, + 等等, 由下划线代替. 如果模块符号名可能和其他符号冲突, 前面需要加 ! 进行区分.
!ModuleName!SymbolName 符号总是以字母, _, ?, $ 开头, 不区分大小写. 如果有冲突, 前面可以加模块名和 ! 来限制. 如果和数字冲突, 可以只加 ! 来区分.符号中可以使用 :: 或 __ 来表示类的成员.
$!SymbolName 指明符号是一个局部变量. 当局部变量名字和其他名字有冲突时加 $! 可指明为局部变量.
`ModuleName!FileName:LineNumber` 源代码行数对应的地址. 模块名可以省略; 文件名如果冲突, 需加路径, 省略则使用当前指针对应的文件; 行数默认使用十进制, 和 n 命令的设置无关.
@RegName 指明符号是一个寄存器. 如果寄存器名字和其他名字有冲突, 前面加 @ 可以明确指明他是一个寄存器.
@$RegName 指明符号是一个伪寄存器. 如果寄存器名字没有冲突, 可以省略 @, 但是加上 @ 可以让解析更快. 伪寄存器包括自定义伪寄存器和自动伪寄存器. 自定义伪寄存器为 @$t0, @$t1, ..., @$t19 . 它们是可以通过调试器读写的变量, 能用来保存任意整数值.自定义伪寄存器缺省为零.
自动伪寄存器: 自动伪寄存器是调试器会自动赋值的变量.
$ea effective address, 最后一条被执行指令的有效地址, 如果这条指令有两个有效地址,则表示第一个地址.
$ea2 最后一条被执行指令的第二个有效地址.
$ra return address, 当前堆栈的返回地址.
$ip 指令指针, 根据不同的系统对应不同的真实寄存器.
$eventip 当前事件发生时的指令指针, 通常和 $ip 匹配, 除非你切换了线程或者手动改变了指令指针的值.
$previp 前一个事件发生时的指令指针.
$relip 和当前事件相关的指令指针, 当你正在跟踪分支指令时, 这个是分支来源指针.
$scopeip 当前局部上下文(也称为作用域)的指令指针.
$exentry 当前进程的第一个可执行的入口点地址.
$retreg 返回值寄存器的值.
$retreg64 返回值寄存器, 以64位格式.
$csp 当前调用堆栈指针, esp, rsp 等根据系统架构而不同.
$p 最后一条 d* (Display Memory)命令打印的值.
$proc 当前进程的地址(就是 EPROCESS 块的地址).
$thread 当前线程的地址(就是 ETHREAD 块的地址).
$peb 当前进程的进程环境块(PEB)的地址.
$teb 当前线程的线程环境块(TEB)的地址.
$tpid 当前线程所在进程的进程 ID(PID).
$tid 当前线程的线程 ID.
$bpnumber 断点 ID 为 number 的地址.
$frame 当前帧索引, 这个和.frame (Set Local Context) 命令常用的 frame number 相同.
$dbgtime 调试器运行计算机的当前时间.
$callret 被.call 命令调用的或者被.fnret /s命令使用的最后函数得到的返回值, $callret 的数据类型就是返回值的数据类型.
$lastclrex 仅托管代码调试: 最近一次遇到的公共语言运行时(CLR)异常对象的地址.
$ptrsize 指针大小.
$pagesize 内存页的大小.
$exp 最后求值的表达式的值
+, -(expr) 一元操作符, 正负号
not(expr) 一元操作符, 取反, 参数为零返回 1, 非零参数返回 0
hi, low(expr) 取高/低 16 位
by, wo, dwo, qwo, poi(expr) 读取指定地址的一个 BYTE, WORD, DWORD, QWORD 和一个指针大小的数据.
$pby, $pwo, $pdwo,
$pqwo, $ppoi(expr)
同上, 只是读取的是物理地址.
(expr)*, /, %, mod(expr) 二元操作符, 乘法, 除法, 取模. (二元操作符的优先级按顺序依次降低)
(expr)+, -(expr) 二元操作符, 加法, 减法
(expr)<<, >>, >>>(expr) 二元操作符, 左移, 右移, 算术右移
(expr)<, >, <=, >=(expr) 二元操作符, 左移, 右移, 算术右移
(expr)&, and(expr) 二元操作符, 按位与. 由于比较运算结果为只有 1 位的 0 或 1, 也可以用来做逻辑与.
(expr)^, xor(expr) 二元操作符, 按位异或
(expr)|, or(expr) 二元操作符, 按位或
$vvalid(addr, len) 函数, 检查地址为 addr, 长度为 len 的内存是否有效. 有效返回 1, 无效返回 0.
$scmp(str1, str2) 函数, 比较字符串, 和 C 语言中的 strcmp 一样返回 -1, 0 , 1.
$sicmp(str1, str2) 函数, 忽略大小写比较.
$spat(str, pattern) 函数, 检查字符串是否和包含通配符的模板匹配.
$iment(addr) 函数, 通过模块基地址返回入口点地址.
$fnsucc(addr,retval, flag) 将 retval 作为位于 addr 处的函数地返回值. 如果返回值是一个成功码, $fnsucc 返回 1,否则返回 0.
如果返回值类型是 BOOL, bool, HANDLE, HRESULT 或者 NTSTATUS, $fnsucc 可以正确理解指定的返回值是否一个成功码. 如果返回值类型是一个指针, 所有 NULL 以外的值都是成功码. 对于其它返回值类型, 根据 Flag 值来定义成功与否, 如果 flag 是 0, 那么一个非零 retval 值表示成功; 如果 flag 是 1,则 retval 值为 0 表示成功.
C++语法: C++ 中的数值默认为十进制, 不受 n 命令的影响.
0xnum, numU, numI, numI64... 数值和 C++ 语言相同
!ModuleName!SymbolName 在 C++ 中, 符号都是有类型的, 使用没有类型对应的符号会产生语法错误.
@RegName C++ 中使用寄存器和伪寄存器时必须加 @, 不能省略
下面是 C++ 表达式支持的运算符, 优先级按下面的顺序从高到低, 可以加小括号改变优先级:
expr // comment 注释, 忽略 // 后面的内容
Class::Member, Class::~Class, ::Name 类成员, 析构函数, 全局
Struct.Field, Pointer->Field 结构成员, 指针结构的成员
Array[Number] 数组成员
Name++, Name-- 自增/减
dynamic_cast<type>(value)
static_cast<type>(value)
reinterpret_cast<type>(value)
const_cast<type>(value)
类型转换
(type)value 类型转换
sizeof value, sizeof(type) 表达式类型大小, 结构类型大小
++value, --value 自增/减
~val, !val, -val, +val 按位取反, 逻辑非, 取负, 取正
&val 取地址
*val 解引用, 取地址内容
Struct.*Pointer, Pointer->*Pointer 成员指针
expr*, /, %expr 乘法, 除法, 取摸
expr+, -expr 加法, 减法
expr<<, >> expr 左移位, 右移位
expr&expr 按位与
expr^expr 按位异或
expr|expr 按位或
expr&&expr 逻辑与
expr||expr 逻辑或
name=, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^=expr 和 C++ 相同
expr ? expr : expr 问号表达式
expr, expr 逗号表达式

C++ 可以使用宏, 宏以 # 开头, 下面是可以使用的宏:
#FIELD_OFFSET(type, field) 字段在结构中的偏移
#RTL_FIELD_SIZE(type, field) 字段的大小
#RTL_CONTAINS_FILED(type, size, field) 给定的大小内是否包含指定的字段
#CONTAINING_RECORD(addr, Struct, Field) 根据字段的地址得到结构的基地址
#RTL_SIZEOF_THROUGH_FIELD(type, filed) 从基址到给定字段位置的大小
#RTL_NUMBER_OF(array) 数组元素的个数

范围 即命令中用到的 range
addr1 从 addr1 开始, 使用默认长度 0x80 的地址范围
addr1 addr2 从 addr1 到 addr2 的地址范围, 包括 addr1, 不包括 addr2
addr1 Llen 从 addr1 到 addr1 + len 的地址范围
addr1 L-len 从 addr1 - len 到 addr1 的地址范围
addr1 L?len 忽略对 len 不能超过 256M 的限制
别名
别名用来自动替换成其他的字符串, 类似于 C++ 的宏替换. 别名分为自动别名, 固定别名, 自定义别名三种. 别名可以在任何命令中使用, 即使被引号括起来, 仍然可以正确展开.
自动别名: 由调试器自动定义的别名
$ntsym 当前机器模式最适合的NT符号的模块名.
$ntnsym 当前系统架构NT符号的最适合的模块. 该别名可能等于ntdll或nt.
$ntwsym WOW64最适合的NT符号模块. 等于 ntdll32 或 Ntdll 的32位版本.
$CurrentDumpFile 调试器上一次加载的dump文件名字.
$CurrentDumpPath 调试器上一次加载的dump文件所在的目录.
$CurrentDumpArchiveFile 调试器上一次加载的dump存档文件(CAB文件)名字.
$CurrentDumpArchivePath 调试器上一次加载的dump存档文件(CAB文件)所在目录.
固定别名: $u0, $u1, ... $u9 他们的值为不包含换行的字符串. 使用 r $.uNum=text 来设置值.
自定义别名: 使用 as/aS/ad/al 等命令来操作. 自定义别名和其他字符之间需要有空格才能被识别, 自定义别名前没有 $ 号.
别名可以用 ${} 来访问, 比如: ${$ntsym} (自动别名), ${$u0} (固定别名), ${myaliase} (自定义别名). ${} 语法:
${name} 替换为别名的值, 如果别名没定义, 保持原样. name 为别名或 .foreach 中的变量值.
${/d:name} 如果别名已定义, 替换成 1, 否则替换成 0
${/f:name} 如果别名已定义, 替换成别名值, 否则替换成空
${/n:name} 如果别名已定义, 替换成别名的名字, 否则保持原样
${/v:name} 总是保持原样

命令

? 显示所有可用的命令和操作符
.hh cmd 在 chm 中打开命令的帮助页面
? expression 使用默认语法对表达式求值
?? expression 使用 C++ 语法对表达式求值
.expr 查看当前默认的表达式求值语法
.expr /q 显示可用的表达式语法
.expr /s C++ 设置当期默认的表达式为 C++ 语法
.expr /s masm 设置当期默认的表达式为 MASM 语法
n 显示 MASM 语法中数值的默认基数
n Radix 设置 MASM 语法中数值的默认基数, 值可以是 8, 10, 16
l+t 开启源代码模式, t 命令会每次执行一行源代码
l-t 开启汇编模式, t 命令每次执行一条汇编语句
l+ls 在命令提示符显示源码行和行号
l-ls 取消源码行和行号的显示
l+o 单步执行代码时隐藏除了源码行和行号外的所有信息, 必须要有 s 选项才行
l-o 取消其他信息的隐藏
as name value 创建名为 name 的自定义别名. name 区分大小写, 不能包含空格或换行, 不能以 al, as, aS, ad 开头. value 不包含前面的空格, 包含后面的空格和换行. 分号, 引号, 空格都会被包含在 value 中.
aS name value 同上, 但是 value 会被分号中断, 如果 value 要包含分号则需添加引号.
as /e name EnvirVar 设置别名值为环境变量 EnvirVar 的值.
as /x name expr 设置别名值为 expr 表达式的 64 位值.
aS /f name filename 设置别名的值为文件的内容.
as /c name command 设置别名的值为命令的输出.每条命令的末尾会包含一个换行. command 可以包含以 ; 分割的多条命令.
as /ma name addr 设置别名值为地址 addr 开始, 0 结尾的 ASCII 字符串.
as /mu name addr 设置别名值为地址 addr 开始, 0 结尾的 UNICODE 字符串.
as /msa name addr 设置别名值为地址 addr 开始, 0 结尾的 ANSI_STRING 结构.
as /msu name addr 设置别名值为地址 addr 开始, 0 结尾的 UNICODE_STRING 结构.
al 列出所有的自定义别名
ad name 删除别名. 可以加 /q 选项在别名不存在时不报错.
ad * 删除所有自定义别名.
$$ text 注释, 从 $$ 开始到分号或换行将被忽略
* text 注释, 从 * 开始到换行将被忽略, 即使其中有分号, 分号后面的命令也不会解析.
.echo test 输出 text, 远程调试时可以作为客户端和服务器的交流工具使用
.cls 清空命令窗口的显示
$<FileName 运行脚本.
$><FileName 运行脚本.
$$< FileName 运行脚本.
$$>< FileName 运行脚本.
$$>a< FileName arg1 arg2 ... 带参数运行脚本.

带两个 $ 号的 $$< 和 $$>< 命令可以在 < 和文件名中加空格, 文件名可以使用双引号括起来. 后面还可以使用 ; 接其它的命令.

只有一个 $ 号的 $< 和 $>< 命令 < 和 文件名中不能包含空格, 文件名不能用双引号括起来. 后面也不可以使用 ; 接其它的命令.

不带 > 号的 $< 和 $$< 命令将脚本中的每一行作为一条命令来执行. 带 > 号的 $>< 和 $$>< 将脚本中的换行替换为分号, 然后当作一条命令块来执行.

$$>a< 命令可以向脚本传递参数. < 和文件名间可以包含空格, 文件名如果包含空格, 则必须加双引号. 分号可以结束参数列表, 开启一个新的命令. 如果提供的参数过多, 多余的将会被忽略; 如果参数不够, 缺少的参数值将会保持原始的 ${$argn} 形式的值.

version 显示调试器和所有已加载的扩展DLL的版本信息以及目标机的操作系统版本.
vertarget 显示目标机的操作系统版本信息, version 的子集
.lastevent 最近一次发生的异常或事件
|| 显示当前正在调试的系统的状态信息
.effmach 显示调试会话所使用的处理器类型, 该类型决定了栈回溯方法, 指针长度, 寄存器集合.
.effmach .|#|x86|amd64|ia64 修改调试会话的处理器类型, . 为目标机器类型, # 为上一次调试事件时处理器类型.
.attach PID 附加到进程
.detach 从进程脱离
q 结束调试会话, 回到静止模式. 远程调试时无效.
qd 从进程脱离, 然后结束调试会话.
qq 结束调试会话, 回到静止模式. 远程调试时结束调试服务器.
.dump /f FileName 生成完整转储文件. 用户模式下最好使用迷你转储, 完整转储并不比迷你转储包含更多信息.
.dump /ma FileName 创建一个包含所有附加选项的迷你转储. 等价于 /mfFhut .
lm 显示模块列表
lm1m 只显示模块名的列表, 方便使用 .foreach 进行迭代
lmvm ModuleName 查看模块的详细信息, 可使用通配符
!lmi ModuleAddr 显示模块的详细信息.
ld ModuleName 强制加载指定模块的符号. 默认为延迟加载
ld * 强制加载所有模块的符号
x symbol 显示符号, 可以使用通配符
x /v/f/t/d symbol 显示符号的详细信息, 可以使用通配符
ln addr 显示与特定地址相关的符号
.sympath 显示搜索符号的路径
.sympath path 设置搜索符号的路径, 多个路径之间用 ; 分开
.sympath+ path 增加搜索符号的路径
.symfix path 等价于 .sympath srv*path*http://msdl.microsoft.com/download/symbols
.symfix+ path 等价于 .sympath+ srv*path*http://msdl.microsoft.com/download/symbols
!sym 显示当前符号加载的提示信息详细程度
!sym noisy 激活详细符号加载提示
!sym quiet 关闭详细符号加载提示
.reload 重新加载所有的模块符号, 该命令只是将模块标记为需要重新加载, 并不会立即加载.
.reload MoudleName 重新加载指定模块的符号, ModuleName 包含扩展名, 可以有通配符.
.reload /f ModuleName 立即重新加载模块的符号, 不使用延迟加载了. 省略模块名则针对所有模块.
.reload /u ModuleName 卸载模块和他的符号, 省略模块名则针对所有模块.
.reload /unl ModuleName 基于已卸载模块列表中的映像信息重新加载符号.
.reload /s ModuleName 加载一个不在目标程序模块列表中的模块.
.reload ModuleName=addr,size 指定模块的基地址和大小, 加载模块的符号.
!chksym ModuleAddr 检查模块和已加载的符号文件是否匹配
!chksym ModuleAddr SymbolFile 检查模块和指定的符号文件是否匹配
.srcpath path1;path2... 设置源文件的查找路径
.srcpath+ path1;path2... 添加源文件的查找路径
.lsrcpath path_list 设置本地的源文件路径, .srcpath 设置的是调试服务上的路径
.lsrcpath+ path_list 添加本地源文件路径
.srcnoisy 输出是否显示源文件加载信息的设置
.srcnoisy 0, 1, 2, 3 设置源文件加载信息的提示. 0: 不显示 1: 显示源文件加载和卸载 2: 显示符号文件加载和卸载 3: 等于 1, 2 同时设置
!handle 显示句柄列表
!handle Handle f 用户模式下显示句柄的详细信息
!gle 返回最后的错误码
!peb 显示当前进程的 PEB 信息, 包含了命令行参数和环境变量等
!teb 显示当前线程的 TEB 信息
.load DllName 加载扩展 DLL, 使用 !DLLName.Cmd 来执行命令时 DLL 会自动加载. 等价于 !DLLName.load
.loadby DllName DllName2 在 DllName2 的路径下查找和加载 DllName.
.unload DllName 卸载扩展 DLL. 等价于 !DLLName.unload.
.extmatch pattern 搜索扩展命令中包含指定模板的命令
!module.help 显示扩展模块的帮助信息
!module.cmd /? 显示扩展命令的帮助信息
r 根据当前的掩码显示寄存器的值, 掩码决定了默认显示哪些寄存器
r. 显示当前指令涉及的寄存器
r reg 显示指定寄存器的值
r reg = expr 修改寄存器的值
r $.uNum = text 设置固定别名的值
r reg:format 按指定的格式显示寄存器的值, f-浮点 d-双精度 b-字节 w-字 d-双字 q-四字 b, w, d, q 前面可加 u 或 i 来表示无符号和有符号.
rM1ff 显示所有寄存器的值, 1ff 为掩码: 2-通用 4-浮点 8-段 10-MMX 20-调试 40-SSE XMM 80-内核控制 100-内核 TSS
rm 显示当前的掩码
rm ? 显示可用的掩码值的含义
rm mask 设置当前默认的掩码
rF 等价于 rM4, 显示浮点寄存器
rX 等价于 rM40, 显示 XMM 寄存器
db, dw, dd, dq range 按字节, 字, 双字, 四字格式来显示内存
df, dD range 按单精度, 双精度来显示内存
da, du range 显示 ASCII, UNICODE 内容
da, du /c width range 指定显示的列宽度, 该选项对其他的 dispaly 命令也有效
dp range 显示指针值
dyb range 二进制值和字节的值
dyd range 二进制值和双字值
dps, dds, dqs range 显示地址对应的符号, 可用于查看虚表. 32 位下 dps = dds, 64 位下 dps = dqs
ddp, dqp, dpp, dda, dqa, dpa, ddu, dqu, dpu range
在内存中进行迭代, 将每一个取出的值都当做一个指针, 用指定的类型来显示这个指针.

d*p 以指针的大小来显示引用的内存, 如果和符号匹配, 也会显示符号

d*a 以 ASCII 字符显示

d*u 以 Unicode 字符显示

dd* 使用 32 位指针

dq* 使用 64 位指针

dp* 根据处理器决定指针大小

dc range 双字值和 ASCII 字符
dg selector 显示段选择符信息, selector 通常就是段寄存器
dt type 显示结构成员信息
dt type addr 显示给定地址上的数据类型
dt type.. addr 同上, 展开 2 级的子结构
dt addr 如果地址是一个已知的符号名, 可以省略类型
/anum 展开数组的 num 个元素, 省略 num 在显示数组的全部元素
/rnum 递归显示子字段的层数. num 只能是 1 到 9. 该选项必须紧靠在地址前面.
/b 展开子结构, 但是不会展开指针
/d 对遍历到的每一个类型使用详细输出
/v 详细输出
/c 紧凑模式
/i 不缩进
/o 不显示偏移值
/t 只列举类型
/p 地址是物理地址, 而不是虚拟地址
/e 当 dt 将枚举当成实例时, 用该选项强制设置枚举类型
/s size 只显示大小为 size 的类型, 该选项包含了 /e 选项.
!address 显示整个地址空间和使用摘要信息
!address -summary 只显示摘要信息
!address addr 显示地址空间中包含 addr 的区域信息.
dv 显示局部变量
dv symbol 显示和符号匹配的变量的值, 符号可以使用通配符
/i 显示变量的类型
/t 显示变量的数据类型
/v 显示变量的内存地址或寄存器位置
/V 同 /v, 并且包含和相应寄存器有关联的局部变量的地址
/a, /A 按地址排序, /a 从小到大, /A 从大到小
/n, /N 按名字排序
/z, /Z 按大小排序
dv /f addr "arg_filter" /f 选项必须放在最后, 查看指定地址处有哪些局部变量和参数, 它不显示参数的值. 后面可以带一个用引号括起来的包含通配符的字符串表示参数过滤模板.
!dh addr 显示 addr 映像头地址的 PE 结构信息.
!drvobj addr 显示 DRIVER_OBJECT 对象信息.
!slist addr type offset 显示单链表, 加上 type 和 offset 后能显示链表结构中的数据.
!exchain 显示当前的异常处理链
/f 显示遍历 CRT 函数获得的信息
/c 显示 try/catch 异常相关信息, 如果存在的话.
/C 显示 try/catch 异常相关信息, 即使不存在.
eb, ew, ed, eq addr value 按字节, 字, 双字, 四字格式来修改内存内容
ef, eD addr value 写入单精度, 双精度的浮点数到内存
ea, eu addr string 在指定的地址输入 ASCII, UNICODE 字符串, 不加 0 终止符.
eza, ezu addr string 在指定的地址输入 ASCII, UNICODE 字符串, 要加 0 终止符.
f addr Llen values 用给定的值循环填充内存区域, 例: f @eax L0x40 0x21 0x22 0x23;
a addr 从指定的地址开始进行汇编, 省略地址则从当前指令指针开始. 开始汇编后输入空结束汇编.
s range pattern 在内存中搜索
c range addr2 比较两段内存区域
!heap 显示进程所使用堆的简明列表
!heap -s 输出堆的统计信息
!heap addr 显示堆的使用信息
!heap num 根据序号显示堆的使用信息, 0 表示所有的堆
-v 校验指定堆的元数据
-a 显示堆的所有信息, 等价于 -h -f -m
-h 分配的内存表
-f 空闲内存表
-m 段条目
-t 标签信息
-T 伪标签信息
-g 全局标签信息
-k 分配内存的栈回溯信息
!heap -E/-D addr/num 启用/禁止指定堆的调用时验证
!heap -e/-d addr/num 启用/禁用指定堆的堆检查
!heap -l 检查堆中的内存泄露
!heap -x addr 搜索包含了指定地址的堆块
!heap -x -v addr 搜索包含指定地址的堆块,并在当前进程的整个虚拟内存空间中搜索指向该堆块的指针
!heap -stat 显示堆使用统计
!heap -srch pattern 在堆中搜索指定的内容
!pool 显示内存池信息
.dvalloc size 在调试器进程空间分配内存
.dvfree addr size 释放 .dvalloc 分配的内存
.readmem filename range 将文件读入被调试进程的内存
.writemem filename range 将被调试进程的内存写入文件
k 显示当前的调用堆栈
kb 显示函数的前三个参数
kp 显示所有参数
kP 分行显示所有参数
kv 显示帧优化信息(FPO)和调用约定
k* n f L k* 包括 k, kb, kp, kP, kv 命令, 不包括 kd 命令. n f L 为可选项, n: 显示帧号码 f: 显示帧距离 L: 隐藏源代码信息.
k* number 指定要显示的栈帧数量, 默认为 .kframes 设置的值.
k* = addr 指定栈回溯的基地址
k* = addr number 指定回溯的基地址和栈帧数量
k* = addr1 addr2 addr3 指定栈帧基地址, 堆栈指针(默认为 esp), 指令指针(默认为 eip)
kd 显示原始堆栈数据, 等价于 dds stack_addr
kd number 显示 number 个原始堆栈数据
.kframes 显示 k 命令默认显示的栈帧数量
.kframes number 设置 k 命令默认显示的栈帧数量
.frame 显示当前的栈帧
.frame /r 设置当前栈帧为当前指令指针位置的栈帧(即 0 号), 并显示局部上下文寄存器和其他信息
.frame FrameNumber 根据栈帧序号(k 命令显示)来设置当前栈帧
.frame /r FrameNumber 设置栈帧, 并显示上下文信息
.frame =addr_base 通过堆栈基指针来设置当前的栈帧
.frame =addr_base number 在指定的基指针上再越过 number 层的栈帧
.frame =base stack ip 指定基地址, 用来确认的栈指针(esp), 用来确认的指令指针(eip)
u 从当前位置反汇编 8 条指令, 连续使用 u 会继续从上次结束的地方往下反汇编
u range 反汇编指定的内存区域
u addr 从指定的地址反汇编 8 条指令. 可以用 . 表示当前指令指针的地址
ub 从当前地址往前反汇编 8 条指令
ub addr Llength 反汇编以 addr 结尾的指定长度的内容. ub 不能使用 addr1 addr2 形式的内存区域.
ub addr 从指定的地址往前反汇编 8 条指令
ur 按16位实模式进行反汇编, 从当前地址反汇编 8 条指令
ur range 按 16 位模式反汇编指定的内存区域
ur addr 按 16 位模式从指定的地址反汇编 8 条指令
uf addr 反汇编地址所在的函数
uf /c addr 只显示 call 指令
uf /i addr 显示指令条数
# "assembly" range 在反汇编代码中搜索
bl 列出所有断点
bd bpid 禁用断点, 断点号用空格分开如 bd 0 2 3; 也可用 bd 0-3 方式指定范围; 还可以用 * 指定所有断点.
be bpid 启用断点
bc bpid 清除断点
bp addr 创建软件断点, 模块重新加载时不会重新计算断点地址.
bu addr 创建未解析断点, 模块卸载后重新加载会重新计算断点地址, 断点的地址是不固定的.
bm symbol 可以在符号中使用通配符, 在所有匹配的符号上下断点
bm /a symbol 即使符号对应在数据段中, 也强制下断点.
bm /d symbol 将断点位置转换为地址, 模块重新加载后不会重新求值来改变断点.
bm /( symbol 可以对同名不同参数的函数设置断点
ba accsize addr 创建硬件断点. acc 为 r, w, e, i(内核) 之一,代表读, 读写, 执行, IO. size 值为 1, 2, 4, 8. acc 为 e 时只能为 1.
bp, bu, ba, bm ... "command" 断点命令后可以接一个字符串命令来关联, 关联到条件命令即可实现条件断点.
t 单步进入
~.t 在当前线程中执行一条语句, 其他线程都被暂停
p 单步跳过
ta addr 按单步进入执行到指定的地址
pa addr 按单步跳过执行到指定的地址
tc, pc 单步进入/跳过, 直到遇到 CALL 指令
th, ph 单步进入/跳过, 直到遇到分支跳转指令
tt, pt 单步进入/跳过, 直到遇到 RET 指令
tct, pct 单步进入/跳过, 直到遇到 CALL 或 RET 指令
g 恢复运行
g addr 执行到指定的地址
gu 执行到当前函数返回
gc 从条件断点恢复执行
gh 从异常恢复执行, 异常已经处理好了
gn 从异常恢复执行, 异常还未处理, 需要继续搜索异常处理器
wt =addr_start addr_end 追踪代码运行过程中调用了哪些函数, 省略 =addr_end 则追踪单条指令, 省略 addr_start 则从当前指令指针开始. 如果从函数的开始位置进行追踪, 则会执行到函数返回为止!
/l Depth 指明追踪的深度, 超过设置深度的将直接运行, 不进行追踪.
/m Module 只追踪指定的模块和模块内的一级调用, 该选项可重复多次, 指明要追踪多个模块.
/i Module 指明要忽略的模块, 有 /m 选项时 /i 无效.
/ni 不显示 /i 忽略的代码的入口
/nc 不显示调用信息
/ns 不显示摘要信息
/nw 不显示警告信息
/oa 显示 call 的实际地址(默认只有符号)
/or 使用默认基数显示被调用函数的返回寄存器
/oR 根据返回类型显示被调用函数的返回寄存器
!htrace -enable 启用操作系统的句柄追踪, 并生成一个快照
!htrace -disable 关闭句柄追踪
!htrace -diff 将当前句柄信息和上一次快照进行对比, 显示仍然打开的句柄的栈回溯
!htrace -snapshot 使用当前的句柄信息建立快照, 作为 !htrace -diff 的初始状态
!htrace 显示进程中所有句柄的栈回溯
!htrace handle 显示指定句柄的栈回溯, 若句柄为 0, 等价于 !htrace 命令
!analyze 自动分析当前的异常信息.
-v 详细输出
-f 即使调试器未检测到异常时也进行异常分析
-hang 对线程挂起(停止响应)的原因进行分析, 需要先切换到挂起的线程
~ 列出所有线程
~N 查看第 N 号线程的信息, N 可以使用通配符 * 表示所有线程
~Nn 挂起第 N 号线程, suspend 计数加 1
~Nm 恢复第 N 号线程, suspend 计数减 1
~Nf 冻结第 N 号线程
~Nu 解冻第 N 号线程
~Ns 切换到第 N 号线程
~~[PID]s 根据 PID 来切换线程
~N cmd 在第 N 号线程中执行 cmd 命令, 其他线程会被暂停. 不是所有命令都支持.
~*cmd 在所有线程中执行 cmd 命令, 不是所有命令都支持.
| 列出所有进程
|N 查看第 N 号进程的信息
|Ns 切换到第 N 号进程
.cxr 重置寄存器上下文
.cxr addr 显示保存在指定地址上的上下文
.cxr /w addr 将当前上下文写入内存地址
!runaway 显示每个线程消耗的用户时间
!runaway 7 显示每个线程消耗的用户时间, 内核时间, 运行时间
sx 列出所有事件和异常
sxr 将所有事件的中断和处理都设置为默认值
sx- -c "cmd1" -c2 "cmd2" event 不改变事件的中断和处理状态, 只修改事件发生时执行的命令.
sxe:event 为事件打开断点, event 可以是数字代码, 短代码名称, 或 * 表示所有事件
sxe ld module 加载指定的模块时中断到调试器
sxd event 为事件关闭断点, 只能关闭第一轮处理
sxn event 禁用第一和第二轮的调试中断, 只输出信息
sxi event 忽略事件, 关闭输出, 第一轮和第二轮异常都会忽略
   sxe, sxd, sxn, sxi 分别对应 Event Filter 对话框 Execution 里面的 Enable, Disable, Output, Ignore 选项. 可以使用如下的可选参数:
-c "cmd1" 调试器收到调试事件时执行的命令, 如果是异常则是第一轮事件, 这个命令会在调试器处理事件之前执行, 一定不能包含 g 命令
-c2 "cmd2" 第二轮异常事件到达调试器是要执行的命令, 不能包含 g 命令
-h 只修改事件的处理行为, 不修改事件的中断行为. sxe 命令为异常已被调试器处理, 其余则为未处理
命令行参数和调试会话中的命令对应关系:
-g sxd ibp 忽略初始断点, 进程启动时不中断.
-G sxd epr 进程结束时不中断
-x sxd av 发生访问违例时不中断
-xe event sxe event
-xd event sxd event
-xi event sxi event
-xn event sxn event
.printf format args... 和 C 语言的 printf 一样. 格式包括 %p-指针, %d %x %u-整数, %ma-ASCII字符串, %mu-UNICODE字符串, %msa, %msu-指定地址处的字符串, %y-指定指针处的符号.
.printf /D format args... 启用 DML 标记, DML 标记包括:
<b>text</b>: 黑体
<i>text</i>: 斜体
<u>text</u>: 下划线
<link cmd=\"command\">text</link>: 点击执行命令的链接
<exec cmd=\"command\">text</exec>: 和 link 一样, 但是不替换当前的输出
<col fg=\"name\" bg=\"name\">text</col>: 指定字符串颜色. name 值为: wfg/wbg-默认; clfg/clbg-高亮; changed-上次改变的值, 默认红色; srcnum, srchar, srcstr, srcid, srckw, srcpair, srccmnt, srcdrct, srcspid, srcannot-源代码颜色; empfg/empbg-强调色, 默认高亮蓝色; subfg/subbg-子元素颜色; normfg/normbg, warnfg/warnbg, errfg/errbg, verbfg/verbbg-各种输出等级的颜色.
.logopen filename 将命令窗口中的输出写入到一个新的日志文件中. /u - 使用 unicode 格式, /t - 文件名中添加进程 id 和时间
.logopen /d 同上, 根据目标进程自动生成文件名
.logappend filename 以追加方式来写日志文件. /u - 使用 unicode 格式
.logclose 关闭打开的日志文件
.logfile 查看当前日志文件状态
.block { commands } 语句块. 在同一行命令中, 别名不会重新计算, 引入语句块可以让所有的别名都重新计算.
.if condition { commands } condition 为一个表达式, 也可以用括号括起来. commands 在一个语句块中, 别名都会重新计算.
.if condition { commands } .else { commands }
.if condition { commands } .elif condition { commands }
.if condition { commands } .elif condition { commands } .else { commands }
j condition 'cmd1' ; 'cmd2' 条件为 TRUE, 执行 cmd1, 否则执行 cmd2. 如果 cmd1 和 cmd2 只有 1 条命令, 可以省略单引号. j 命令后面不能加 ; 后跟其他命令. j 命令常用于实现条件断点.
.catch { commands } 在 commands 运行发生错误时跳出 catch 块继续执行后面的命令, 而不是终止整个脚本.
.leave 只能在 .catch 块中使用, 用于跳出 catch 块, 执行 catch 后面的命令.
.while(condition) { commands } 类似于 C 语言的 while 语句
.do { commands } (condition) 类似于 C 语言的 do while 语句. condition 的括号是可选的.
.for(InitCmd ; condition ; IncrCmds) { commands }
InitCmd 只能是单条命令, IncrCmds 可以是多条命令, 使用多条命令时直接用分号分割, 无需加大括号.
.break 在 .for, .while, .do 循环块中使用, 跳出循环
.continue 在 .for, .while, .do 循环块中使用, 开始下一次循环
cmds1 z (condition); cmds2 先执行一次 cmd1, 判断条件, 如果为 TRUE, 再次执行 cmd1, 循环直到条件为 FALSE, 然后执行 cmd2. 其中 cmd2 是可选的.
.foreach /pS first_skip_number /ps skip_number (name {init_cmds} ) { commands }
/pS first_skip_number 和 /ps skip_number 是可选的, 分别指定开始要跳过的个数和每次循环要跳过的个数. 执行时先运行 init_cmds, 然后将输出中的每一个值传递给 commands 来运行. 在 commands 中使用 ${name} 来引用输入的值.
.foreach /s (name "text") {commands}     同上, 使用指定的字符串 text 代替命令的输出
.foreach /f (name "filename") {commands}     同上, 使用文件内容代替命令的输出
!for_each_frame cmd 为当前线程中的每一帧执行一个命令
!for_each_function 为匹配到的所有函数执行一个命令
!for_each_local 为每一局部变量执行一个命令
!for_each_module 为加载的每一个模块执行一个命令
!error ErrorCode 显示错误码对应的错误信息
!dreg 查看被调试机器的注册表内容
.formats number 显示数字按各种不同的方式解释时的意义
.shell command 运行一条 shell 命令, 并将输出重定向到调试器
.shell -i InFile -o OutF -e ErrF cmd 将输入输出重定向到文件. InFile 可用 - 表示没有输入, 标准输出和错误输出的选项可省略.
.shell -ci "commands" cmd 将调试器命令 commands 的输出作为 shell 命令 cmd 的输入
.shell -x cmd 创建的 shell 进程脱离调试器, 调试会话结束后任可运行
.servers 列出当前调试器启动的调试服务器
.server protocl:protocl_options 启动调试服务器
.endsrc server_id 停止调试服务器
.client 列出当前连接的客户端
.remote_exit 退出当前的调试客户端
.send_file source_path dest_path 将文件从智能客户端发送到智能服务器上
.send_file -s dest_path 复制所有已加载的符号文件到智能服务器上
-f 强制覆盖文件

内核命令

lm 查看加载的驱动列表
!process 0 0 列出所有运行的进程
.process /r /p EPROCESS_ADDR 切换到进程的上下文
!for_each_process 为每一个进程执行一个命令
!for_each_thread 为每一个线程执行一个命令
.breakin 从用户态调试状态切换到内核态调试, 在从内核调试器控制用户模式调试器时使用.
.sleep milliseconds 暂停用户模式调试器, 只能在从内核调试器控制用户模式调试器时使用.
.thread addr 设置当前寄存器上下文为指定的线程, 省略地址则默认为当前线程
.thread /p /r addr 重新加载线程所在进程的用户模式符号
.trap addr 显示陷阱帧寄存器状态,并设置寄存器上下文
!ready 显示系统中处于就绪状态的线程信息
!object addr 显示系统对象的信息

杂项

工作空间保存位置: HKEY_CURRENT_USER\Software\Microsoft\WinDBG\Workspaces
符号服务器环境变量: _NT_SYMBOL_PATH=SRV*D:\NtSymbols*http://msdl.microsoft.com/download/symbols
预先下载符号表: symchk /oe /op /ov /r C:\Windows /s SRV*D:\NtSymbols*http://msdl.microsoft.com/download/symbols

应用

1. 远程调试
1. remote.exe (Windows 自带)
服务端: remote /S cmd.exe UniqueName
客户端: remote /C ServerName UniqueName
2. 调试服务器
服务端: debugger -server tcp:port=port other_debugger_argument
调试过程中启动服务器: .server tcp:port=port
客户端: debugger -remote tcp:server=ip,port=port
其中 debugger 可以是 WinDbg, cdb, kd 等等. 服务器和客户端可以使用不同的调试器.
3. 反向连接的调试服务器
服务端: debugger -server tcp:clicon=ip,port=port other_debugger_argument
客户端: debugger -remote tcp:clicon=ip,port=port
4. 智能服务器
服务端: dbgsrv/kdsrv -t tcp:port=port
客户端: debugger -premote tcp:server=ip,port=port
dbgsrv 也被称为进程服务器, kdsrv 也被称为内核服务器. 只有智能服务器的符号是从客户端加载的, 其余方式都会从服务端加载符号文件.
5. 反向连接的智能服务器
服务端: dbgsrv/kdsrv -t tcp:clicon=ip,port=port
客户端: debugger -premote tcp:clicon=ip,port=port
6. 转发服务器
服务端可以是调试服务器, 进程服务器或内核服务器, 启动端口为 port1.
转发器: dbengprx -p -c tcp:server=server_ip,port=port1 -s tcp:port=port2
客户端: debugger -remote/-premote tcp:server=proxy_ip,port=port2
2. 在 GetLastError 返回指定错误的时候中断到调试器
> ep ntdll!g_dwLastErrorToBreakOn errcode
3. 显示进程连接的网络
x64> bp WS2_32!connect ".printf \"Socket %d Connect %d.%d.%d.%d:%d\", @rcx, by(rdx+4), by(rdx+5), by(rdx+6), by(rdx+7), by(rdx+2)*100+by(rdx+3); .echo; gc; "
x86> bp WS2_32!connect ".printf \"Socket %d Connect %d.%d.%d.%d:%d\", poi(esp+4), by(poi(esp+8)+4), by(poi(esp+8)+5), by(poi(esp+8)+6), by(poi(esp+8)+7), by(poi(esp+8)+2)*100+by(poi(esp+8)+3); .echo; gc; "
4. 查看进程的堆数据
> dp @@(@$peb->ProcessHeaps) * 查看进程的堆, 每一个指针都是 _HEAP 结构, 也可以使用 !heap 命令查看
> dt _HEAP 堆指针 * 显示堆结构, 其中 VirtualAllocdBlocks 为直接使用虚拟内存分配的大内存; SegmentList 为一组 _HEAP_SEGMENT 结构, 包含了活跃的堆块; NonDedicatedListLength 为空闲列表中第 0 项的个数; FreeLists 空闲堆块列表; FrontEndHeap 前端分配器;

资源

https://code.google.com/p/narly/ 列出 SAFESEH, GS, DEP 等信息的扩展
SOS (Window WDK) 托管代码调试扩展
http://virtualkd.sysprogs.org/ 这个扩展可以提高 VMWare/VirtualBox 内核调试的效率
windbg.info 大量命令参考和用户论坛
kdext.com 汇编语法高亮扩展和 UI 增强扩展
www.laboskopia.com/download/SysecLabs-Windbg-Script.zip 帮助查看内核的脚本
http://msecdbg.codeplex.com 自动崩溃分析和安全评估的扩展
http://github.com/quarkslab/qb-sync 同步 IDA 反汇编和 Windbg 图形视图的扩展
http://pykd.codeplex.com Python 扩展

▲评论

X 正在回复:
姓 名: 留下更多信息
性 别:
邮 件:
主 页:
Q Q:
来 自:
职 业:
评 论:


Valid HTML 4.01 Strict Valid CSS!
Copyleft.A!die Software Studio.ADSS
Power by webmaster@adintr.com