gdb使用技巧
GDB调试工具
0x00 介绍
gdb是用来debug c/c++程序的工具,用来检测程序的逻辑错误
要想使用必须在gcc编译的时候加上-g
选项
加了-g
之后会生成调试表
GDB的三种调试方式如下:
- 运行并调试一个新进程:
- 运行GDB, 并通过命令行或
file
命令指定目标程序; - 输入
run
命令, GDB将进行以下操作:- 通过
fork()
系统调用创建一个子进程; - 在新创建的子进程中执行操作
ptrace(PTRACE_TRACEME, 0, 0, 0)
; - 在子进程中通过
execv()
系统调用加载用户指定的可执行文件。
- 通过
- 运行GDB, 并通过命令行或
- attach 并调试一个已经运行的进程:
- 用户确定需要进行调试的进程PID
- 运行GDB, 输入
attach <pid>
, 或者gdb -p <pid>
- GDB会对其执行操作
ptrace(PTRACE_ATTACH, 0, 0, 0)
;
- 远程调试目标机上新创建的进程:
- gdb 运行在调试机上,gdbserver 运行在被调试机上,两者之间的通信数据格式为GDB串行协议(Remote Serial Protocol)定义;
- RSP协议数据基本格式为
$..........#xx
; - gdbserver的启动方式相当于第一种GDB调试方式.
note:
GDB attach到一个进程时,可能会出现权限问题
1 | gef➤ attach 1 |
这是由于内核参数 ptrace_scope=1
被设置了,因此普通用户没有权限对其他进程进行 attach 操作,而 root 权限启动GDB不会受权限所限制,但更好的解决方案是关闭它:
1 | ╰──➤ cat /proc/sys/kernel/yama/ptrace_scope |
0x01 gdb简单使用
e.g. 调试hello程序的步骤
编译生成ELF文件 并加上
-g
选项1
gcc hello.c -o hello -g
使用gdb进行调试
1
2
3gdb hello
## 或者使用tui界面的gdb
gdb -tui hello
note: 若编译时忘记加-g
选项而且已经在gdb中打开了一个程序,可以重新编译之后在用file
命令加载可执行程序
gdb中常用命令如下表
命令 | 简写 | 作用 | 例 |
---|---|---|---|
help | h | 按模块列出命令类 | |
help class | 查看某一类型的具体命令 | ||
list | l | 查看代码,可跟行号或函数名 | |
quit | q | 退出gdb | |
run | r | 全速运行程序 | |
run arg[1] arg[2] | 调试时命令行传参 | run aa bb cc | |
start | 单步执行,运行程序,停在第一行执行语句 | ||
next | n | 逐过程执行代码,跳到下一行代码继续执行 | |
nexti | ni | 逐过程执行机器指令,跳到下一行代码继续执行 | |
step | s | 逐语句执行代码,遇见函数,跳到函数内执行 | |
stepi | si | 逐语句执行机器指令,遇见函数,跳到函数内执行 | |
backtrace | bt | 查看函数的调用栈帧和层级关系 | |
info | i | 查看GDB内部局部变量的的数值 | info breakpoints //(可以简写成 i b) 查看断点信息表info locals //查看局部变量 |
frame | f | 切换函数的栈帧 | |
finish | fin | 跳出当前循环(跳出当前栈) | |
util |
运行到指定行结束 | ||
return |
结束当前函数,返回到函数调用点, 并决定返回值 | return 233 |
|
set | 设置变量的值 | set var n=100 | |
set args | 设置main函数的参数值(在start、run之前) | set args aa bb cc | |
p | 打印变量和地址 | p *数组名 @10 // 列出数组前10个元素p 数组名[n] @m // 从下标n开始向后打印m个元素 |
|
print type | ptype | 查看变量的类型 | |
break | b | 设置断点,可根据行号和函数名 | b 20 if i = 5 设置条件断点break * 0x1234 在地址0x1234处设置断点 |
delete | d | 删除断点 | d breakpoints NUM |
display | 设置观察变量 | display testvar | |
undisplay | 取消观察变量 | undisplay 观察变量的编号 | |
continue | c | 继续全速运行剩下的代码 | |
enable breakpoints | 启用断点 | ||
disable breakpoints | 禁用断点 | ||
x | 查看内存 | x /20xw 显示20个单元,16进制,4字节每单元 | |
watch | 被设置观察点的变量发生修改时,打印显示 | ||
core file | ulimit -c 1024 开启core文件, 调试时 gdb a.out core |
||
tui enable/disable | 打开关闭tui界面 | ||
disas | 反汇编当前函数 | disas foo 反汇编函数foo |
|
info watch | i watch | 显示观察点 | |
info frame | 有关栈帧的信息 | ||
info registers | 所有寄存器的值 |
其他一些常用命令
set disassembly-flavor intel
转换为intel格式的汇编set disassembly-flavor att
转换为att格式的汇编set solib-search-path <path>
设置动态库路径set solib-absolute-prefix <path>
设置绝对路径系统动态库路径layout asm
显示汇编窗口layout regs
显示寄存器窗口layout src
显示源码窗口
多线程调试
1 | ## 列出所有线程 |
多进程调试
1 | ## 查看当前所有进程 |
反向调试(gdb7.0以上)
1 | ## 开启记录 |
断点的储存与恢复
这个文件本质上存的是命令列表
同理,也可以将一些命令写入文件,用source
命令加载
类似批处理
1 | ## 存储断点到文件 |
可以把一些设置写在 ~/.gdbinit
中,以便让gdb下次启动时自动加载你的设置
0x02 gdb增强
gef
是一组用于 X86、ARM、MIPS、PowerPC
和 SPARC
的命令集,可让 GDB
为漏洞利用开发人员提供更多更酷炫的功能。它主要由漏洞利用开发人员和逆向工程师使用,使用 Python API 为 GDB 提供附加功能,以协助动态分析和漏洞利用开发过程.
gef
的主要功能包括:
- 一个
GDB
脚本(用python
写的,有1w多行代码) - 完全与操作系统无关,无依赖
- 提供种类繁多的命令来彻底改变您在
GDB
中的体验。 - 完整的
python3
支持 - 提供
GDB python API
来自定义插件
安装
gef
是一个开箱即用的工具,可以通过下面命令通过脚本安装
1 | bash -c "$(curl -fsSL http://gef.blah.cat/sh)" |
也可以通过git
安装
1 | git clone https://github.com/hugsy/gef.git |
为了更好的体验,可以安装一些依赖(可选)
1 | pip3 install capstone unicorn keystone-engine ropper |
只需确保您使用的pip
是与编译 GDB
时所用的 Python
版本相对应的版本。
用户写基于gef的插件
GEF
的构建还为外部脚本提供了坚实的基础。该存储库gef-extras
是一个开放的存储库,任何人都可以通过 GEF
的 API
自由提交自己的命令来扩展 GDB
。
1 | ## via the install script |
gef使用小技巧
- 将输出重定向到一个tty
若是从tmux
会话中启动的gdb
可以用下面命令 自动分屏指定tty
1 | (gdb) gef tmux-setup |
更加通用的办法是 手工指定tty
首先开一个新终端并查看其tty
号
1 | $ tty |
然后在配置gef
的context.redirect
选项为上一步的输出结果
1 | gef➤ gef config context.redirect /dev/pts/0 |