Featured image of post Gdb_mannual

Gdb_mannual

Gdb调试基础,记录一些指令

GDB调试基础

1. 启动GDB

1
gdb <可执行文件>

如上图所示,gdb启动后,默认进入gdb的命令行模式,并打开了一个二进制文件,通过run指令进行了运行。 输入start指令,进入gdb的调试模式。

  1. STACK 含义:栈(Stack) 描述:栈是程序运行时用于存储局部变量、函数调用信息和返回地址的内存区域。每当函数被调用时,栈会存储当前函数的局部变量、参数和返回地址等信息。栈通常是向下增长的(即栈顶地址减小)。 用途:在调试时,STACK 区域显示的是当前栈的内容,包括函数调用栈、局部变量等。
  2. HEAP 含义:堆(Heap) 描述:堆是程序动态分配内存的地方(比如通过 malloc、calloc 等动态内存分配函数)。堆的内存是由程序员显式管理的,可以进行手动分配和释放。堆的分配方式通常是向上增长的。 用途:HEAP 区域会显示当前程序使用的动态分配的内存,包括由 malloc、free 等操作分配的内存块。在调试时,这有助于你检查内存泄漏或溢出等问题。
  3. CODE 含义:代码段(Code Segment) 描述:代码段是存放程序指令(机器代码)的区域。这部分内存是只读的,程序的执行代码存储在这里。一般来说,代码段是只读的,以防止程序在运行时修改其自身的指令。 用途:CODE 区域显示程序的机器指令,可以通过它来检查正在执行的代码,反汇编操作通常会展示这个区域。
  4. DATA 含义:数据段(Data Segment) 描述:数据段是用来存放程序的全局变量和静态变量的地方。这些变量在程序执行期间保持固定的内存位置。数据段通常包括初始化的数据(如全局变量的初始值)和未初始化的数据(例如 BSS 段)。 用途:DATA 区域显示程序的全局变量和静态变量的内存布局,帮助调试全局状态和数据。
  5. WX 含义:可写且可执行(Writable and Executable) 描述:标记为 WX 的内存区域是既可写又可执行的。这种内存区域通常是存在漏洞的地方,例如程序可能会被攻击者通过写入一些恶意代码并随后执行它,导致代码注入攻击。 用途:如果程序中有 WX 区域,它可能会被用来检查是否存在不当的内存访问,尤其是在涉及堆栈溢出、代码注入等漏洞时。
  6. 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函数

 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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  unsigned __int64 v3; // rax
  size_t v4; // rax
  int fd; // [rsp+0h] [rbp-440h]
  char s[9]; // [rsp+7h] [rbp-439h] BYREF
  __int64 v8[4]; // [rsp+10h] [rbp-430h] BYREF
  char buf[1032]; // [rsp+30h] [rbp-410h] BYREF
  unsigned __int64 v10; // [rsp+438h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  strcpy(s, "0d000721");
  qmemcpy(v8, "mysecretkey1234567890abcdefghijk", sizeof(v8));
  printf("Original: %s\n", s);
  v3 = strlen(s);
  sub_12E5((__int64)s, v3, (__int64)v8);
  printf("Input your encrypted data: ");
  read(0, buf, 0x200uLL);
  v4 = strlen(s);
  if ( !memcmp(s, buf, v4) )
  {
    printf("Congratulations!");
    fd = open("/flag", 0);
    memset(buf, 0, 0x100uLL);
    read(fd, buf, 0x100uLL);
    write(1, buf, 0x100uLL);
  }
  return 0LL;
}

SUB_12E5为加密函数,加密看不懂(出题人故意的) 动调查看
找到偏移地址b *$rebase(0x181A) 运行到调用处停下,
RDI存储加密内容,RSI存储加密密钥(len),所以我们复制加密内容地址,然后ni单步运行后再输入tel 0x7fffffffd7d7
此时已经转换成加密后的样子了
发送b'\x5d\x1d\x43\x55\x53\x45\x57\x45即可

本博客已稳定运行
发表了14篇文章 · 总计3万2千字

浙ICP备2024137952号 『网站统计』

𝓌𝒶𝒾𝓉 𝒻ℴ𝓇 𝒶 𝒹ℯ𝓁𝒾𝓋ℯ𝓇𝒶𝓃𝒸ℯ
使用 Hugo 构建
主题 StackJimmy 设计
⬆️该页面访问量Loading...