Windows软件调试初探-WinDBG命令入门

碎碎念

标准命令有:

  • 控制调试目标执行,包括回复运行g、跟踪执行t、单步执行p、追踪监视wt
  • 观察和修改通用寄存器r,读写MSRrdmsr/wrmsr,设置寄存器显示掩码rm
  • 读写I/O端口ib/iw/idob/ow/od
  • 观察、编辑和搜索内存数据的d系列、e系列、s系列。观察栈k系列。
  • 设置和维护断点bp/ba,管理断点bl/bc/bd/be
  • 显示和控制线程~,显示进程|
  • 评估表达式?,评估C++表达式??
  • 汇编与反汇编a/u
  • 显示段选择子dg
  • 执行命令文件$
  • 设置调试事件处理方式sx系列,启用/禁止静默模式sq,设置内核选项so,设置符号后缀ss
  • 显示调试器和调试目标版本version,显示调试目标所在系统信息vertarget
  • 检查符号x
  • 控制和显示源程序ls系列。
  • 加载调试符号ld,搜索相邻符号ln,显示模块列表lm
  • 结束调试会话q,结束远程调试qq,结束调试会话并分离调试目标qd
  • 标准命令帮助?

元命令以点开始,有:

  • 显示和设置调试会话的调试器选项,如符号选项.symopt、符号路径.sympath/.symfix、源程序文件.srcpath/.srcnoise/.srcfix、扩展命令模块路径.extpath、匹配扩展命令.extmatch、可执行文件.exepath、反汇编选项.asm、控制表达式评估其.expr等。
  • 控制调试会话或调试目标,如重新开始调试会话.restart、放弃用户态调试目标进程.abandon、创建新进程.create、附加到存在进程.attach、打开转储文件.opendump,分离调试目标.detach、终止进程.kill等。
  • 管理扩展命令模块,如加载模块.load、卸载.unload/.unloadall、显示已加载模块.chain等。
  • 管理调试器日志文件,如显示信息.logfile、打开.logopen、追加.logappend、关闭.logclose
  • 远程调试,如启动服务.remote、启动调试引擎服务器.server、列出可用服务器.servers、向远程服务器发送文件.send_file、结束远程进程服务器.endpsrv、结束引擎服务器.endsrv
  • 控制调试器,如调试器睡眠.sleep、唤醒调试器.wake、启动另一个调试器来调试当前调试器.dbgdbg
  • 编写命令程序,如.if/.else/.elsif/.foreach/.do/.while/.continue/.catch/.break/.continue/.leave/.printf/.block等。
  • 显示或转储调试目标数据,如产生转储文件.dump、原始内存数据写文件.writemem、显示调试会话时间.time、显示线程时间.ttime、显示任务列表.tlist、以不同格式显示数字.formats
  • 元命令帮助.help

扩展命令结构!扩展模块名.扩展命令名 参数。加载扩展模块用命令.load 扩展模块.loadby 扩展模块 已加载程序模块,用.chain列出当前加载的所有扩展模块,用.unload/.unloadall卸载扩展模块,用!扩展模块.help显示某扩展模块的扩展命令。

对于用户态目标,命令提示符格式为“系统序号:进程序号:线程序号”,所有序号从0开始。对于双机内核调试的内核态目标或内核转储文件目标,为“系统序号:处理器序号:kd”。对于本地内核态调试,为“系统序号:处理器序号:lkd”。

直接按回车则重复上一条命令,用Ctrl+Alt+V开启详细输出模式。大部分命令不区分大小写,有些命令的选项区分大小写。默认使用十六进制,可用0x/h、0n、0y来指定十六进制、十进制、二进制等。

串行执行多个命令用分号隔开,注释用$$时到分号结束,用*时整行都被当作注释。

伪寄存器

伪寄存器PR,解析命令时WinDBG调试引擎自动展开:

伪寄存器 描述
$ea 上一条指令中有效地址
$ea2 上一条指令中第二个有效地址
$exp 表达式评估器评估的上一条表达式
$ra 当前函数返回地址,如g @$ragu效果相同
$ip 指令指针寄存器
$eventip 当前调试事件发生时的指令指针
$previp 上一事件的指令指针
$relip 与当前事件关联的指令指针
$scopeip 当前上下文指令指针
$exentry 当前进程入口地址
$retreg 首要函数返回值寄存器
$retreg64 同上x64
$csp 帧指针
$p 上一个内存显示d系列命令所打印的第一个值
$proc 当前进程PROCESS结构地址
$thread 当前进程ETHREAD结构地址
$peb 当前进程PEB地址
$teb 当前线程TEB地址
$tpid 拥有当前线程的PID
$tid 当前线程ID
$bpx x号断点地址
$frame 当前栈帧序号
$dbgtime 当前时间
$callret .call命令调用的上一个函数的返回值,或用.fnret命令设置的返回值
$ptrsize 调试目标所在系统的指针类型宽度
$pagesize 调试目标所在系统的内存页字节数

使用方法:

1
2
3
4
5
6
7
8
0:000> ln @$exentry *搜索距离指定地址最近的符号
Browse module
Set bu breakpoint

(00007ff7`f6ab127b) FreCheck!ILT+630(mainCRTStartup) | (00007ff7`f6ab1280) FreCheck!ILT+635(__scrt_is_managed_app)
Exact matches:
0:000> ? @$pagesize
Evaluate expression: 4096 = 00000000`00001000

别名

WinDBG自动定义的别名有:

别名 含义
$ntnsym NT内核或NTDLL符号名
$ntwsym 64位系统上调试32位目标时NT系统DLL符号名
$ntsym 与当前调试目标的机器模式匹配的NT模块名称
$CurrentDumpFile 转储文件名称
$CurrentDumpPath 转储文件路径
$CurrentDumpArchiveFile 最近加载的CAB文件名称
$CurrentDumpArchivePath 最近加载的CAB文件路径

例如:

1
2
0:000> .echo $ntnsym
ntdll

上下文

1
2
3
4
5
6
7
8
9
10
!session
!session -s 1 *设置当前会话上下文
.process 83f7fc78 *设为默认进程
.context *显示当前页目录基地址
.thread
.thread 84018d78
r
kv
.frame *观察当前局部上下文
dv *显示该函数的参数和局部变量

符号

1
2
3
4
5
lm v *详细模块列表
x ntdll!dbg*
x dbgee!arg*
x *!_crtheap
x /v dbgee!arg* *显示符号类型大小

未完待续。