我假设你已经掌握Ollydbg的使用,并且希望用WinDbg进行内核级的调试。这篇教程将会以Ollydbg为线索,帮助你尽快掌握WinDbg的使用,并简单介绍它的一些特性。我把这篇文章定位为Ollydbg到WinDbg内核调试之间的过渡。
Ollydbg通过各种窗口提供额外的信息,而WinDbg则是提供各种命令以让你了解相关信息。下面来简单介绍一下这些指令:
.prompt_allow 设置单步过程中,是否显示一些附带信息,通过+/-控制。
dis 反汇编当前指令
ea 当前指令地址
reg 寄存器
src 源代码行中位置
sym 符号
例如关闭所有单步过程中的显示,可以输入.prompt_allow –dis –ea –reg –src -sym
特别地,你可以通过rm指令来设置显示哪些寄存器,你可以通过rm ?来显示可以设置的值。
1 32位寄存器
2 64位寄存器,如果支持
4 浮点寄存器
8 段寄存器
10 MMX寄存器
20 Debug 寄存器,如果在内核模式,还能显示CR类的寄存器
40 SSE XMM 寄存器
如果需要同时显示几类寄存器,只要把对应的数字加起来就可以了,例如你希望同时显示MMX寄存器和SSE XMM寄存器,你可以输入rm 50。显示效果如下:
mm0=0007000077f89964 mm1=000706880134f708
mm2=ffffffff77f899a0 mm3=0009bbd000070b38
mm4=77f8996400000000 mm5=0134f74c00000000
mm6=77f899a077f98191 mm7=00070b3877fcc882
xmm0=1.00797e+034 8.40779e-045 2.8026e-045 8.7191e-040
xmm1=8.68054e-040 8.68099e-040 1.00797e+034 1.02606e+034
xmm2=3.32387e-038 0 0 8.72056e-040
xmm3=3.32387e-038 -1.#QNAN 6.42848e-040 1.01212e+034
xmm4=8.7191e-040 8.7208e-040 8.40779e-045 1.00798e+034
xmm5=3.50325e-044 6.42848e-040 8.71989e-040 3.32383e-038
xmm6=1.94131e+034 3.32388e-038 -1.#QNAN 1.00838e+034
xmm7=3.32406e-038 0 3.32384e-038 6.42848e-040
ntdll!DbgBreakPoint+0x1:
77fa144c c3 ret
.dml_start 以DML的方式显示一些自定义功能,默认情况下会显示各类命令帮助已经关于调试进程的一些基本信息。DML命令在命令浏览窗口将会得到更好的支持。你能使用CTRL+N打开该窗口并使用此指令。
lm /D 以DML的方式显示当前加载模块的详细信息。
!teb 显示TEB的主要信息
!peb 显示PEB的主要信息
!handle 显示handle信息
2. 一般调试流程
2.1 调试目标
CTRL+E相当于Ollydbg的菜单“文件=>打开”,WinDbg除了能设启动参数之外,还能设起始文件夹,还有一个调试子进程的额外选项。
F6相当于Ollydbg的“文件=>附加”
CTRL+K 内核调试模式。分4种模式,分别是COM,1394,USB2.0和本地调试,头3种都是双机调试,最后一种调试需要XP以上版本。
Ollydbg有3个入口点可以选择,而WinDbg只能停在系统断点。要使WinDbg停在EP,可以使用g @$exentry。
2.2 调试功能
2.2.1 基本调试功能
F11或者F8 步入
F10 步过
SHIFT+F11 步出(注:这个功能最好少用,尤其在TLSCALLBACK中,100%出错)
F5 运行
CTRL+SHIFT+F5 重新运行
SHIFT+F5 关闭
CTRL+BREAK 停止
Ollydbg的两个功能,执行到返回和执行到用户代码,WinDbg没有。我用两个SCRIPT来实现它,你将能在附件中找到,分别为gr和guc,把他们复制到WinDbg的安装目录下,可以通过输入“$><gr”来使用执行到返回功能,输入“$><guc”则对应执行到用户代码。假如你把文件复制到别的地方,那么你需要输入完整的文件路径。
由于执行到返回的SCRIPT已经在我的另一篇教程中提到,现在我们来看看guc的代码,简单了解一下WinDbg的SCRIPT功能。
r @$t0=@$peb+8
r @$t0=poi(@$t0) ;$ 通过PEB取得MZ头,程序上界
r @$t1=@$t0+3c
r @$t1=poi(@$t1)+@$t0 ;$ 取得PE头
r @$t1=@$t1+50
r @$t1=poi(@$t1)+@$t0 ;$ 取得程序在内存中的下界
.while(@$ip>@$t1 or @$ip<@$t0){ ;$ 超过下界或者超过上界进入循环
.if(@$ip<@$t1 and @$ip>@$t0){ ;$ 在程序范围之内则跳出循环
.break
}
p ;$ 单步步过
}
注意:这个SCRIPT并没有判断是否处于系统断点,假如你在进入程序区域之前使用这个SCRIPT,效果则等同运行。如果永远都不会进入用户区域,例如你在调试ExitProcess,那么将会导致死循环。