windbg中文文档:
windbg官网:
b断点命令:
1.bl
列出当前所有中断
2.bp+地址/bp+MyModel!Function
在某个地址下断点,windbg不会把bp断点保存在工作空间中
3.bu+MyModel!Function
针对某个符号下断点,在代码修改后,该断点可以随着函数地址改变而更新,而且bu断点会保存在windbg工作空间中,下次启动windbg会自动设置上去
4.bm+MyModel!Function
针对符号下断点,它支持匹配表达式
例如bm MyModel!sb*CreateFile、bm MyModel!Dispatch*
5.ba access size 地址
针对内存访问下硬件断点(内存断点),access可以为e(执行)、r(读)、w(写),size以字节为单位,可以是1/2/4,在64为机器上还可以是8
access和size之间不能有空格,如果access是e(执行),那么size必须是1
例如ba w4 0x804ef129,一旦在该地址执行写操作,那么系统就会停下来
查看当前堆栈:
1.kv
提示FPO信息,ChildEBP、retaddr、前三个参数
2.kb
ChildEBP、retaddr、前三个参数
3.kp
全参数(需要所有符号)
dt查看数据结构,如果是查询nt模块中的数据结构,nt模块名可以省略
1.dt _EPROCESS
显示EPROCESS的数据结构 _EPROCESS中的DebugPort:调试程序与被调试程序之间的桥梁,将它设为0,可以进行反调试,有的反调试
_EPROCESS中的Token:进程的权限,可以通过修改该值来获得权限
_EPROCESS中的SectionObject:可以通过该值获得全路径
_EPROCESS中的ImageFileName:进程名称
2.dt _EPROCESS -r
显示EPROCESS包含子项的详细数据结构
3.dt nt!_P*
显示nt模块中所有以_P为开头的结构体的名称
4.x nt!zw*
显示nt模块中所有以Zw为开头的函数名
5.dt _DRIVER_OBJECT 0x87c07bc0
显示地址0x87c07bc0开始的结构体_DRIVER_OBJECT的成员值
6.dt _DRIVER_OBJECT DriverName 0x87c07bc0
显示地址0x87c07bc0开始的结构体_DRIVER_OBJECT的成员值DriverName的值
7.自己在帮助文档中查看dt命令的具体使用
d命令打印值:
1.
db 以一个字节显示值和ASSCII字符
dw 显示2字节的值
dd 显示4字节的值
dq 显示8字节的值
dp 显示指针长度的值(32等同于dd,64等同于dq)
dD 显示double(8字节)的值(内核中不存在浮点数)
df 显示float(4字节)的值(内核中不存在浮点数)
2.
da 显示ascii值
du 显示unicode值
ds 显示ANSI_STRING值
dS 显示UNICODE_STRING值
3.
db 0x87c07bc0 La8(十六进制):显示地址0x87c07bc0处开始的a8个单字节的数据
dw 0x87c07bc0 L2:显示地址0x87c07bc0处开始的2个双字节的数据
4.
dpp 以DWORD或者QWORD显示地址处的指针所指向的值
dpu 以UNICODE显示地址处的指针所指向的UNICODE字符串
dpa 以ASSCII显示地址处的指针所指向的ASSCII字符串
(指针长度随操作系统而定,32位系统则指针为32位,64位系统则指针为64位)
5.
dpu 0x86c6e030+0x1c+0x4 L1:显示地址0x86c6e030+0x1c+0x4开始的指针所指向的UNICODE字符串
e命令修改内存:
eb/ed/eq address
ea address ASCII字符串
eu address UNICODE字符串
获取进程信息(内核调试使用):
!process 0x79c 获取进程号为0x79c的进程信息
!process -1 0 获取当前进程的简略信息
!process 0 0 SoftMgr.exe 获取进程名为SoftMgr.exe的信息
!process 0 0 查看所有进程信息,包括进程块地址
!process 0 7 查看所有进程的详细信息
!process EPROCESS地址 查看进程信息
!process EPROCESS地址 0 获取进程简略信息
!process EPROCESS地址 7 查看进程详细信息
例子:
!process 852fbd40 7:显示进程块地址为852fbd40的进程的详细信息,包括所有线程和线程栈
切换进程/线程(内核调试使用):
.process /p EPROCESS地址:进入进程上下文
.thread ETHREAD地址:进入线程上下文
将windbg中命令的输出保存到文本文件中:
1. .logopen d:/dump.txt 将显示结果放到文件中
2. 运行你想要输出到文本中的命令
3. .logclose d:/dump.txt 关闭文件
4. .logfile 查看是否还有打开的日志记录
在查看ssdt表和shadowssdt表之前一定要加载好符号
查看ssdt和shadowssdt:
1.x nt!kes*des*table*:得到ssdt和shadowssdt的地址
2.dd 地址:查看表数据
3.dds 地址:查看符号表
u 地址:进行反汇编
ub 地址:倒着反汇编
uf 函数地址:对nt模块的函数进行反汇编
uf 模块名!函数地址:反汇编函数
uf /i 函数地址/模块名!函数地址:反汇编并显示共有多少条指令
例如:
u IofCallDriver L5:显示五行的IofCallDriver的反汇编代码
uf /i nt!PspProcessOpen
反调试方法:
1._EPROCESS中的DebugPort-->清零可以反调试
反反调试:ba w4 debugport_addr找到篡改DebugPort的程序
2.KdDisableDebugger-->在内核层检测调试器是否存在
反反调试:bp KdDisableDebugger;eb xxxx修改关键代码让它返回不存在调试器
3.IsDebuggerPresent和CheckRemoteDebuggerPresent-->在用户层检测调试器是否存在
4.Hook一些调试时使用的函数
5.花指令:无用指令或者干扰指令
蓝屏分析:
1.打开虚拟机的dump功能:
右键“我的电脑”->属性->高级系统设置->启动和故障恢复 设置->写入调试信息 核心内存转储->确定->确定->退出
2.这时,如果电脑蓝屏,会转储核心内存为C:\Windows\MEMORY.DMP文件,在蓝屏的最后一行显示转储进度(Dumping physical memory to disk: 80),并在重启后可以发现该文件。
如果sys文件在系统开机时启动,为防止蓝屏,可以开机时,按f8,进入安全模式,然后查看C:\Windows\MEMORY.DMP文件
3.将dmp文件拷贝到实机
4.打开Windbg,菜单栏File->Open Crash Dump...,选择dmp文件
5.设置Windbg的符号链接
6.在windbg命令行中输入!analyze -v,即可分析出问题的原因
7.在windbg命令行中输入.open -a 自己的引发错误的模块名!引发错误的函数+偏移,即可定位到引起错误的源代码位置
例如:.open -a MyModel!OperUnicodeStr+0x2a .open -a DriverToDump!DriverEntry+2b
8.kv/kp/kb
R0与R3联调:
1.!process 0 0获取用户空间进程块地址或者!process 0 0 SoftMgr.exe就可以直接得到进程SoftMgr.exe的信息
2.输入.process /p + EProcesss地址 切换到应用程序的地址空间
3.如果是x64的系统上调试x86的程序,则输入.effmach x86切换到x86模式,也可以.effmach amd64切换回x64模式,直接输入.effmach可以查看当前模式
4.在符号链接中添加user程序的PDB文件,但不要点击重新加载选择框,然后在命令行输入.reload /f /user
就算没有符号表,也要输入.reload来加载和同步当前用户层内容
5.在windbg中打开应用层源文件
6.下应用层断点,打开R3程序的源代码,按F9下断点
查看局部变量和全局变量:(前提:加载了对应的符号表)
dv 查看局部变量
dv /i 查看局部变量, 并显示符号的类型和参数类型
dv /V 查看局部变量, 并显示变量的存储位置
dv /V VariableName 指定需要查看的变量的名字
dv MyModule!g* dv命令可以带有通配符, 来查看具有某命名模式的变量
查看符号
x MyModule!GlobalVariableName 查看模块MyModule中名为GlobalVariableName的全局变量
x *! // 列出所有模块对应的符号信息
x ConsoleTest!* // 列出ConsoleTest模块中的所有符号
x ConsoleTest!add* // 列出ConsoleTest模块中的所有add开头的符号
x /t /v ConsoleTest!* // 带数据类型、符号类型和大小信息,列出ConsoleTest模块中的所有符号
x kernel32!*LoadLib* // 列出kernel32模块中所有含LoadLib字样的符号
查看模块
1.lm 列出所有模块(加载和未加载)对应的符号信息
2.lmf 显示所有模块的信息(包含模块的文件路径)
2.lm m *nt* 显示名字中包含nt的模块的信息
3.lmvm *nt* 显示名字中包含nt的模块的详细信息(注意exe、dll等都不要带后缀名)
4.!lmi uxtheme 想要了解uxtheme.dll的详细调试文件(PDB)信息
5.lmv 列出所有模块(加载和未加载)对应的符号信息
t指令
1.tc 继续运行直到call指令停止
2.tt 继续运行直到ret指令停止
3.tct 继续运行直到call或者ret指令停止
4.th 继续运行直到跳转指令停止,跳转指令包括call ret 有条件无条件跳转等
5.ta+地址 继续运行程序直到指定地址到达为止
6.t 单步步入
pt 执行到当前函数的ret指令停止,相当于单步按t,直到ret指令停止
gu 继续执行直到当前函数完成
无符号驱动模块360FsFlt的入口点中断:
1.sxe ld:360FsFlt
在加载360FsFlt模块的时候会在DebugService函数里中断下来
2.lm m 360FsFlt
查看360FsFlt的起始地址
3.!dh -a 360FsFlt的起始地址
解析360FsFlt的PE文件,在address of entry point处会显示入口点地址偏移(该入口点并不是DriverEntry,是导出的start)
如果分析失败,则通过ida调整基地址后获取需要下断点的地址并直接用bp下断即可
4.bp 360FsFlt的起始地址+入口点地址偏移
在360FsFlt的入口点处下断点
给进程SoftManagerProxy.exe或者进程模块leakrepair.dll在载入内存时下断点
1.先设置一个异常
!gflag +ksl
sxe ld:SoftManagerProxy.exe
sxe ld:leakrepair.dll
2.接着输入g继续执行,直到进程启动时断下,再输入lm查看想要调试的模块的起始地址
3.断下之后由于peb还没建立,所以这段内存无法访问,但是通过ida找到代码入口点地址然后用bp下断
4.继续执行就会在代码入口点处断下
x64切换到x86模式下:
.load wow64exts
!sw
sx指令:
1.sx
显示所有支持的异常中断
2.加载内核模块时下断
sxe ld:MyDriver 驱动MyDriver加载前下断
sxn ld:MyDriver 驱动MyDriver加载时不会中断,但会显示一条相关信息
sxi ld:MyDriver 取消对MyDriver加载时的设置
3.卸载驱动时下断
sxe ud:MyDriver 驱动MyDriver卸载前下断
sxn ud:MyDriver 驱动MyDriver卸载时不会中断,但会显示一条相关信息
sxi ud:MyDriver 取消对MyDriver卸载时的设置
4.
sxr 复原异常中断设置
!dml_proc
查找字符串:
s -[type] range pattern
(1)type可以是b w d a u,默认为b
(2)range表示范围,有两种方式表示,一是起始地址+空格+终止地址,二是起始地址+空格+L+长度,如果搜索长度超过256MB,则用起始地址+空格+L?+长度
(3)pattern用于指定要搜索的内容,可以用空格分隔要搜索的数值
1.s-sa
s-sa 00000000`01360000 L00000000`0007b000 显示00000000`01360000开始的长度为00000000`0007b000的内存范围内所有的ASCII字符串
s-sa 00000000`01360000 00000000`013db000 显示00000000`01360000为起点00000000`0007b000为终点的内存范围内所有的ASCII字符串
2.s-su
s-su 00000000`01360000 L00000000`0007b000 显示00000000`01360000开始的长度为00000000`0007b000的内存范围内所有的UNICODE字符串
s-su 00000000`01360000 00000000`013db000 显示00000000`01360000为起点00000000`0007b000为终点的内存范围内所有的UNICODE字符串
3.s -a
s -a 00000000`01360000 L00000000`0007b000 "Windows" 在00000000`01360000开始的长度为00000000`0007b000的内存范围内搜索ASCII字符串"Windows"的地址
s -a 00000000`01360000 00000000`013db000 "Windows" 显示00000000`01360000为起点00000000`0007b000为终点的内存范围内搜索ASCII字符串"Windows"的地址
4.s -u
s -u 00000000`01360000 L00000000`0007b000 "Windows" 在00000000`01360000开始的长度为00000000`0007b000的内存范围内搜索UNICODE字符串"Windows"的地址
s -u 00000000`01360000 00000000`013db000 "Windows" 显示00000000`01360000为起点00000000`0007b000为终点的内存范围内搜索UNICODE字符串"Windows"的地址
5.以下三个是同一个意思
s 0012ff40 L20 'H' 'e' 'l' 'l' 'o'
s 0012ff40 L20 48 65 6c 6c 6f
s -a 0012ff40 L20 "Hello"
6.
s -a 0x0 L?0x7fffffff mytest 在目标空间为2G的user mode内存空间中搜索ASCII字符串"mytest"
!address [Address] 显示指定地址的内存属性
执行/跟踪到指定地址:
1.
ta [r] [= StartAddress] StopAddress
从StartAddress开始执行,到StopAddress为止执行过的指令就像单步按t一样显示出来,如果是tar则不显示寄存器值
例如:
ta 0x401890
从当前地址开始执行,执行到0x401890之前的所有指令都打印出来,并显示寄存器值
tr =0x00401124 0x401890
立刻从地址0x00401124开始执行,到0x401890地址期间执行过的所有指令都会打印出来,并且不显示寄存器值
2.
pa [r] [= StartAddress] StopAddress ["Command"]
从StartAddress开始执行,到StopAddress为止执行过的指令就像单步按p一样显示出来,如果是par则不显示寄存器值
例如:
pa 0x401890
从当前地址开始执行,执行到0x401890之前的所有指令都打印出来
par =0x00401124 0x401890
立刻从地址0x00401124开始执行,到0x401890地址期间执行过的所有指令都会打印出来,并且不显示寄存器值
最好的使用方法就是:
在StartAddress和StopAddress处都下断点,StartAddress处断下时,执行par StopAddress,执行完后,StopAddress处断下
如何将文件内容读取到调试器内存/从调试器内存写入文件?
注意这里的读写没有pe映射之类的操作,而是二进制读写
命令:.readmem 文件路径 加载基址 l长度 将文件内容拷贝到调试目标内存
命令:.writemem 文件路径 加载基址 l长度 从调试目标内存拷贝到文件
例:
0:000> .writemem 1234.bin 00000000`76eb0000 l0x20000
Writing 20000 bytes................................................................
显示指定进程块fffffa8003cdeb00的安全描述符信息
(1)dt _OBJECT_HEADER SecurityDescriptor fffffa8003cdeb00
显示安全描述符地址0xfffff8a00000463b
(2)!sd 0xfffff8a000004630
显示安全描述符详细信息
显示进程访问令牌
(1)!process 0 1 进程名
显示出Token的地址
(2)!token Token地址
显示Token详细信息
!exchain
显示当前的异常处理程序链
从指定地址开始输入汇编代码
a 地址
之后会出现Input>的符号,就可以输入汇编代码了,输入回车则退出
查看内存页的属性:
(1)用户模式
!address Address
!address -summary
(2)内核模式
!address Address
!address
显示所有KMDF驱动:
!wdfkd.wdfldr
!gle 查看LastError值
!gle -all 打印所有线程的最近的错误信息
!error 897 显示错误码为897的详细描述信息
!locks 显示死锁
!cs 列出CriticalSection的详细信息
!idt 查看中断向量表内容
ld kernel32 加载kernel32.dll的符号
.call
符号加载与查看
除了使用ld和.reload命令直接加载符号文件,某些使用符号的命令也可以触发调试器来加载符号,如:栈回溯命令(k*)和反汇编命令(u)等。
值得说明的是,windbg缺省使用的是懒惰式符号加载策略,当它收到模块加载事件时,它通常是不会加载符号的,符号状态显示为deferred(延迟加载)。
.symopt // 显示当前所有符号选项
.symopt+ flags // 添加符号选项
.symopt- flags // 删除符号选项
!sym noisy // 激活详细符号加载(noisy symbol loading)显示
!sym quiet // 禁止详细符号加载显示
ld * // 为所有模块加载符号
ld kernel32 // 加载kernel32.dll的符号
ld kernel32 /f c:\kernel32.pdb // 加载kernel32.dll的符号kernel32.pdb
.reload /n // 加载内核层符号
.reload /u // 加载用户层符号
.reload // 为所有已加载模块载入符号信息
.reload /i // 重新加载不匹配符号的模块【dmp文件没有对应的pdb时使用】
.reload /i TGame.exe // 重新加载不匹配符号的TGame.exe
.reload /f /v // f:强制立即模式(不允许延迟载入) v:详细模式
.reload /f @"c:\windows\System32\verifier.dll" // 为指定模块加载符号信息
.reload /f TGame.exe // 为TGame.exe加载符号信息
.chain // 显示已经加载进来的模块
lm // 列出所有模块(加载和未加载)对应的符号信息
lmv // 列出所有模块(加载和未加载)对应的符号信息
lmvm ntdll // 查看ntdll.dll的详细信息(注意exe、dll等都不要带后缀名)
查看调试进程
| // 列出调试进程
|* // 列出调试进程
|N // 参看序数为N的调试进程
|Ns // 切换序数为N的进程为当前调试进程
查看线程
~ // 列出线程
~* // 所有线程
~* k // 所有线程堆栈信息
~* r // 所有线程寄存器信息
~. // 查看当前线程
~0s // 查看主线程
~# // 查看导致当前事件或异常的线程
~N // 查看序数为N的线程
~~[n] // 查看线程ID为n的线程
~Ns // 切换序数为N的线程为当前调试线程
~N f // 冻结序数为N的线程
~N u // 解冻序数为N的线程
~N n // Suspend序数为N的线程
~N m // Resume序数为N的线程
!runaway// 显示所有线程的CPU消耗
查看句柄
!handle // 查看所有句柄的ID
!handle 000007f8 1 // 查看ID为000007f8的句柄的类型
!handle 000007f8 4 // 查看ID为000007f8的句柄的名称
!handle 0 5 // 查看所有句柄的类型和名称
dump输出
.dump /ma "d:\mydmpfile.dmp" // 将当前调试进程输出Dump文件
显示栈帧
.frame // 显示当前栈帧
.frame n // 设置编号n的栈帧为当前栈帧
.frame /r n // 设置编号n的栈帧为当前栈帧 并显示寄存器变量
!uniqstack // 显示所有线程的调用堆栈
查看堆分配状况
!heap -p -a 00530B98 // 查看地址00530B98在哪个堆分配空间中
!heap // 查看所有堆分配空间信息
!heap -a 00530000 // 查看堆分配空间00530000的堆块信息
页堆(Page Heap)
1.开启页堆
开启该机制后,堆溢出时就立即触发异常
!gflag +hpa
!gflag -i app.exe +hpa
2.关闭页堆
!gflag -hpa
!gflag -i app.exe -hpa
3.查看是否gflag开启了哪些调试选项f
!gflag
开启子进程调试
.childdbg 1
关闭子进程调试
.childdbg 0
查看是否调试子进程
.childdbg