GDB 调试基本命令

目标

  • GDB基本命令
  • GDB的高级增强插件的使用
  • GDB的多线程多进程调试
  • GDB破解和破解讲解

安装下载GDB

sudo apt-get install gdb

wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.sh | sh

安装GDB插件GDF

依次运行 即可安装成功

  1. //安装gef 插件
  2. wget -q -O- https://github.com/hugsy/gef/raw/master/gef.sh | sh
  3. wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py
  4. echo source ~/.gdbinit-gef.py >> ~/.gdbinit
  5. //安装peda
  6. git clone https://github.com/longld/peda.git ~/peda
  7. //安装gdbinit配置信息
  8. git clone git@github.com:gdbinit/Gdbinit.git
  9. cp Gdbinit/gdbinit ~/.gdbinit

启动 GDB

  1. gdb FileName gdb 加上要调试的文件名
  2. 程序已经运行 在用gdb 挂接上去
  3. 使用 core 文件 启动程序

GDB 基本命令

  • 想要调试的程序 在gcc 中必须加上 -g | l | 显示函数体内容 | | —- | —- | | b | 给程序下断点 | | info breakpoints | 查看当前程序下的断点 | | run | 运行程序 | | s | 步进 进入函数中运行调试 | | n nest | 步出 不进入函数中调试 | | p | 查看变量信息 | | u | 跳出当前循环 | | info lpcals | 查看当前函数中所有局部变量的值 | | set listsize | 更换字体大小 | | x / n、f、u | 查看变量地址 | | bt | 查看函数当前函数是谁调用的 | | info frame | 打印栈所有信息 | | shell | 运行shell 命名 | | finish | 结束当前函数 | | disable | 停止断点 | | enable | 启动断点 | | file | 载入符号表 | | set args | 设置 程序进入参数 | | quit | 退出gdb | | display | 每次运行打印信息 | | commands bnum ~~end | 执行到某个断点执行commands bnum中的命令 | | nexti/ni | 汇编执行一条 | | vmmap | 查看当前程序所有段 地址 | | | |

list l

  1. >>> gdb hello //启动gdb
  2. gdb>> l main //显示main 函数内容
  3. gdb>> list 4 //显示主程序前4行代码
  4. gdb>> l hell:4 //显示主程序前4行代码

break

  1. b 18 //在程序18行下断点
  2. break 18 //在程序18行下断点
  3. b 8 if sum>100 //在sum>100时在第8行停下 条件断点
  4. b hello.c:18 if sum>100 //在hello.c文件中的第18行 当sum>100时在第18行停下 条件断点
  5. b heClass::fun //在 heclass类中的fun函数设置断点 针对虚函数

print

x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。c
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。p/a i
c 按字符格式显示变量。p/c i
f 按浮点数格式显示变量。

  1. p i //查看i变量的值c
  2. p i=100 //把i的值改为100
  3. p *arr@24 //查看arr数组的24个元素

until u

set listsize

  1. gdb>> set listsize20 //设置字体为20

examine x

符代参数:

参数 含c义 助记
n 表示显示内存的长度 个数
f 表示显示的格式,跟print 的格式参数相同 单位
u 表示从当前地址往后请求的字节数 字符

u参数可以用下面的字符来代替

b表示单字节,h表示双字节,w表示四字节,g表示八字节

  1. gdb>> x &i //查看i变量的内存地址
  2. gdb>> x/d &i //查看i变量的内容地址的值为十进制
  3. gdb>> x/10xw 0xffe74 //以十六进制查看10个四字节的地址
  4. gdb>> x/100dw x //查看x数组的100个元素 用10进制四字节表示查看

info proc mappings 查看内存分布

shell

  1. gdb>> shell pwd //在gdcb中运行shell命名

disable / enable c 暂时失效断点

  1. gdb>> info breakpoints //查看当前断点ID号
  2. Num Type Disp Enb Address What
  3. 1 breakpoint keep y 0x000055555555464e in main at hello.c:3
  4. breakpoint already hit 1 time
  5. gdb>> disable 1 //取消1号断点
  6. gdb>> enable
  7. gdb>>quit //退出gdb

导入符号表的方法

  1. 启动后在gdb里面导入

file ./hello.symbol

  1. 在启动gdb时导入

gdb --symbol-hello.symbol --exec-hello

  1. 1.启动后在gdb里面导入
  2. gdb>> file ./hello.symbol
  3. //在当前文件夹下导入hello.symbol 符号表,hello.symbol是用 objcopy --only-keep-debug 生成的
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. 2.在启动gdb时导入
  6. shell#>> gdb --symbol-hello.symbol --exec-hello
  7. --symbol 表示符号表文件
  8. --exec 启动文件

set args

  1. 启动gdb后设置参数
  2. gdb>> set atgs 10 20 30 40 // 传入 4个参数
  3. gdb>> b 20 // 设置断点在20行出
  4. gdb>> bt //查看当前函数调用栈

display

  1. gdb>> display a //每次运行都打印a的值
  2. gdb>> display 0x1024 //每次运行都打印地址的值

commands bnum ~~end

  1. gdb>> b 20 //设置断点
  2. gdb>> info breakpoints //查看断点id
  3. 1 breakpoint keep y 0x00005555555546ea in main at hello.c:15
  4. breakpoint already hit 1 time
  5. gdb>> commands 1 //在1号断点id出设置触发代码
  6. > print a
  7. > shell pwdc
  8. > end //结束输入
  9. geb>> run //运行

确定程序是否存在符号表

readelf -s 确定程序是否存在符号表

  1. readelf -s hello //加上文件名

导出符号表文件

objcopy --only-keep-debug

  1. objcopy --only-keep-debug hello hello.symbol
  2. // 把hello的符号表导出成hello.symbol文件

用Debug程序生成Release 程序

objcopy--strip-debug

  1. objcopy --srtip-debug hello hello.release
  2. // 把Debug版的hello程序脱离符号表生成hello.release程序


GDB高级使用方法

GDB 观察点

是当某一个内存/变量被查看/修改时暂停到这个内存这个地址上

  • watch 地址
  • info watchpoints
  • rwatch
  1. 如果在局部变量于全局变量重名 他会按照就近原则来匹配
  2. 要观察全局就要加上函数名字和双分号
  3. watch i // 在最近的i变量设置内容访问断点
  4. watch main::i // 在main函数中的全局i变量设置内容访问断点
  5. watch *0x10964 // 在0x10964这个内存上设置设置内容访问断点

捕捉点

可以设置捕捉点来捕捉程序运行时的一些事件,如:载入共享库(动态链接库)或是C++的异常。设置捕捉点的格式为:

  • catch <event> | | 作用 | 含义 | | —- | —- | —- | | throw | 一个C++抛出的异常 | 只在C++程序中可以使用 | | catch | 一个C++捕捉到的异常 | 只在C++程序中可以使用 | | exec | 调用系统调用exec时 | exec 表示系统调用 如:在一个进程中启动另一个程序 | | fork | 调用系统调用fork时 | fork 表示创建进程 | | load/load | 载入共享库(动态链接库)时 | load 表示 载入新的库文件时 | | unload 或 unload | 卸载共享库(动态链接库)时 | unload表示 删除载入库时 |
  1. gdb>> throw
  2. gdb>> catch exec
  3. gdb>> load<Wso32.so>

搜索原代码

命令 含义
reverse-search 全部搜索
forward-search 向前面搜索
search 向前面搜索

forward-search <regexp>

没有生成的变量 或 还未调用的函数 是搜索不到的

  1. gdb>> b 20 //设置断点
  2. gdb>> run //启动程序 只有进入堆占的 内容才会被搜索到
  3. gdb>> search fun //查找堆占中的fun

GDB多进程多线程调试

  • 多进程
    进程是一些资源的总和:内存 文件 时间片 协处理器(gpu dsp)
    进程是为了运行程序 进程的创建是复制(fork) 完全复制
    gid 表示进程

  • 多线程
    程序执行单元 线程在进程中 是一直轻量级的进程


测试调试

程序中生成进程 GDB会默认跟随新的进程执行(子进程)

命令 含义
show follow-fork-mode 显示当前跟随进程 是 parent or child
set follow-fork-mode parent 跟随父进程调试
set follow-fork-mode child 跟随子进程调试
set detach-on-fork off 设置未跟随进程暂停运行
info inferiors 查看当前所有运行进程 带*表示正在查看的进程
remmove-inferiors 删除一个进程
add-inferior 复制一个进程
  1. dgb>> set detach-on-fork off //暂停的进程等于已经被杀死 不可以在恢复,可以挂接上去
  2. gdb>> info inferiors //查看当前所有运行进程
  3. gdb>> shell ps -ef | grep 3980 //在系统进程中查找 3980的进程
  4. gdb>> inferior <进程编号> // 跳转到其他进程中去
  5. gdb>> remmove-inferiors <进程编号> //删除一个进程

挂接到另一个进程中去

命令 含义
inferior 切换到其他进程中去
attach 挂接到另一个进程中
  1. gdb>> info inferiors //查看当前所有运行进程
  2. gdb>> attach 3889 //挂接到3889进程上
  • attach
  1. gdb>>
  2. gdb>> attach 38891 //挂接到 38891进程中
  • add-inferior
  1. add-inferior [-copies n] [-exec executable]
  2. //copies 表示复制进程ID executable 表示新开一个进程 文件路径
  3. add-inferior -copies 3 //复制3号进程
  4. add-inferior -exec ./hello //新开hello程序

gdb attach 挂接进程

启动方式 2 通过进程ID挂接到程序中

  1. shell>> gdb attach 38891 //挂接到38891进程上
  2. gdb>>