gdb笔记

摘要

命令总结

https://github.com/codeExploreHub/GDBDebug

连接

gdb连接进程分为两种(可能?)

使用gdb打开文件,然后开始运行

1
2
gdb file
(gdb) r

使用gdb连接到一个已经在运行的进程

例如使用qemu模拟的一个qcow2内核镜像。这里需要注意的是,另外起的进程一般不会自动停住,等运行完连上去就晚了,所以需要控制启动后停止,例如qemu的参数就是-S,开机后暂停 CPU。

然后s等同 gdb tcp::1234,在本机监听 1234 端口作为 gdb server,让gdb能连上去

(其他参数都略过了)

1
qemu-system-x86_64 -hda sys.qcow2 -S -s

这边启动完成,另起终端打开gdb,连接到QEMU的调试端口

1
2
sudo gdb
(gdb) target remote :1234

如果说调试的镜像文件没有符号,但是你手头有一个有符号的文件symbol(?)可以为gdb调试镜像文件提供调试信息,那么可以在一开始用gdb打开它:

1
2
(gdb) file ./symbol    # 加载符号文件(如果之前没加载)
(gdb) target remote :1234

或者

1
2
gdb ./symbol
(gdb) target remote :1234

解析:

symbol是带符号的elf,为gdb提供函数名、变量名、类型信息、行号等调试信息

gdb与qemu通过远程调试协议在端口1234进行连接

模拟器qemu实际执行的是要调试的内核镜像文件sys.qcow2

断点

函数断点。不讲

地址断点。形如:

1
b *0xFFFFFFFF82890FBC

删除断点

1
(gdb) delete 3        # 删除编号为 3 的断点

禁用/启用断点

1
2
(gdb) disable 3       # 禁用断点 3(暂时不用,但保留)
(gdb) enable 3 # 重新启用断点 3

条件断点

1
2
3
4
5
(gdb) break *0xffffffff8289260e thread 2 if $r14d == 0x10
# 给现有断点加条件(或修改条件
(gdb) condition 16 $r14d > *(unsigned int*)($rsp + 0x14)
# 删除条件(变成普通断点)
(gdb) condition 16

获取信息

查找find

基础命令形如

1
find addr_start, addr_end, (内容)

也可以是addr_start, +0x10000(地址范围

1
(gdb) find /b 0xFFFFFFFF82891E7C, 0xFFFFFFFF82892000, 0xe8, 0x74, 0x54

x (examine)

基础命令形如

1
x/(多少个)(格式) addr

按指令显示

1
(gdb) x/5i 0x12345680

按字节显示十六进制格式

1
(gdb) x/5bx 0x12345680

image-20260311103639090

查看寄存器

info或者p

查看所有寄存器

1
(gdb) info all-registers

查看特定寄存器

1
2
3
4
# 查看rax寄存器的值
(gdb) info register rax
# 或简写
(gdb) p $rax

捕捉

watch

观察点一般用来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,则马上停住。

catch

修改

set

改变某处的指令

1
set {unsigned char}0xffffffff8280000 = 0xeb

改变寄存器

1
set $rax = 0x123456789abcdef0

线程

线程相关的命令

查看线程列表

1
info thread

image-20260311141454326

切换线程

1
thread 4

查看当前线程

1
thread

只在线程2上调试,其他线程冻结

1
2
3
(gdb) thread 2
(gdb) set scheduler-locking on
(gdb) c # 只运行线程2

只影响当前线程的命令

1
2
3
4
5
(gdb) next          # 只执行当前线程的下一行
(gdb) step # 只执行当前线程的下一步
(gdb) finish # 只完成当前线程的函数
(gdb) set $rax = 0 # 只修改当前线程的寄存器
(gdb) x/10i $pc # 只看当前线程的指令

对所有线程生效的命令

输入 continue 时,所有线程都开始运行。哪个线程先遇到事件(断点、信号等),GDB 就会自动切换到那个线程并暂停。

按下Ctrl+C时,所有线程都会停止,但 GDB 会显示当前线程(通常是收到信号的线程)

断点(不指明线程时)对所有线程有效。GDB 默认在全停模式:当一个线程到达断点时,所有线程都会停止。

1
2
3
(gdb) continue      # 所有线程都会继续运行
(gdb) signal SIGINT # 所有线程都会收到信号
(gdb) b main # 所有线程都可能触发这个断点

单独对某个(些)线程执行命令-thread apply

不用切换线程的情况下对其他线程进行操作

1
2
(gdb) thread apply all bt        # 让所有线程执行 bt
(gdb) thread apply 2 5 x/10i $pc # 让线程2和5执行 x/10i $pc

栈帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 切换到指定帧(如 #2)
(gdb) frame 2

# 查看当前帧信息
(gdb) info frame

# 查看当前帧的局部变量
(gdb) info locals

# 查看当前帧的参数
(gdb) info args

# 向上/向下移动一帧
(gdb) up
(gdb) down

其他

中止Ctrl+z

1
2
3
4
5
6
7
8
9
# 方法1:恢复到前台(最常用)
fg

# 方法2:恢复到后台(继续在后台运行)
bg

# 方法3:查看后台作业
jobs
# 输出示例:[1]+ Stopped sudo gdb ./vmlinux_full.dump.fgt.elf

记录record

了解当前情况的方法

看不出来运行到哪里了怎么办?(运行太久了我都怀疑是不是已经跑空了)

在gdb界面按ctrl+c中止一下,程序收到 SIGINT(中断信号),冻结,这时可以进行操作。bt查看栈帧

image-20260307173724265

gdb脚本

使用方法:

1
2
gdb file
(gdb) source solve.py

pwndgb

breakrva:会自动给加基址偏移,在开了地址随机化的题目里可以用这个下断点

比如说基址是0x56555000,下断点,会自动计算0x56555000 + 0x152C = 0x5655652C:

1
2
pwndbg> breakrva 0x152C
Breakpoint 2 at 0x5655652c

rebase:还是加偏移的问题,加上rebase之后地址会自动加上vmmap里的基址

nearpc:会显示当前指令及其前后各 5-10 条指令,并自动标注跳转方向。