GDB 支持的语言 Ada,Assembly,C,C++,D,Fortran,Haskell,Go,Objective-C,OpenCL C,Modula-2,Pascal,Rust。
gdb实现原理 调试器与进程建立联系 使用linux中ptrace()系统调用
使用时会建立ptracer和ptracee关系,从而进行交互。
软断点 gdb通过对代码进行修改,将目标代码修改为0xcc程序码,触发一个调试终端,发送sigtrap信号然后程序暂停执行,调试器接收到信号后,把0xcc恢复,然后将pc指针回退一步,等待进一步指令,如果不取消断点,执行完当前命令后,Tracer又会重新插入
基础使用 语法规则
1 !{command}#在进入路径处执行命令,但不能使用cd等改变gdb目标地址
gdb启动程序 gdb要跟踪程序存在三种方法
gdb中加载 优点
简单调试易于使用
可以在main或_start函数前下断点
缺点
默认未开启ASLR随机化
启动行为存在差异
内存布局存在差异
环境变量纯在差异
方法
打开gdb时设置参数
1 2 gdb -q "./pwn" #无参数 gdb -ex "run" --args 【文件名】 【参数1】 【参数2】...
这里延申一下, -ex指令可以实现进入后执行引号内的命令
进入gdb中进行调用
设置程序文件
设置参数
attach附加进程 优点
环境和远程靶机相似性高
默认开启ASLR地址随机化
启动行为基本一致
环境变量基本一致
内存布局基本一致
调试结束后不影响程序进行
缺点 程序交互前无法下断点
方法
gdbserver用过网络协议调试 优点
跨机器调试
使用docker启动gdbserver调试
与IDA Pro等联动调试
缺点
程序交互麻烦
程序启动不方便
方法 靶机(gdbserver)
主机(gdb)
1 target remote:【IP】:【port】
默认监听0.0.0.0
target remote默认连接localhost
退出和日志 退出GDB 直接关闭进程退出
关闭进程
断开附加
暂停(终止)进程查看面板
记录日志 开启记录设置
关闭
面板介绍
调试信息 载入的源文件·
这一段是固定的
它说明了各种数据对应的颜色,方便分别下方数据
黄色:栈中数据(栈段)
蓝色:堆中数据
红色:代码段数据
紫色:数据段数据
下划线:可读可写可执行的数据
白色:只读
寄存器
向右箭头代表可以继续解引用的数据,想左代表不能继续解引用
汇编
程序运行到当前的地址<对应当前函数编译地址>
反汇编指令
绿色代表执行到的位置
栈帧信息
相对于sp的偏移/8(序号): 16进制的偏移量 (默认为+)
相对于rbp的偏移
bp的地址
数据
调用循序(系统栈)
上下之间的关系是callee->caller的关系
最下方是最开始的函数,最上方为当前函数
点 断点 指定程序在执行过程中的某个特定点暂停执行。调试器可以检测此时程序状态,包括变量的值执行路径等。
1 break {function} 或 b在函数function处下断点,会搜索所有符号并断点
1 break X.c {lines} #在源码X.c文件的lines处下断点
1 tbreak {express} 或 tb #设置临时断点
1 rbreak {regexpr} 或rb #设置满足正则匹配的所有断点
1 hbreak {expr} 或 hb #使用硬件断点
1 condition {id} {expr} #设置id条件断点,只要条件生效时才发生
1 break {exp} if {cond} #设置条件断点,只要满足条件时才方式
1 info breakpoints 或 info b #显示所有断点/观察点/捕获点
1 delete {id} #删除id的断点/观察点/捕获点
1 clear {expr} #根据表达式清空断点/观察点/捕获点
1 enable {id} #启用id断点/观察点/捕获点
1 disable {id} #关闭id断点/观察点/捕获点
1 ignore {id} {count} #忽略id断点/观察点/捕获点count次
观察点 监视数据读写事件,当对应事件发生时,程序停止运行
1 watch *{address} 或 w #当地址对应地址数据发生改变或修改时,程序中断
1 watch *(int*) {address} #监视四字节读写
1 watch *(long long int*) {address} #监视四字节读写
1 info watchpoints #查看所有观察点/断点/捕获点
捕获点 监视程序的系统调用、信号、异常事件
1 catch exec #设置捕获点,捕获系统调用
1 catch fork/vfork #设置捕获fork/vfork系统调用
1 catch syscall #设置调用号为syscall的系统调用
1 catch signal {name/id} #捕获对应id/name的信号
信号和核心转储 信号 进程间的通信方式,用于查询的状态、信息的命令,gdb可以截获或者伪造,命令终止的根本原理就是调试器伪造终止信号发送给进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 SIGHUP 1 终止进程 终端线路挂断 SIGINT 2 终止进程 中断进程 Ctrl+C SIGQUIT 3 建立CORE文件终止进程,并且生成core文件 Ctrl+\ SIGILL 4 建立CORE文件,非法指令 SIGTRAP 5 建立CORE文件,跟踪自陷 SIGABRT 6 SIGIOT 6 建立CORE文件,执行I/O自陷 SIGBUS 7 建立CORE文件,总线错误 SIGFPE 8 建立CORE文件,浮点异常 SIGKILL 9 终止进程 杀死进程 SIGUSR1 10 终止进程 用户定义信号1 SIGSEGV 11 建立CORE文件,段非法错误 SIGUSR2 12 终止进程 用户定义信号2 SIGPIPE 13 终止进程 向一个没有读进程的管道写数据 SIGALARM 14 终止进程 计时器到时 SIGTERM 15 终止进程 软件终止信号 SIGSTKFLT 16 SIGCLD SIGCHLD SIGCHLD 17 忽略信号 当子进程停止或退出时通知父进程 SIGCONT 18 忽略信号 继续执行一个停止的进程 SIGSTOP 19 停止进程 非终端来的停止信号 SIGTSTP 20 停止进程 终端来的停止信号 Ctrl+Z SIGTTIN 21 停止进程 后台进程读终端 SIGTTOU 22 停止进程 后台进程写终端 SIGURG 23 忽略信号 I/O紧急信号 SIGXCPU 24 终止进程 CPU时限超时 SIGXFSZ 25 终止进程 文件长度过长 SIGVTALRM 26 终止进程 虚拟计时器到时 SIGPROF 27 终止进程 统计分布图用计时器到时 SIGWINCH 28 忽略信号 窗口大小发生变化 SIGPOLL SIGIO SIGIO 29 忽略信号 描述符上可以进行I/O SIGPWR 30 SIGSYS 31 SIGUNUSED 31
核心转储 程序崩溃时生成的文件,这个文件包含了程序崩溃时内存镜像和寄存器状态。
GDB可以使用核心转储文件来后续调试分析。
开启核心转储 1 ulimit -c 【字节大小】# 关闭设置为0,unlimited为无限制
这个命令只会在当前终端有效,退出终端或者打开一个新的终端时是无效的 。因此可以在将上述配置加入到 /etc/profile
中可以持续生效
分析核心转储文件 运行程序出错后会在程序中生成名为core的文件
在调试gdb时加参数
即可分析核心存储文件,注意,分析核心转储时一定要运行原先程序文件
调试 调试过程中使用的命令
启动类 1 run 【参数1】【参数2】【参数3】# 运行当前程序,若不设置参数则使用set args设置的参数
1 start 【参数1】【参数2】【参数3】#运行当前程序,在main处终止
1 starti 【参数1】【参数2】【参数3】 #运行当前程序,在_start处终止
运行类 步入 1 step {count} 或 s {count} #执行一行源码,会进入函数中,count为步数,默认为一
这里的步数不能简单的理解为步长,因为它遇见函数不是越过而是进入
1 stepi {cout} 或 si {count} #执行一行汇编,会进入函数中,count为步数,默认为一
步过 1 next {count} 或 n {count} #执行一行源码,不会会进入函数中,count为步数,默认为一
1 nexti {cout} 或 ni {count} #执行一行汇编,不会进入函数中,count为步数,默认为一
多次执行 1 until 或 u #快速运行完当前的循环体,并运行至循环体外停止。
断点命中后继续执行
观测类 查看设置 1 procinfo #查看当前进程信息,以及设置设置
1 show directories #显示当前源码搜索目录
1 2 list 或 l #列处当前pc处源码,默认前5行 list *{address} 查看地址处源码
数据
1 stack {count} #查看栈信息,默认长度0x40
1 p *(指针类型) {address} #查看设置地址以规定类型的值,默认类型(void)
1 【类型】/count {address} #从address处以该类型查看count个数据
Format letters are o (octal), x (hex), d (decimal), u (unsigned decimal), t (binary), f (float), a (address), i (instruction), c (char), s (string) and z (hex, zero padded on the left)
Size letters are b (byte), h (halfword), w (word), g (giant, 8 bytes)
设置类 1 file {filepath} #加载可执行文件,会从当前路径和
1 directory{dirname} 或 dir #添加dirname为源码搜索目录,没参数复位源码搜索路径
1 core-file {filepath} #加载core文件用于调试
1 exec-file {object} #制定与调试可执行文件
1 add-symbol-file{filepath} -s {section-name} {address}添加调试文件并指定段名和加载地址,用于多核
多线程 查看可切换调试的线程:info threads
(2)切换调试的线程:thread 线程id
(3)只运行当前线程:set scheduler-locking on
(4)运行全部的线程:set scheduler-locking off
(5)指定某线程执行某gdb命令:thread apply 线程id gdb_cmd
(6)全部的线程执行某gdb命令:thread apply all gdb_cmd
多进程和fork 方法1:attach pid 如果fork后的命令存在暂停或其他等待,可以考虑使用attach pid 来再次attach
方法2:detach-on-fork 我们可以通过命令
来查看现在的模式,如果返回值为off则fork后新的子进程不会执行,而是等待,否则继续执行,当然我们也可以通过以下命令来更改设置
1 set detach-on-fork off/on
设置完后,经过fork容然默认进入子进程,但此时的父进程是attach的状态,我们可以通过命令来查看我们现在存在的进程的进程
然后通过inferior命令进行切换
这样后即可调试父进程
方法3: 如果我们只想针对父fork进行调试 的情况下,我们可以在fork前使用
1 set follow-fork-mode [parent|child|ask]
来进行设置通过设立来确定我们fork后将attach哪一个进程
gdb-multiarch调试不同架构 当gdb去调试qemu或者其他不同架构设备时,直接调试是无法成功的,需要使用gdb-multiarch(需要下载)来进行调试
使用方法
设置架构
1 set architecture arm # 设置架构
连接目标机器
引用参考
roderick01师傅的【快来pwn1pwn】视频
Linux下signal信号汇总 - 撒欢 - 博客园 (cnblogs.com)
Linux系统调试篇——核心转储(core dump) - 知乎 (zhihu.com)