补充知识
这里参考了一篇极好的知乎回答: 我是链接
汇编语言需要掌握的知识点包括:
- 寄存器,包括栈寄存器rsp, rip, rbp;赋值寄存器:rdi、rsi等;返回值寄存器rax、rdx等。
- 赋值指令 mov, lea,移位指令sal, sar。
- 判断指令,cmp, test, 跳跃指令,je, jle, jne。以及构成条件语句和循环语句的方法。
- 区分寄存器存储的是值还是地址,()为解地址符号,即得到内存地址中的数。
- 入栈,出栈指令push, pop,以及出栈入栈的原因。
常见指令
mov指令是赋值指令,即a=b等赋值操作:注意到对某寄存器rbp而言:rbp 表示取寄存器的值,(rbp)表示以rbp的值为地址在内存中取值,也就是所谓的指针解引用,根据指针地址取值。
lea是“load effective address”的缩写, lea指令可以用来将一个内存地址直接赋给目的操作数,例如:lea eax,[ebx+8]就是将ebx+8这个地址值直接赋给eax,而不解ebx+8的地址。mov指令则相反,例如:mov eax,[ebx+8]则是把内存地址为ebx+8处的数据赋给eax。
个人的理解:lea eax,[ebx+8]表示,不改变eax的值,而直接将eax的地址变为eax+8;而mov eax,[ebx+8]表示将ebx+8这个地址上的数据给eax。
左移指令包含sal和shl,这两条指令的作用是相同的,空出来的位用0填充。右移指令将操作数的bit位向右移动n位,sar执行算术移位(填上符号位),而shr执行逻辑移位(填上0).移位操作的目的操作数可以是一个寄存器或是一个存储器位置。
前期准备
将相关test.c文件使用gcc编译器编译生成可执行文件,同时注意仅仅生成可执行文件是无法进行调试的,因为使用 GDB 调试某个可执行文件,该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等),而上面生成的 test.exe 则没有。
所以使用-g
命令,如
1 | gcc test.c -o test.exe -g |
接下来输入gdb test.exe
启动。输入--silent
可以消掉前面免责条款
调试指令 | 作 用 |
---|---|
(gdb) break xxx (gdb) b xxx | 在源代码指定的某一行设置断点,其中 xxx 用于指定具体打断点的位置。 |
(gdb) run (gdb) r | 执行被调试的程序,其会自动在第一个断点处暂停执行。 |
(gdb) continue (gdb) c | 当程序在某一断点处停止运行后,使用该指令可以继续执行,直至遇到下一个断点或者程序结束。 |
(gdb) next (gdb) n | 令程序一行代码一行代码的执行。 |
(gdb) print xxx (gdb) p xxx | 打印指定变量的值,其中 xxx 指的就是某一变量名。 |
(gdb) list (gdb) l | 显示源程序代码的内容,包括各行代码所在的行号。 |
(gdb) quit (gdb) q | 终止调试。 |
l
显示带行号的源代码,默认显示10行,按回车继续显示。
(gdb)b 7
<–设置断点,在第七行处(gdb)r
<–运行程序,遇到断点处停止
(gdb)p n
<–表示查看此刻n的值(gdb)b 12
<–第十二行为断点(gdb)c
<–继续(gdb)p n
此时显示$3=0x65表示十六进制六十五,也就是十进制的101
接下来分析另一段代码
首先通过(gdb)break func
在函数处设置断点,**gdb还支持if判断设置断点如(gdb)break 7 if n==6
**,然后开始执行。
其中rsp寄存器,stack pointer, 它存储的值永远是栈顶的地址,所以它又被叫做栈顶指针。我们可以用(%rsp)来获取栈顶存储的值,通过a(%rsp), 其中a是任何一个整数,来获取地址是rsp存储的值加a处的内存单元的值。比如说,2(%rsp)就是栈顶上方(逻辑地址增大方向)2个字节处的值,-2(%rsp)就是栈顶下方(逻辑地址减小方向)2个字节处的值。
以r开头的寄存器如rbp、rsp一般表示64位寄存器,而以e开头的寄存器如ebp、esp表示32位寄存器。一般不影响比较,即把rbp作用等同于ebp即可。
rbp寄存器:为基指针(Base Pointer)寄存器,通过它减去一定的偏移值,来访问栈中的元素;
rax、rdx常作为函数返回值使用
rdi、rsi、rdx、rcx、r8、r9等寄存器常用于存放函数参数
rip作为指令指针存储着CPU下一条要执行的指令的地址
一旦CPU读取一条指令,rip会自动指向下一条指令(存储下一条指令的地址)
如其中[rbp-0x8]就作为sum参数,被赋值了0
接下来逐条使用(gdb)print i
,(gdb)print n
,(gdb)next
可以看到func函数中在底层的运行情况。
gdb调试,难得不是汇编语言,难得是逻辑分析QAQ
今天先到这里,明天继续。
- 本文作者: Isabella
- 本文链接: https://username.github.io/2021/01/30/gdb调试/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!