Joe1sn's Cabinet

windbg调试入门笔记

准备点windows基础功

安装与配置

1.版本

WinDbg一般分为x86和x64,具体由调试os平台(Host)和被调试程序类型决定

  • x86-based Host Computer

    x86处理器平台上调试所有程序都使用该版本

  • x64-based Host Computer

    1. 分析DUMP文件:若文件是在windows XP及更新的版本生成的,则两者都可;若Windows2000及更早的版本就只能使用x86版本的WinDbg

    2. 双机调试

      对于WindowsXP及其更新的系统windbg会自动适应,更老的只能使用x86版本

    3. 用户态

      x64版本程序只能使用x64版本,x86版本程序则是两者皆可

2.工作空间

​ 每调试一个程序,会涉及到要使用的参数、配置等信息,WinDBG会使用Workspace来保存这些信息,可以理解为项目文件
image-20220609192902393

3.调试符号

​ 最经典的就是使用IDA反汇编的时候,如果文件没有调试符号信息,那么函数就会被命名为sub_startaddr这样。所以在调试的时候这些信息会大大帮助我们理解程序

​ windows的就是PDB文件,

image-20220609193314803

image-20220609193424085

WinDBG允许用户指定一个或者多个目录存放符号文件,并使用环境变量_NT_SYMBOL_PATH指向目录,最常用的是从微软的符号服务器下载(IDA会默认加载,并且设置了全局变量为符号服务器的话每次vs编译都会从服务器拉,特别慢),具体配置百度

相关指令

ld:自动从符号文件目录或者符号服务器加载符号文件

  1. 使用 lm 查看模块

    image-20220609194324229

  2. 符号的表达:模块名!函数名

    kernel32模块的OpenProcess函数:kernel32!OpenProcess

    内核不同,比如 ntdll!NtOpenProcessnt!NtOpenProcess 分别表示ntdll中的NtOpenProcess函数 和 内核模块中的NtOpenProcess函数

  3. 符号检索:x [选项] Module!Symbol

    符号名可以使用? * [] # +进行模糊匹配

  4. 源码级别调试

    需要源文件于WinDBG在同一系统中,使用ctrl+P,指定源代码路径就行了,多个路径使用;分隔

    使用ctrl+O选择源代码文件,在源代码文件中F9添加断点

    image-20220609195717749

    image-20220609200249003

调试过程

1.开始调试

反汇编代码默认停留在ntdll中的系统断点,使用g @$exentry跳转带函数入口

image-20220609201112856

2.控制目标程序执行

image-20220609201202279

伪寄存器@$ra表示当前函数返回地址,那么pa @$ra则表示跳出当前函数

断点命令

1.对于int 3

分别是bp bu bm

  • bp[ID] [选项] [地址or符号[要忽略的中断次数]] [中断时执行的命令]

    选项:/l 一次性断点;/c最大调用深度;/C最小调用深度

  • bu kernel32!GetVersion:对符号下断点

  • bu kernel32!GetVersio*:对包含通配符下断点

image-20220609202028555

image-20220609203322923

2.硬件断点

ba[ID] 访问方式 访问长度 [选项] [断点地址or符号[忽略中断次数]] [终端执行指令]

  • 访问方式
    • e:读取或执行时触发
    • r:读取时触发
    • w:写入时触发
    • i:在执行输入输出时触发

3.条件断点

建议用到的时候百度

image-20220609203950271

4.管理断点

bl:列出所有断点

bc bd be:删除 禁止 启用断点

栈窗口

image-20220609204833953

1
2
3
4
5
6
7
8
0:000:x86> k
# ChildEBP RetAddr
00 0019ff3c 0040112e esp+0x115e
01 0019ff54 00401097 esp+0x112e
02 0019ff64 00401009 esp+0x1097
03 0019ff80 77037a7e esp+0x1009
04 0019ffdc 77037a4e ntdll_76fd0000!__RtlUserThreadStart+0x2f
05 0019ffec 00000000 ntdll_76fd0000!_RtlUserThreadStart+0x1b

每一行都是当前线程的一个栈帧,00~05是站的调用链,从当前到顶层。

第一列是基地址(ChildEBP);第二列是返回地址;第三列是函数的执行地址

1
2
3
4
5
6
7
8
0:000:x86> kb
# ChildEBP RetAddr Args to Child
00 0019ff3c 0040112e 00000008 00000007 00000006 esp+0x115e
01 0019ff54 00401097 00000004 00000003 0019ff80 esp+0x112e
02 0019ff64 00401009 00000002 00000001 7681fa29 esp+0x1097
03 0019ff80 77037a7e 002f5000 f602b8bf 00000000 esp+0x1009
04 0019ffdc 77037a4e ffffffff 77058a01 00000000 ntdll_76fd0000!__RtlUserThreadStart+0x2f
05 0019ffec 00000000 00401000 002f5000 00000000 ntdll_76fd0000!_RtlUserThreadStart+0x1b

kb会显示栈上面的前3个参数

  • kp :参数和参数值按函数原型显示
  • kv:相对于kp增加帧指针省略信息和调用约定显示
  • kd:列出栈中的数据

内存命令

1.查看内存

d[类型] [地址范围]

dw:DWORD
dd:4字节DWORD
dp:8字节
df:4字节单精度浮点
dp:指针大小格式
da:ASCII字符串
db:字节和ASCII字符串
ds:ANSI_STRING

dt [模块名!]类型名:显示数据类型和数据结构

image-20220610105504772

2.搜索内存

s - [type] range pattern

  • type:搜索的数据类型:b(byte) w(word) d(dword) a(ASCII) u(Unicode)
  • range:
    • 起始地址与终止地址 s -a 0x4000000 0x4030000 "test"
    • 起始地址和搜索长度 s -a 0x0000000 L?0x7fffffff "mytest"

image-20220610105541940

3.修改内存

e {a|u|za|zu} address "String"
e {a|b|d|D|f|q|u|w} address [value]

4.观察内存属性

!address [Address]

脚本

winDBG可以像python一样解释执行脚本中的语言

1.伪寄存器

  • @$exentry:当前进程的入口,g @$exentry可以直达入口
  • $ip:指令指针寄存器
  • $ra:当前函数返回地址
  • $retreg:函数返回值
  • $csp:当前栈指针(current stack pointer)

image-20220610114849541

其余建议百度

调试拓展功能

类似于插件的加载

image-20220610115012875

开发的话可以参考WinDbg提供的sdk(在安装文件夹下就有)