windbg笔记


windbg既能用户态调试,也能内核态调试。

不支持exe拖拽。

尽量熟悉调试菜单和视图菜单。

0. 配置

用户空间

符号

设置symbol path或者环境变量_NT_SYMBOL_PATH.

u指令现实的函数名基于调试符号,存于pdb文件。vs调试-反汇编窗口-右键符号,可以看到函数名、全局变量等。

一般调试自己的程序时,自带pdb,lm列出,ld加载即可,并且会自动弹出源码窗口,工具栏由source mode on/off开关。

vs编译时也会生成pdb文件,可执行文件中rdata区段包含了符号路径,010Editor打开exe搜索pdb就可以看到路径。所以调试本地编译的exe时不设置路径直接ld也能加载符号。

  • lm列出模块
  • ld 模块名加载某一个模块的符号。
  • .reload /f刷新并强制加载所有符号。或.reload /f 文件路径

设置符号选项:.symopt命令,举例:

  • SYMOPT_UNDNAME
  • SYMOPT_DEFERRED_LOADS
  • SYMOPT_NO_CPP
  • SYMOPT_OMAP_FIND_NEAREST
  • SYMOPT_LOAD_ANYTHING
  • SYMOPT_FAIL_CRITICAL_ERRORS
  • SYMOPT_EXACT_SYMBOLS
  • SYNOPT_NO_IMAGE_SEARCH

1. 调试

1.1 用户层3种调试方式

直接调试

file - open execuable

附加调试

分为:

  • 侵入式,可以调试。
  • 非侵入式,用来读取进程/线程信息,如u、r指令,没有调试,

小知识点,一个程序只能被一个调试器调试。想查看一个被调试程序的信息,可以用非侵入式附加。

attach to process:左下角noninvasive。

本地内核调试

File - kernel Debug

1.2 内核层调试

自己的系统无法调试自己的系统,因为不能中断自己的系统。两种方法:

  • 一种是类似用户层非侵入式调试,不接管系统,仅查看本系统内核信息。
  • 另一种,使用VirtualKD双机调试

双机调试

主机运行vmmon.exe;VirtualKD的target文件夹放进虚拟机,运行vminstall,虚拟机重启,选择调试,主机会默认开启windbg(前提设置好路径),连接好后显示int 3.

本地内核调试

虚拟机开机时f8选择调试模式,管理员方式运行windbg,kernel debug - local,命令窗口显示lkd。

2. 命令

先列举下地址表示方式,也就是文档中所有出现的Address:常量、符号(如函数名)或伪寄存器。

寄存器又叫线程环境。

命令窗口:

  • busy
  • 0.0002> 进程号:线程号;
  • 0.kd:内核调试,多核环境kd前会有核的标号;
  • lkd:本地内核调试。

三类命令:

  • 常规/标准命令,>130
  • 元命令,>100
  • 扩展命令,无限

按回车直接执行上一条命令。上下键查看历史。

输入问号查询帮助。

0:000> ?

Open debugger.chm for complete debugger documentation

B[C|D|E][<bps>] - clear/disable/enable breakpoint(s)
BL - list breakpoints
BA <access> <size> <addr> - set processor breakpoint
BP <address> - set soft breakpoint
D[type][<range>] - dump memory
...

Hit Enter...b
b

<expr> unary ops: + - not by wo dwo qwo poi hi low
       binary ops: + - * / mod(%) and(&) xor(^) or(|)
       comparisons: == (=) < > !=
       operands: number in current radix, public symbol, <reg>
...

2.1 常规命令

windbg内置,130多条。

常用:

  • lm,列出模块
  • ld,加载模块
  • q,退出调试

程序控制类

g执行:

  • g,可跟地址
  • gn,不处理异常并继续执行
  • gu,执行到父函数

t系列:

  • t,单步步入,F11;
  • ta ADDR,执行到某地址
  • tc,执行到下一个call

p系列:

  • p,单步步过,F10;
  • pa;
  • pc;

内存查看修改

d系列:

  • d:查看数据
  • da:ascii
  • du:unicode
  • dt:查看数据结构
  • dd:查看内存

e系列:

s系列:

断点类

软件断点
  • bp:软件断点
  • bu:符号断点,推荐使用

bp[ID] [Options] [Address [Passes]] ["CommandString"]

  • options:
    • /1: 一次性
    • /c:指定最大调用深度
    • /C:指定最小调用深度
  • address:地址,也可以是符号
  • passes:忽略中断次数,可缺省
  • CommandString:记着带引号

bu对符号下断点,且符号地址改变后,断点仍关联。

bm支持通配符,可设置多个bu或bp。

bp kernel32!GetVersion无效,要先ld kernel32加载模块,lm可查看。

硬件断点

ba [i|w|r|e] Size Addr

有数量限制,但相比软件断点,可以监视IO访问(即i选项)等。

size: 1,2,4.

注意访问方式和size之间不能有空格。


条件断点

j命令:j Exp CMD1; CMD2。相当于C的Exp ? CMD1 : CMD2

软件断点和硬件断点都支持条件断点。

断点附加命令,断下来时执行一段命令:

  • bp ADDR ".if (CONDITION) {cmd} .else {gc}"
  • bp ADDR "j (CONDITION) 'cmd'; 'gc'",gc就是继续执行。

eg. :

  • bp kernel32!GetVersion ".if (@eax=0x12345678) {} .else {gc}"
  • bp @$exentry "r @eax; r @edx"

这个条件未必断下,因为在内核态masm会对eax的值进行符号扩展,最好这样改条件:

(@eax & 0xffffffff)=0x12345678


管理断点
  • bl列出断点
  • bc删除
  • bd禁用
  • be启用
  • br改变编号

bc *删除所有断点。


堆栈

k系列命令可以查看栈回溯。

k[b|p|P|v|d]

查看某个线程的堆栈:~TID k

带参:kb 2,只显示2层堆栈调用。

查看第4个参数:dd ebp+14h

  • kb,显示函数的前3个参数,即ebp+8h/ch/10h;;
  • kd,列出栈的内容;
  • kp,显示函数原型,有符号时使用;
  • kv,在kb基础上增加栈帧省略信息FPO。

反汇编

u系列:

  • u [Addr] [ l count ]:反汇编指定地址之后的指令,count默认8行;
  • ub:反汇编指定地址之前的指令;
  • uf:反汇编函数,有符号时使用,eg. uf USER32!MessageBoxA; uf func

注意l和行数之间没有空格。

线程和寄存器

寄存器属于线程环境,所以放在一块。

线程相关命令(带波浪号):

  • ~.,当前线程;
  • ~#,导致当前异常的线程;
  • ~*,进程中的所有线程;
  • ~Ord,序号查询,例如4核0-3
  • ~~[TID]

eg.:

  • ~1 k,查看1号线程堆栈

windbg很多命令需要携带线程,如下面的寄存器命令。

r系列:

  • r,查询所有
  • r @eax,查询
  • r @eax=表达式,修改,eg.r @eax=1
  • r @eax=@ebx

建议不要省略@符号。比如监视窗口一定要有@符号。

其它

x, q, ls

$exentry 入口点

!wow32

2.2 元命令

windbg内置,以点开头,后跟单词。

.symopt, .sympathy, .asm, .restart, .reboot

.help查看元命令帮助,相当于标准命令的问号。

常用:

  • .reload
  • .restart
  • .detach

2.3 扩展命令

叹号+单词,非内置,实现于扩展模块dll中。

.chain列出所有扩展模块。

叹号开头的操作内存的指令都是对物理内存操作,ring 3下不起作用。

!teb得到地址,可搭配dt _TEB 地址使用。

3. 分析基础

3.1 模块分析

  • lm
  • lmvm,列出某个模块具体信息,eg. lmvm user32
  • !dh ImageBase,查看某一个模块PE信息

3.2 搜索

s命令,内存搜索:

  • s [-flags]sa|su,搜索ascii或unicode字符串
  • s [-flags]type,搜索特定数据类型

flags: s|r|n|w|l|1; type: b|w|d|q|a|u

eg.

  • s -su 0000000140000000 L400,可能查到中文
  • s -a 0012ff40 L20 "Hello"
  • s -b 0012ff40 L20 4d 5a

x命令,符号搜索:

  • x 符号,eg. x *er*!Message*

井号#搜索符号引用:

  • # 符号 范围

eg:

  • # MessageBoxA 0012ff40 L5000

3.3 分析模块链

PEB.Ldr的3个LIST_ENTRY节点,同属LDR_TABLE_ENTRY结构。

dt 符号|链表节点 ADDR

参数:

  • -r: -r2,展开2层结构体,常用

遍历链表:dt _LDR_DATA_TABLE_ENTRY -l InLoaderOrderLinks.Flink [字段 字段 ...] ADDR

eg. dt _LDR_DATA_TABLE_ENTRY -l InLoaderOrderLinks.Flink DllBase SizeOfImage ADDR

3.4 分析驱动

单独整理在内核编程部分。

地址表示方式不变,如? DriverUnload

3.5 分析进程

内核层调试时,如果想调试某个进程(虚拟空间),需要切入该进程。

  • 枚举进程:!process 0 0
  • 切入进程虚拟空间:.process /i EPROCESS地址

3.6 分析蓝屏dump

系统属性-高级-写入调试信息-核心内存转储。

dump文件可以直接拖拽进windbg分析。

如果时分析别人的驱动,可能既没有源码也没有符号,就比较困难了。

3.7 re

  • *,0或多个
  • ?,任意单个
  • [],范围
  • #,0或多个前一字符
  • +,1或多个前一字符

通常搭配x使用:

  • x test!m[a-z]#n,找到main

4. 脚本

脚本保存为txt即可,执行命令$$>< ScriptPath

单行注释用$$

4.1 数据类型(c++和masm)

4.2 变量定义(伪寄存器和别名)

伪寄存器分为:

  • 自定义伪寄存器:$t0 - $t19,相当于10个变量
    1. r $t0 = 100
    2. ? $t0+200
    3. r $t1 = $t0*2
  • 自动伪寄存器:存储关键信息

常用自动伪寄存器:

  • $exentry
  • $peb
  • $teb
0:000> ? $exentry
Evaluate expression: 4264746 = 0041132a
0:000> u 0041132a
CTest!ILT+805(_mainCRTStartup):
0041132a e9d10b0000      jmp     CTest!mainCRTStartup (00411f00)
0:000> dd $peb
002c2000  00010000 ffffffff 00400000 773bdca0
0:000> dt _PEB
CTest!_PEB
   +0x000 Reserved1        : [2] UChar
   +0x002 BeingDebugged    : UChar
   +0x003 Reserved2        : [1] UChar
...

0:000> !peb

windbg别名机制

可以理解为宏,而伪寄存器更像变量。

  • 自动别名:调试器设置

  • 固定别名:$u0 - $u9

    1. r $.u0 = 1带点使用
    2. ? $u0,不用带点
    3. r $u0,错误指令,相当于r 1
    4. r $.u0,可以执行
  • 自定义别名

    1. as/aS 定义,可带参
      1. -e,环境变量作为别名
      2. -ma,内存地址字符串作为别名
      3. -f,文件内容作为别名,用在aS中
    2. ad 删除
    3. al 列出
  • as CMD bp user32!MessageBoxA

  • r $.u0 = bp user32!MessageBoxA

4.3 表达式

windbg支持2种表达式:

  • masm,默认且推荐默认
  • c++,

-ee {masm|c++}设置表达式类型。

masm表达式特点:

  • 问号求值
  • 默认16进制,可指定前缀
    1. 0x
    2. 0n,十进制
    3. 0t,八进制
    4. 0y,二进制
  • 所有符号均为地址
  • 一些特有的运算符(美元符表示物理地址)
    1. hi/low,取高/低16位,? hi(0018ffcc)
    2. by/$pby,取地址处的1个字节
    3. wo/$pwo,取地址处的2个字节
    4. dwo/$pdwo,取地址处的4个字节
    5. qwo/$pqwo,取地址处的8个字节
    6. poi/$ppoi,取地址处的数据

c++表达式特点:

  • ??求值
  • 进制默认十进制,前缀0x/0
  • 符号根据自身类型解析,如数据结构、指针、类,而不仅仅是地址。
  • 使用寄存器和伪寄存器,必须有@前缀,且不能用等号赋值,而是用r命令。

masm/c++中@@(exp),则exp由c++/masm规则解析。或者@@c++(), @@masm()指定。也可以.expr /s c++|masm切换。

eg.

  • dd stcVar
  • dd @@(stcVar.szString)
  • ? @@(stcVar.nNum)

0:000> ? 100
Evaluate expression: 256=00000100
0:000> ?? 100
int 0n100
0:000> .formats 100
Evaluate expression:
  Hex:     00000100
  Decimal: 256
  Octal:   00000000400
  Binary:  00000000 00000000 00000001 00000000
  Chars:   ....
  Time:    Thu Jan  1 08:04:16 1970
  Float:   low 3.58732e-043 high 0
  Double:  1.26481e-321
0:000> .formats @@c++(100)
Evaluate expression:
  Hex:     00000064
  ...

4.4 三大结构

选择:

  • .if (CONDITION) {CMD} .else {CMD}
  • .if (CONDITION) {CMD} .elsif(CONDITION) {CMD} .else {CMD}

循环:

  • .for (;;) {}
  • .while() {}
  • .do {} while()

如果没有符号,可以通过脚本来锁定某个地址,如main。

4.5 库函数命令和语句

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值