跳转到内容

使用GDB

概述

学习汇编的过程中利用GDB了解运行中的状态是必要的,了解实际运作中做了什么事而变成了什么,对于熟悉有巨大的帮助。
另外现在已经有发达的LLM,问ai各种为什么的问题和网络搜索也同样重要。

说明

GDB的作用是将可执行文件反汇编来做分析,是编写汇编过程中良好的调试工具。

使用GDB

对着我们的第一个程序打开gdb

Terminal window
gdb filename

常用指令:

b 设定断点
r 运行
disas 显示当前位置代码
i r 显示当前所有寄存器的内容
i r rax 显示rax寄存器的内容
n 执行高级语言源文件一行不进入函数
ni 执行汇编语言一行不进入函数
s 执行高级语言源文件一行
si 执行汇编语言一行
x/gx 查看内存内容 8bytes hex格式

首先设定断点,linux的程序进入点默认是_start函数
接着执行到中断处,并显示当前位置代码内容

Terminal window
b _start
r
disas

可以看到显示了跟我们写的相似的代码,不过指令是gdb用的风格
另外有些寄存器被改成32位元的e开头而不是64位元的r,是nasm处理后的结果

查看寄存器

首先第一行

=> mov rax, 1

在运行前我们可以先用i r rax看看它原本的值
由于这一段原本就是汇编语言,所以执行下一行可以用si或s都一样
再看看rax改变后的值

查看内存

接着把mov rsi, 0x402000这行运行完,再看看rsi的内容

Terminal window
i r rsi

这行原本是将hello的内存地址放到rsi
也就是0x402000存着Hello World!\n

使用

Terminal window
x/gx 0x402000
x/gx $rsi

存取它的内存地址
x/gx $rsi就像是對指針做*rsi取值,在GDB代入暫存器要以$開頭

w , o l l e H
0x402000: 0x57202c6f6c6c6548

再看看下面单个byte

Terminal window
x/bx $rsi
也可以按enter自动递增
x/bx $rsi+1
...
x/bx $rsi+7

然后把下一段8bytes一起印出来

Terminal window
x/2gx $rsi
w , o l l e H \n! d l r o
0x402000: 0x57202c6f6c6c6548 0x00000a21646c726f

x/的用法是x/数量长度格式 地址取得该内存地址的内容
长度

b 1 byte
h 2 bytes
w 4 bytes
g 8 bytes

单位

x hex
d decimal
u unsign decimal
o octal
t binary
c char
s string
i asm code

记不住没关系,数字类全用x/gx,字串则用x/s
现在试试x/s $rsi,这行会取字串直到出现0 (00, \0, ascii 0, NUL 同义)
但是hello并没有以0结尾,所以在gdb x/s可能显示多余的内容,而实际运行结果并不影响

如何阅读内存

1 byte = 2 hex
一次看两个hex,两两一组,一条8 bytes之中每组hex从右边看到左边,每条8 bytes由左到右上到下
x/gx 一次显示8 bytes也就是8组hex,刚好是一个寄存器的完整大小
以hex格式输出的原因是易于阅读,如果用二进制输出0和1在当前64位环境难以阅读

补充资料

现代cpu的地址总线48 bits,数据总线64 bits
这表示一个指令操作比如mov,最多可以搬动8 bytes大小,刚好是rax长度
不过设计上也有别的cpu数据总线跟寄存器不一致,实际动作可能就要分两次

地址总线48 bits等于6 bytes等于12个hex
理论内存范围是0~0x0000FFFFFFFFFFFF,但这部份完全取决于操作系统
在linux用户可用的就到0x7FFFFFFFFFFF,再往上是内核使用的空间