GDB

基础命令

程序控制

r / run            # 运行程序
c / continue       # 继续执行
n / next           # 单步(不进函数)   / c代码
s / step           # 单步(进函数)    / c代码
ni                 # 执行一条 CPU 指令 / 汇编
si                 # 
finish             # 跑完当前函数
Ctrl + C           # 强制暂停(神器)

断点相关

下断点和删除断点

i b / info b	 #查看所有断点信息
b *0x123456	 	 #地址断点
b fun_name		 #函数断点
b file_name:15	 #给file_name的15行下断点,要有源码才行
b +0x10			 #在程序当前停住的位置下0x10的位置下断点,同样可以-0x10,就是前0x10
break fun if $rdi==5  #条件断点,rdi值为5的时候才断

delete 5 / d 5	删除5号断点,直接delete不接数字删除所有
disable 5	禁用5号断点
enable 5 	启用5号断点

内存断点

watch 0x123456	 	//0x123456地址的数据改变的时候会断
watch a 			//变量a改变的时候会断
info watchpoints 	//查看watch断点信息

捕获断点

info break 			 //catch的断点可以通过i b查看
catch syscall		 //syscall系统调用的时候断住
tcatch syscall		 //syscall系统调用的时候断住,只断一次

除syscall外还可以使用的有:
1)throw: 抛出异常
2)catch: 捕获异常
3)exec: exec被调用
4)fork: fork被调用
5)vfork: vfork被调用
6)load: 加载动态库
7)load libname: 加载名为libname的动态库
8)unload: 卸载动态库
9)unload libname: 卸载名为libname的动态库
10)syscall [args]: 调用系统调用,args可以指定系统调用号,或者系统名称

寄存器相关

regs               # 所有寄存器(pwndbg)
context            # 栈 / 寄存器 / 代码(超级常用)
info registers     # gdb 原生

栈 & 内存

stack
telescope $rsp
x/40gx $rsp

x/gx 0x404000
x/20gx $rbp-0x80

x/s 0x404078

打印查看指令

  • 内存查看指令

    x /nuf 0x123456 
    
    n代表显示几个单元(而不是显示几个字节,后面的u表示一个单元多少个字节)
    u代表一个单元几个字节,b(一个字节),h(俩字节),w(四字节),g(八字节)
    f代表显示数据的格式,f和u的顺序可以互换,也可以只有一个或者不带n,用的时候很灵活
    x 按十六进制格式显示变量。
    d 按十进制格式显示变量。
    u 按十六进制格式显示无符号整型。
    o 按八进制格式显示变量。
    t 按二进制格式显示变量。
    a 按十六进制格式显示变量。
    c 按字符格式显示变量。
    f 按浮点数格式显示变量。
    s 按字符串显示。
    b 按字符显示。
    i 显示汇编指令。
    
    x /10gx 0x123456 	//常用,从0x123456开始每个单元八个字节,十六进制显示是个单元的数据
    x /10xd $rdi 		//从rdi指向的地址向后打印10个单元,每个单元4字节的十进制数
    x /10i 0x123456 	//常用,从0x123456处向后显示十条汇编指令
    
  • 打印指令(print)

    p fun_name 		 //打印fun_name的地址,需要保留符号
    p 0x10-0x08		 //计算0x10-0x08的结果
    p &a 			 //查看变量a的地址
    p *(0x123456)	 //查看0x123456地址的值,注意和x指令的区别,x指令查看地址的值不用星号
    p $rdi 			 //显示rdi寄存器的值,注意和x的区别,这只是显示rdi的值,而不是rdi指向的值
    p *($rdi)		 //显示rdi指向的值
    
  • 打印汇编指令

    disass 0x123456 //显示0x123456前后的汇编指令
    x /10i 			
    
  • 寄存器值查看

    i r eax
    

修改和

修改数据指令set

set $rdi=0x10 //把rdi寄存器的值变为0x10
set $eax=1
set *(0x123456)=0x10 //0x123456地址的值变为0x10,
set args “abc” “def” “gh“//给参数123赋值
set args “python -c ‘print “1234\x7f\xde”’” //使用python给参数赋值不可见字符

查找数据

search rdi //从当前位置向后查包含rdi的指令,返回若干
search -h //查看search帮助
find “hello” //查找hello字符串,pwndbg独有
ropgadget //查找ropgadget,pwndbg独有

查看可写段

vmmap

查找连续字符

32字节大小的字符往往是各种算法所需要的常量,故我们可以在read下断,然后扫描内存:

catch syscall read  
run
continue

find /b 0x400000, 0x800000, 'e', '4', 'Y', '8' //遇到不可访问内存会停止
search <指定字符串>

如果不知道字符串的内容,在pwndbg中使用下面命令:

import gdb, re
inf = gdb.selected_inferior()
pat = re.compile(rb'[A-Za-z0-9]{32}')

start = 0x400000
end   = 0x800000
step  = 0x1000

for base in range(start, end, step):
    try:
        data = inf.read_memory(base, step)
    except gdb.MemoryError:
        continue
    for m in pat.finditer(data):
        print(hex(base + m.start()), m.group().decode('ascii', 'ignore'))
end

image.png

需要注意的是,这个命令依然会跳过不可读内存

调用栈

寻找某个函数的返回地址

调用链: game()->scanf()

bt             #查看调用栈
frame 4        #切换到第四帧(scanf的帧)
info frame     #看rip at 0x7fffffffdc28(最后一行),这是当前scanf函数返回地址在栈上的位置。可以把他修改为其他指令的地址来劫持,saved rip=0x4015a存储的是call scanf的下一个汇编指令的地址。
x/gx $rsp      #查看当前栈帧的栈顶,通常是当前函数的返回地址
telescope $rsp #看的更清楚

image-20260124234910094

image-20260124235023734

上面那个图是game()函数内的命令。

接下来我们返回到game函数的栈帧

p/x $rbp-0x60	        #查看game函数内的一个变量的地址
p/x (&rbp-0x60)+(-3)*8  

image-20260124235254979

反汇编

寻找某个函数的返回地址

disas game     #反汇编这个函数
b *0x401af     #断点在caller命令下一个地址
c	
p/x $rbp       #查看caller的函数的返回地址
x/gx $rbp-0x78 #查看_QWORD v6[12]; // [rsp+10h] [rbp-60h] BYREF这种变量的地址位置

disassemble/disass命令

disassemble

  • disassemble [Function]

    ​ 指定要反汇编的函数。如果指定,反汇编命令将产生整个函数的反汇编输出。

    (gdb) disassemble main
    
  • isassemble [Address]

    ​ 指定要反汇编的地址。请注意,当仅指定一个地址时,此命令将反汇编包含给定地址的整个函数,包括其上方的指令。

    (gdb) disassemble 0x00000000004008fd
    
  • disassemble [Start],[End]

    指定要反汇编的起始地址和结束地址。如果使用这种形式,该命令将不会反汇编整个函数,而只会反汇编起始地址和结束地址之间的指令。

    (gdb) disassemble 0x000000000040068a,0x00000000004006ac
    
    • disassemble [Function],+[Length]
      disassemble [Address],+[Length]

      指定从给定地址或函数开始反汇编的字节数,例如:

    (gdb) disassemble main, +10
    

libc

指定libc

(gdb) set environment LD_PRELOAD=./libc-2.xx.so
(gdb) run

debuginfod 服务

pwndbg 是可以在有调试符号的情况下显示源码,在程序崩溃时会停在崩溃的地方。

debuginfod是 elfutils项目提供的“调试符号分发服务”,它通过HTTP API把debuginfo、可执行文件和源代码按需推送给调试器,省去手动安装 *.debuginfo包的麻烦。

# 本地先清掉旧缓存,防止 debuginfod 不再拉取
rm -rf ~/.cache/debuginfod_client/$(readelf -n cook|grep -Po 'Build ID: \K.*')

# 启动 gdb 并令程序崩溃
DEBUGINFOD_URLS="http://<靶机>:8000" gdb ./cook
(gdb) r          # 不加参数,程序崩溃自动停到源码行

调试c语言代码

创建文件

gcc -g -O0 -Wall main.c -o main
posted @ 2026-03-14 15:45  MillionMind  阅读(52)  评论(0)    收藏  举报