OllyDbg调试器
指定到光标所在的行停止:F4
反汇编面板中对指令进行编辑快捷键:F2
反汇编面板中对指令进行添加注释快捷键:;
数据面板中想要显式指定地址的数据快捷键:Ctrl+G
OllyDbg配置文件:olldbg.ini
OllyDbg工程文件:UDD
,保存了当前调试的状态、断点、注释等
OllyDbg插件文件夹:plugin
调试快捷键:
INT3断点:采用INT 3(无条件中断指令)来替换断点的指令,当CPU执行INT 3时将产生一个中断来通知调试器
DRx断点:又称硬件断点,利用CPU提供的DRx寄存器,来执行断点,优点是不容易被检测到,缺点是只能同时下4个断点
内存读写断点:通过对指定的内存设置不可访问、不可写的属性,当被调试程序想要访问/写入时将出现异常,此时将被调试器截获,程序将停止在将要执行内存访问/写入的指令上
内存硬件断点:通过Drx寄存器可使用内存硬件断点,内存硬件断点,将停在内存访问/写入/执行的指令之后的一条指令上
内存段断点:可以通过ALT+M打开内存段窗口,对某一个段内存设置断点,此类断点为一次性断点
消息断点:消息断点只能在程序窗口已经被创建完毕后,才能设置
条件断点:当满足一定条件是,断点才会生效,如寄存器等于某个值、调用某个API时当某个参数为特定的值时
条件记录断点:条件断点+记录此时的函数表达式或参数的值
WinDbg调试器
WinDbg使用环境变量_NT_SYMBOL_PATH指向符号文件文件夹位置
配置微软符号服务器:
SRC*C:\Symbols*http://msdl.microsoft.com/download/symbols
1. WinDbg符号表示方式:
模块名!符号名
如:kernel32!OpenProcess
内核模块统一使用nt进行表示,如nt!NtOpenProcess
不知道符号具体名字时,可采用模糊匹配,语法为:
x [Options] Module!Symbol
检索时,符号名称可使用*、?、[]、#、+等特殊字符进行模糊匹配
如:x kernel32!*Process*
ld
:从符号文件目录或符号服务器加载符号
lm
:观察模块的符号加载情况
2. 控制程序执行流程命令
- WinDbg调试程序时,反汇编代码默认停留在ntdll.dll中的系统断点处,不会直接停在程序应用层入口处,可以输入
g @$exentry
转到程序入口处 - WinDbg单步跟踪快捷键:
- 伪寄存器
$ra
代表当前函数的返回地址,可使用pa
或ta
命令加上@$ra
来走出当前函数,即pa $ra
3. 断点命令
bl
:列出当前所有断点
bc ID
:删除断点
bd ID
:停用断点
be ID
:启用断点
1. 软件断点
bp [ID] [Options] [Address [Passes]] [“CommandString”]
ID
: 断点ID,可缺省。内核调试最多32个断点,用户模式不限
Options
:可缺省
- /1:中端后自动删除该断点,即一次性断点
- /c:指定最大调用深度,大于这个深度则断点不工作
- /C:指定最小调用深度,小于这个深度则断点不工作
Address
:地址或符号,不可缺省。例如MessageBoxW
Passes
:忽略中断的次数,可缺省
CommandString
:当断点被触发时,WinDbg将自动执行里面的命令,双引号包裹,多个命令逗号分隔
bu
命令用于对某个符号下断点,如:bu kernel32!GetVersion
。
bu
命令创建的断点与符号关联,如果符号地址改变,断点会保持与原符号的关联
bp
命令创建的断点与地址相关联,如果符号地址改变,断点仍然和之前的地址相关联
bm
命令支持设置包含通配符的断点,可以一次创建一个或多个断点,如:bm msvcr80d!print*
。
2. 硬件断点
ba [ID] Access Size [Options] [Address [Passes]] [“CommandString”]
ID
: 断点ID,可缺省
Access
:指定触发断点的访问方式
- e:读取或执行时触发断点
- r:读取时触发断点
- w:写入时触发断点
- i:执行输入/输出访问I/O时触发断点
Size
:访问的长度,x86可使用1、2、4,x64可使用1、2、4、8,单位为字节
Address
:断点的地址,地址值按Size的值进行对齐
Passes
:忽略中断的次数,可缺省
CommandString
:当断点被触发时,WinDbg将自动执行里面的命令,双引号包裹,多个命令逗号分隔
3. 条件断点
bp|bu|bm|ba _Address ".if (Condition) {OptionalCommands} .else {gc} "
示例:bp kernel32!GetVersion “.if (@eax=0x12ffc4) {} .else{gc}”
含义:当GetVersion被调用时,将检测eax寄存器,如果其值等于0x12ffc4就中断,否则执行gc继续
实例:bp kernel32!CreateFileA “.echo; .printf\“CreateFileA(%ma, %p, %p), ret=\”,poi(esp+4),dwo(esp+8),dwo(esp+c);gu;.printf\”%N\",eax;.echo;g"
含义:在不中断进程的情况下,打印所有CreateFileA的函数调用
结果:
4. 栈命令
栈命令: k、kb、kp、kP、kv、kd
k
命令:ChildEBP地址,函数的返回地址、函数名和偏移位置
kb
命令:ChildEBP地址,函数的返回地址、函数的在栈上的前3个参数、函数名和偏移位置
kp
命令:可以将参数和参数值以函数原型的形式展现出来,包括参数类型、名称、取值
kv
命令:在kb
命令的基础上增加帧指针省略信息(FPO)和调用约定显式
kd
命令:用于列出栈中的数据
5. 内存命令
1. 查看内存
d[类型] [地址范围]
d命令有d、da、db、dc、dd、dD、df、dp、dq、du、dw、dW、dyb、dyd、ds、dS等形式
dw
:表示2字节WORD形式
dd
:表示4字节DWORD形式
dq
:表示8字节格式
df
:表示4字节单精度浮点形式
dD
:表示8字节双精度浮点形式
dp
:表示指针大小格式,在32位系统中4字节,64位系统中8字节
da
:表示ASCII字符串
db
:表示字节和ASCII字符串
dc
:表示DWORD和ASCII字符串
du
:表示Unicode字符串
dW
:表示双字节WORD和ASCII字符串
ds
:用于显式ANSI_STRING类型的字符串
dS
:用于显式UNICODE_STRING类型的字符串
dyb
:表示显式二进制和字节
dyd
:表示显式二进制和DWORD值
dt [模块名!]类型名
:用于显式数据类型和数据结构,例如dt ntdll!*
列出NtDll模块中所有数据结构
dds
、dqs
、dps
:用于显示地址及相关符号
地址范围使用L参数设置,如dd 401000 L4
表示显示前4个数据
2. 搜索内存
s - [type] range pattern
type
:搜索内容的数据类型,默认为b
- b:BYTE
- w:WORD
- d:DWORD
- a:ASCII
- u:UNICODE
range
:表示地址范围, 两种表示方式,
- 起始地址 终止地址
- 起始地址 L长度(长度超过256M,需用L?长度)
pattern
:指定要搜索的内容
3. 修改内存
修改内存共有三种模式,字符串模式、数值模式、交互模式
字符串模式:e{a|u|za|zu} address “string”
ea
:表示要写入的字符串是不需要以0结尾的ASCII字符串
eu
:表示要写入的字符串是不需要以0结尾的UNICODE字符串
eza
:表示要写入的字符串是需要以0结尾的ASCII字符串
ezu
:表示要写入的字符串是需要以0结尾的UNICODE字符串
示例:eza 298438 “pediy”
表示在地址为298438开始地方写上字符串pediy并且以0结尾
数值模式:e{a|b|d|D|f|q|u|w} address [Values]
ea
:表示将Values中的每一部分的数字作为ASCII写入
eb
:表示将Values中的每一部分的数字作为BYTE写入
ed
:表示将Values中的每一部分的数字作为DWORD写入
eD
:表示将Values中的每一部分的数字作为DOUBLE写入
ef
:表示将Values中的每一部分的数字作为FLOAT写入
eq
:表示将Values中的每一部分的数字作为8字节写入
eu
:表示将Values中的每一部分的数字作为UNICODE写入
ew
:表示将Values中的每一部分的数字作为WORD写入
示例:eza 298438 70 65 64 69 79
表示在地址为298438开始地方写入"70 65 64 69 79"(字符pediy)
交互模式:e{a|u|za|zu} address 或 e{a|b|d|D|f|q|u|w} address,不添加要修改的字符
4. 观察内存属性
!address [Address]