1. 线程相关
线程命令是以~开始,后面跟线程id(不是tid,是windbg从0开始的一个编号),或者.,#,*等
~. 表示当前线程
~# 表示异常或者产生调试事件的线程
~* 表示所有线程
~1 表示一号线程
~2 s 表示选择2号线程作为当前线程
~3 f 冻结三号线程
~3 u 解冻三号线程
~2 n 挂起二号线程
~2 m 恢复二线程
线程指令还可以与其他指令混合使用,如查看某一个线程的栈:
~1 k 查看一号线程的栈
~* k 查看所有线程的栈
如只想让某个线程激活断点,可以使用
~ 1 bp CPeListCtrl::ShowExportFuncs 对一号线程下断点
2. 查看栈指令: k
k指令单独使用时,之显示栈地址、返回地址、函数名信息。如果需要其他信息需要使用参数。常见的有
b 显示函数调用时的前三个参数
c 只显示函数名
p 显示函数的所有参数,包括参数的名字、类型、值。
v 显示帧指针遗漏(FPO)信息。在基于x86处理器,显示器还包括调用约定信息
n 显示调用栈的每帧编号
f 显示调用栈的每帧占用字节数
默认情况下使用k显示当前线程栈,如果加上线程命令前缀,可以查看某个线程栈,如~* k查看所有线程栈
3. 显示局部变量: dv。显示本层局部变量信息
常用的参数:
/i 显示变量是局部的、全局的、参数、还是函数等信息
/v 显示变量所在地址
/t 显示变量的类型
4. 切换栈帧:.frame 切换栈帧
与dv命令一起可以查看每帧的局部变量
5. 查看类型指令: dt
默认显示所有类型信息。加参数-b显示子类型信息
6. 断点
bp 常用执行断点
bm 设置执行符号断点,可以同时对多个符号下断
bu 设置一个延时断点,可以对一个还未加载的模块执行代码下断。当模块加载时,此断点自动激活
ba 设置内存访问断点
以上断点都可以针对某个线程下断。如 ~1 bp 在一号线程上下执行断点
bl 查看断点列表
bd 1 禁用一号断点
be 2 启用二号断点
bc * 清除所有断点
7. 执行命令:g
g是执行命令,可以指定从某处执行,也可以在执行到某处时断下,如:
g =00423aef 00423af3
修改当前eip为00423aef,执行到00423af3处中断
当然也可以让某个线程继续执行,而其他的线程处在冻结状态。如:
~1 g 一号线程继续执行
8. 其他执行命令:
p 单步步过
t 单步步入
gu 执行到返回
gc 从断点处继续执行,用在条件断点内
gn 忽略异常继续执行。允许应用程序的异常处理程序来处理异常。
gh 异常被处理,继续执行
9. 显示汇编指令: u
u 向下反汇编
ub向上反汇编
uf反汇编整个函数
10. 写入汇编指令: a
此命令可以直接修改代码段中的指令
11. 显示符号指令: x
不光可以显示全局符号,也可以显示局部符号
其中符号包括:函数、全局对象、静态对象、参数、局部对象等
12. 显示最近的符号: ln
此命令在不知道某处地址是属于哪个函数时,此命令很有用。它可以告诉你此地址附近的函数是什么
13. C++表达式的用法
当我们查看某个变量或者下条件断点时,可能会获取一个对象中的成员值。那应该怎么获取呢?
13.1. c++表达式求值运算:??
0:000> ?? this->m_nFlags //显示C++变量
unsigned int 0 //默认为unsigned int类型
0:000> ?? this->m_nFlags=0x78458789 //修改C++变量值
unsigned int 0x78458789
0:000> ?? (this->m_nFlags)
unsigned int 0x78458789
0:000> ?? int(this->m_nFlags) //按int类型显示
int 0n2017822601
0:000> ?? (unsigned short)(this->m_nFlags) //按unsigned short类型显示
unsigned short 0x8789
0:000> ?? (short)(this->m_nFlags) //按short类型显示
short 0n-30839
0:000> ?? (unsigned char)(this->m_nFlags) //按unsigned char类型显示
unsigned char 0x89 ''
0:000> ?? (char)(this->m_nFlags) //按char类型显示
char 0n-119 ''
13.2. C++前缀:@@()
//设置条件断点。当this->m_nFlags==0x78458789时断下
bp 00423af9 ".if(@@(this->m_nFlags) == 0x78458789){}.else{gc;}"
14. 一般表达式
14.1. 一般表达式求值:?
14.2. 字符串比较
//$scmp 为大小写敏感的字符串比较函数.返回值为-1,0,1之一
0:000> ? $scmp("abc","abc")
Evaluate expression: 0 = 00000000
0:000> ? $scmp("Abc","abc") Evaluate expression: -1 = ffffffff 0:000> ? $scmp("abc","Abc")
Evaluate expression: 1 = 00000001
//$sicmp 为大小写不敏感的字符串比较函数.返回值为-1,0,1之一
0:000> ? $sicmp("abc","Abc")
Evaluate expression: 0 = 00000000
0:000> ? $sicmp("abc","Abcd")
Evaluate expression: -100 = ffffff9c
0:000> ? $sicmp("abcd","abc")
Evaluate expression: 100 = 00000064
//$spat 通配符匹配.返回值为0(false),1(true)之一。支持以下通配符:
// * 表示0-n个任意字符
// ? 表示1个任意字符
// + 表示1-n个前面的字符
// [] 表示任意单个字符的列表。可以使用'-'表示一个范围
// # 表示0-n个前面的字符
//下面为示例用法:
0:000> ? $spat("abcd","abc*")
Evaluate expression: 1 = 00000001
0:000> ? $spat("abcd","abc+")
Evaluate expression: 0 = 00000000
0:000> ? $spat("abcd","abc[abc]")
Evaluate expression: 0 = 00000000
0:000> ? $spat("abcd","abc[abcd]")
Evaluate expression: 1 = 00000001
0:000> ? $spat("abcc","abc+")
Evaluate expression: 1 = 00000001
0:000> ? $spat("abcc","abc")
Evaluate expression: 0 = 00000000
0:000> ? $spat("abc","abc?")
Evaluate expression: 0 = 00000000
0:000> ? $spat("abcd","abc?")
Evaluate expression: 1 = 00000001
0:000> ? $spat("abcde","abc?")
Evaluate expression: 0 = 00000000
14.3. 判断一个地址是否无效:$vvalid
返回0无效,1为有效
0:000> ? $vvalid(0, 100)
Evaluate expression: 0 = 00000000
0:000> ? $vvalid(0x400000, 100)
Evaluate expression: 1 = 00000001
14.4. 格式化数值:.formats
0:000> .formats 1000
Evaluate expression:
Hex: 00001000
Decimal: 4096
Octal: 00000010000
Binary: 00000000 00000000 00010000 00000000
Chars: ....
Time: Thu Jan 01 09:08:16 1970
Float: low 5.73972e-042 high 0
Double: 2.02369e-320
15. 返回最近的错误码指令: !gle
与GetLastError相同
16. 查询错误码含义: !error
17. 查看最后一次事件信息: .lastevent
18. 查看版本信息: version、vertarget
在调试的过程中我们可能需要修改汇编代码,比如做个跳板改变执行流(在介绍a指令的时候有用到.在第三楼),或者把内存dump到文件中,或者把文件中的数据读入内存中。这就牵涉到怎么分配和释放内存,怎么读写内存等操作。下面我们一起来学习一下内存操作相关指令把。
19. 分配内存指令: .dvalloc
.dvalloc指令类似与VirtualAlloc函数。可以指定分配的大小、类型(MEM_RESERVE | MEM_COMMIT)(加上参数/r,申请的内存类型为MEM_RESERVE。默认为MEM_COMMIT)、起始地址(加上参数/b)
使用.dvalloc分配的内存都是PAGE_EXECUTE_READWRITE属性
20. 释放内存指令: .dvfree
有分配就有释放.dvfree指令类似VirtualFree 函数。
21. 查看内存属性:!vprot
!vprot指令类似与VirtualQuery 函数。可以获取一个内存块的各种属性。
22. 查询内存信息命令: !address
!address命令可以按照性质(image or stack or heap or filemap...)、类型(MEM_IMAGE or MEM_MAPPED or MEM_PRIVATE) 、状态(MEM_COMMIT or MEM_FREE or MEM_RESERVE )、保护属性(PAGE_NOACCESS or PAGE_READONLY ...)等组合查询某一组内存列表。
23. 从文件中读入数据到内存指令: .readmem
直接使用例子演示。 我在c盘下建立一个hello.txt的文件。
文件中是一句话:hello pediy, I'm ddlx.
下面我把这个文件读入到内存
24. Dump内存指令:.writemem
我们经常会存在如下需求:有一个程序是压缩壳,需要把解压后的代码Dump出来;为了分析某些数据,需要把某个内存块Dump出来。那使用.writemem把。
.writemem与.readmem类似,是他的逆操作。
25. 读内存指令:d
读指令是最常用的指令之一。它负责按照指定的格式,格式化输出内存数据。下面介绍一下读各种数据的方法:
- da 按照ASCII字符串读取
- db 按照单字节和ASCII字符串读取
- dc 按照4字节和ASCII字符串读取
- dd 按照4字节读取
- dD 按照双浮点(8字节)格式读取
- df 按照单浮点(4字节)格式读取
- dp 按照指针(32位系统读取4字节,64位系统读取8字节)格式读取
- dq 按照8字节读取
- du 按照Unicode字符串读取
- dw 按照2字节读取
- dW 按照2字节和ASCII字符串读取
- dyb 按照单字节和二进制读取
- dyd 按照4字节和二进制读取
26. 显示符号表指令: dds、dps、dqs
这个常用在查看虚函数表、导入地址等操作时使用。
27. 查看ascii字符串数组命令: dda、dpa、dqa
28. unicode字符串数组查看: ddu、 dqu、 dpu
29. 内存写入操作命令: e
写指令是负责按照指定的格式,格式化输出内存数据,对命令中已经看到部分写命令的用法。下面介绍一下写各种数据的方法:
- ea 写入ASCII字符串
- eb 写入单字节
- ed 写入4字节
- eD 写入双浮点(8字节)格式
- ef 写入单浮点(4字节)格式
- ep 写入指针(32位系统读取4字节,64位系统读取8字节)格式
- eq 写入8字节
- eu 写入Unicode字符串
- ew 写入2字节读取
- eza 写入无结束符的ASCII字符串
- ezu 写入无结束符的Unicode字符串
30. 内存搜索指令: s
内存搜索指令,在查找某种类型的数据的时候非常有用,我们来看一下内存搜索指令的用法
31. 物理内存读写操作指令
!d 读物理内存
!e 写物理内存
这两个用的很少。不做过多介绍