GDB调试器教程(一):热身游戏

本系列收录于基础理论专栏。


启动和退出GDB

GDB(GNU Project Debugger)几乎适用于所有类Unix系统,小巧方便且不失功能强大,Linux/Unix程序员经常用它来调试程序。

这里写图片描述

总的来说有几下几种方法启动GDB

  • gdb(不加任何参数),如果不希望打印授权信息加上-silent参数

  • gdb 可执行文件

  • gdb 可执行文件的core文件

  • gdb 正在运行的进程之pid

要退出GDB则有两种方法

  • quit

  • ctrl+d


命令表(记忆)

  • 断点相关命令
GDB命令 参数 意义 常用示例
break 地址 下断点,可简写为b。地址类型包括:函数名、源文件行号、*内存地址 break main、break 12、break *0x08048373
watch 表达式 表达式的值被改变程序将立即停止运行 watch *((int*)0x80d1ba8)
clear 地址 和break相反,清除指定地址上的断点 clear main、clear 12、clear *0x08048373
info break 显示断点信息,包括所有断点的编号、种类、使能状态、地址以及位置 info break
disable 断点编号 禁用一个断点 disable 1
enable 断点编号 启用一个被禁用的断点 enable 1
delete 断点编号 删除一个断点,可简写为d delete 1
  • 执行相关命令
GDB命令 参数 意义 常用示例
run 命令行参数 运行程序,可简写为r run vuln
attach 进程号 调试已运行的进程 attach 1022
continue 次数(可选) 继续执行,可简写为c c 4
next 次数(可选) 单步(不进入函数调用),可简写为n n 4
step 次数(可选) 单步(进入函数调用),可简写为s s 4
unitl 源文件行号 执行到执行地址后中断,可简写为u u 18
finish - 运行当前函数直到函数退出 -
return - 立即退出当前函数 -
  • 信息查看相关命令
GDB命令 参数 意义 常用示例
info reg、break、file、args、frame、functions 显示各种信息,info可简写为i info reg
backtrace 帧的数目 显示当前函数调用栈信息,可简写为bt bt
print /f exp,其中f表示修饰,exp为表达式,print可简写为p 显示表达式的值,格式有:x(十六进制)、c(字符)等,print可简写为p p/c 0x41、p/x 1024、p str、p/x $eax
x /nfu addr,其中n表示个数、f表示格式、u表示单元大小,如果没有制定地址则接着上一次x命令显示之后的地址 显示指定地址内容,格式有:x(十六进制)、s(字符串)、i(指令)等,单元大小有b、h、w、g、b为一个字节,依次比前一个大一倍 x/4i $pc、x/16xb $sp、x/s *(argv+1)、x/s 0xbffffc52
list 行号、函数或地址 如果调试的是带符号编译的程序,那么list命令可以列出程序源码,list可简写为l l file.c:19
disass 函数名 反汇编指定函数,默认为当前函数 disass main
  • 其它常用命令
GDB命令 参数 意义 常用示例
set set的参数非常多,具体参见help set 设置值 set var=4、set {int}0xbffffc52=50、set {int}($esp+4)=$eip
shell 外部shell命令 执行外部shell命令 shell ps -ef

比较常用的命令如pxdisassbreaksinicfinishset,美中不足的是GDB没有内置搜索内存的功能,我们可以自定义一个宏脚本并保存在用户目录的.gdbinit文件里即可。


[已解决] 为什么我的Mac无法正常使用GDB

这是因为 Darwin 内核在你没有特殊权限的情况下,不允许调试其它进程。调试某个进程,意味着你对这个进程有完全的控制权限,所以为了防止被恶意利用,它是默认禁止的。允许 gdb 控制其它进程最好的方法就是用系统信任的证书对它进行签名。

首先打开钥匙串app

open /Applications/Utilities/Keychain\ Access.app

接着按照下图创建自签名根证书

这里写图片描述

接下来录一路点击确定,直到出现下面的画面

这里写图片描述

要求你输入用户的账号密码

这里写图片描述

回到起初打开的钥匙串访问程序,找到新建的证书选择永远信任

这里写图片描述

最后输入命令并重启

codesign -s gdb_codesign gdb && reboot

从最简单的程序开始吧

写一个简单的C语言源程序

这里写图片描述

使用gcc编译器将源文件编译成可执行文件。加-g选项是为了便于调试,否则之后在导入gdb时会找不到调试标记(debugging symbol)。

概选项的作用主要有二:

  • 创建符号表,符号表包含了程序中使用的变量名称的列表

  • 关闭所有的优化机制,以便程序执行过程中严格按照原来的C代码进行

gcc -g cc.c -o cc

启动GDB

gdb

导入相应的可执行文件,gdb进行读取,出现reading…done则表明成功读取。

file cc     # 既可以跟相对路径,也可以跟绝对路径

不妨查看下源文件(编译前)的行号信息。

shell nl cc.c   # 这样其实麻烦了,可以直接敲list或者简写l

这里写图片描述

我们在第一个打印后设置断点。注意断点是程序暂停的那一行,程序中止时是没有运行到断点所在行的(运行到断点所在行的上一行)。

break 7     # 7是源文件的行号

这里写图片描述

然后运行该文件,直到第一个断点处程序暂停。

run

这里写图片描述

如果要继续运行,使用continue命令

continue

这里写图片描述

根据提示信息,程序已经正常退出。而本次程序运行设置的断点也被自行销毁,下次必须重新设置断点。

还是上面的那个程序,下面让我们放慢节奏看看它的内部细节。首先在源程序的第七行设置断点,然后运行它。


你的第二个调试程序

我加上了循环、函数调用的概念。首先还是用vim写一个C源文件。很容易的,为了和后面的调试形成参照,这里我想不加修饰的将该文件运行看看。

这里写图片描述

接着启动gdb,这次你懂得多了可以直接敲

gdb cc

接着查看带标号的程序源码。如果你使用不加参数的list命令,它将在第一个遇到的函数调用处中止。(这省却了对源文件的维护)

这里写图片描述

运行一遍该程序,没有问题。

这里写图片描述

但是我们的目的不在此,我们是要用它来学习的,所以即使程序运行没问题,还是下个断点,就定在源文件的第11、12、16行好了。

这里写图片描述

看一下断点信息。

这里写图片描述

然后运行并调试程序,当遇到第一个断点时程序会自动停下。这里也就是在第10行运行结束的位置停下了,敲p a来查看变量a的当前值,显示为1。

这里写图片描述

因为我之前在12行设置了断点,所以这时候直接c继续运行程序即可。如果没有设置断点,也可以敲n来单点步入(不进入函数)。

这里写图片描述

由于停在11行,而实际上第11行尚未执行,所以打印变量a的值仍然是1。注意到循环变量i的值这时候已经被初始化为0了。

这里写图片描述

这里我们使用命令s进行单点步入(进入函数)。随着循环变量的递增,可以清晰的看到随着i的变化a也发生着变化。这里的代码结构是循环套函数,如果你使用两个连续的s命令,将进入调用的函数内部,而这里只是进入了循环的内部。

这里写图片描述

我们选择继续运行,直到遇到下一个断点(位于第16行)。

这里写图片描述

注意到此时之前的循环变量的值还没有销毁,等于3。

这里写图片描述

好吧,继续运行直到程序正常退出。结束。

这里写图片描述

展开阅读全文

没有更多推荐了,返回首页