GDB调试基础
1. 启动GDB
|
|
如上图所示,gdb启动后,默认进入gdb的命令行模式,并打开了一个二进制文件,通过run指令进行了运行。
输入start指令,进入gdb的调试模式。
- STACK 含义:栈(Stack) 描述:栈是程序运行时用于存储局部变量、函数调用信息和返回地址的内存区域。每当函数被调用时,栈会存储当前函数的局部变量、参数和返回地址等信息。栈通常是向下增长的(即栈顶地址减小)。 用途:在调试时,STACK 区域显示的是当前栈的内容,包括函数调用栈、局部变量等。
- HEAP 含义:堆(Heap) 描述:堆是程序动态分配内存的地方(比如通过 malloc、calloc 等动态内存分配函数)。堆的内存是由程序员显式管理的,可以进行手动分配和释放。堆的分配方式通常是向上增长的。 用途:HEAP 区域会显示当前程序使用的动态分配的内存,包括由 malloc、free 等操作分配的内存块。在调试时,这有助于你检查内存泄漏或溢出等问题。
- CODE 含义:代码段(Code Segment) 描述:代码段是存放程序指令(机器代码)的区域。这部分内存是只读的,程序的执行代码存储在这里。一般来说,代码段是只读的,以防止程序在运行时修改其自身的指令。 用途:CODE 区域显示程序的机器指令,可以通过它来检查正在执行的代码,反汇编操作通常会展示这个区域。
- DATA 含义:数据段(Data Segment) 描述:数据段是用来存放程序的全局变量和静态变量的地方。这些变量在程序执行期间保持固定的内存位置。数据段通常包括初始化的数据(如全局变量的初始值)和未初始化的数据(例如 BSS 段)。 用途:DATA 区域显示程序的全局变量和静态变量的内存布局,帮助调试全局状态和数据。
- WX 含义:可写且可执行(Writable and Executable) 描述:标记为 WX 的内存区域是既可写又可执行的。这种内存区域通常是存在漏洞的地方,例如程序可能会被攻击者通过写入一些恶意代码并随后执行它,导致代码注入攻击。 用途:如果程序中有 WX 区域,它可能会被用来检查是否存在不当的内存访问,尤其是在涉及堆栈溢出、代码注入等漏洞时。
- RODATA 含义:只读数据段(Read-Only Data Segment) 描述:只读数据段通常存储程序中常量数据(如字符串常量)。这些数据在程序运行过程中不会被修改,因此通常放在只读内存区域。 用途:RODATA 区域显示的是程序的只读数据,例如 const 字符串、常量数组等。 总结 这些内存区域分别代表了程序运行时不同类型的内存空间: STACK:局部变量、函数调用栈 HEAP:动态分配的内存 CODE:程序的执行代码 DATA:全局变量和静态变量 WX:既可写又可执行的内存区域(通常存在漏洞时) RODATA:只读数据(例如常量)
各部分分支:
寄存器部分
反编译部分,显示汇编指令
栈空间分配
栈调用查看
常用gdb指令:
基本指令
- help //查看帮助
- q //退出gdb
运行指令
- n //单步步过,可以理解为下一步,遇到调用不跟进,相当于step over,源码层面的一步
- ni //常用,同n,汇编层面的一步
- s //单步步入,可以理解为下一步,遇到调用跟进函数中,源码层面的一步
- si //常用,同s,汇编层面的一步
- fini //运行到当前函数结束
- r //run, 巨常用 重新开始执行
- c //执行到断点
断点指令
下普通断点指令b(break):
- b *(0x123456) //常用,给0x123456地址处的指令下断点
- b *$rebase(0x123456) //$rebase 在调试开PIE的程序的时候可以直接加上程序的随机地址, 打开随机化PIE保护时常用
- b fun_name //常用,给函数fun_name下断点,目标文件要保留符号才行
- b file_name:fun_name
- b file_name:15 //给file_name的15行下断点,要有源码才行
- b 15
- b +0x10 //在程序当前停住的位置下0x10的位置下断点,同样可以-0x10,就是前0x10
- break fun if $rdi==5 //条件断点,rdi值为5的时候才断
删除和禁用断点
- d b 断点编号 //删除断点 == delete 断点编号 //删除指定断点
- i b //查看断点信息
- disable 断点编号 //禁用指定断点
- enable 断点编号 //启用指定断点
- clear //清楚所有断点
捕获断点catch
- catch syscall //syscall调用是断住
打印指令p(print):
- p fun_name //打印fun_name的地址,需要保留符号
- p 0x10-0x08 //计算0x10-0x08的结果
- p &a //查看变量a的地址
- p *(0x123456) //查看0x123456地址的值,注意和x指令的区别,x指令查看地址的值不用星号
- p $rdi //显示rdi寄存器的值,注意和x的区别,这只是显示rdi的值,而不是rdi指向的值
- p *($rdi) //显示rdi指向的值
- x /nuf 0x123456 //常用,x指令的格式是:x空格/nfu,nfu代表三个参数
参数:n代表显示几个单元(而不是显示几个字节,后面的u表示一个单元多少个字节),放在’/‘后面 u代表一个单元几个字节,b(一个字节),h(俩字节),w(四字节),g(八字节) f代表显示数据的格式,f和u的顺序可以互换,也可以只有一个或者不带n,用的时候很灵活
-
x 按十六进制格式显示变量。
-
d 按十进制格式显示变量。
-
u 按十六进制格式显示无符号整型。
-
o 按八进制格式显示变量。
-
t 按二进制格式显示变量。
-
a 按十六进制格式显示变量。
-
c 按字符格式显示变量。
-
f 按浮点数格式显示变量。
-
s 按字符串显示。
-
b 按字符显示。
-
i 显示汇编指令。
-
x /10gx 0x123456 //常用,从0x123456开始每个单元八个字节,十六进制显示是个单元的数据
-
x /10xd $rdi //从rdi指向的地址向后打印10个单元,每个单元4字节的十进制数
-
x /10i 0x123456 //常用,从0x123456处向后显示十条汇编指令
-
i r //查看寄存器的值
杂项
- stack //查看栈
- retaddr //打印包含返回地址的栈地址
- canary //直接看canary的值(没用过)
- plt //查看plt表
- got //查看got表
- checksec //检查程序保护
- vmmap //查看内存空间和权限
以一道NewStar CTF2024 题名gdb
为例
下载附件后进入发现main函数
|
|
SUB_12E5
为加密函数,加密看不懂(出题人故意的)
动调查看
找到偏移地址b *$rebase(0x181A)
运行到调用处停下,
RDI存储加密内容,RSI存储加密密钥(len),所以我们复制加密内容地址,然后ni单步运行后再输入tel 0x7fffffffd7d7
此时已经转换成加密后的样子了
发送b'\x5d\x1d\x43\x55\x53\x45\x57\x45
即可