使用GDB和GEF进行调试

使用GDB进行调试

这是编译ARM二进制文件和使用GDB进行基本调试的简单介绍。在您按照教程进行操作时,您可能需要按照自己的习惯使用ARM程序集。在这种情况下,你要么需要一个备用的ARM设备,或者你只是按照在这短短的步骤建立自己的实验室环境中虚拟机操作方法

您可以使用第7部分 - 堆栈和函数中的以下代码来熟悉GDB的基本调试。

.section .text
.global _start

_开始:
    按{r11,lr} / *开始序幕。将帧指针和LR保存到堆栈* /
    添加r11,sp,#0 / *设置堆栈框架的底部* /
    sub sp,sp,#16 / *序幕结束。在堆栈上分配一些缓冲区* /
    mov r0,#1 / *设置局部变量(a = 1)。这也可以作为设置最大功能的第一个参数* /
    mov r1,#2 / *设置局部变量(b = 2)。这也可以作为设置最大功能的第二个参数* /
    bl最大/ *呼叫/分支功能最大* /
    sub sp,r11,#0 / *结尾的开始。重新调整堆栈指针* /
    流行{r11,pc} / *结语结尾。从堆栈中恢复帧指针,通过直接加载到PC,跳转到先前保存的LR * /

最大:
    按{r11} / *开始序幕。将帧指针保存到堆栈* /
    添加r11,sp,#0 / *设置堆栈框架的底部* /
    sub sp,sp,#12 / *序幕结束。在堆栈上分配一些缓冲区* /
    cmp r0,r1 / *执行if(a <b)* /
    movlt r0,r1 / *如果r0小于r1,将r1存入r0 * /
    添加sp,r11,#0 / *结尾的开始。重新调整堆栈指针* /
    弹出{r11} / *恢复帧指针* /
    bx lr / *结语结尾。通过LR寄存器跳回主站* /

就个人而言,我更喜欢使用GEF作为GDB扩展。它给了我一个更好的概述和有用的功能。您可以在这里试用:GEF -  GDB增强功能

将上面的代码保存在一个名为max.s的文件中,并使用以下命令进行编译:

$ as max.s -o max.o
$ ld max.o -o max

调试器是一个强大的工具,可以:

  • 崩溃后加载内存转储(验尸调试)
  • 附加到正在运行的进程(用于服务器进程)
  • 启动一个程序并进行调试

针对二进制文件,核心文件或进程ID启动GDB:

  • 附加到进程:$ gdb -pid $(pidof <process>)
  • 调试一个二进制文件:$ gdb ./file
  • 检查核心(崩溃)文件:$ gdb -c ./core.3243
$ gdb max

如果您安装了GEF,则会释放您的gef>提示符。

这是你如何获得帮助:

  • (gdb)h
  • (gdb)apropos <search-term>
gef> apropos寄存器
收集 - 指定要在追踪点收集的一个或多个数据项目
核心文件 - 使用FILE作为核心转储来检查内存和寄存器
info all-registers  - 所有寄存器及其内容的列表
info r  - 整数寄存器及其内容的列表
信息寄存器 - 整数寄存器及其内容的列表
维护打印烹饪寄存器 - 打印包括烹饪值的内部寄存器配置
维护打印原始寄存器 - 打印内部寄存器配置,包括原始值
维护打印寄存器 - 打印内部寄存器配置
维护打印远程寄存器 - 打印包括每个寄存器的内部寄存器配置
p  - 打印EXP表达式的值
打印 - 表达EXP的打印值
寄存器 - 在一个显示全部细节
设置may-write-registers  - 设置写入寄存器的权限
设置观察者 - 设置gdb是否在观察者模式下控制劣势
显示may-write-registers  - 显示写入寄存器的权限
显示观察者 - 显示gdb是否在观察者模式下控制劣势
tui reg float  - 仅显示浮点寄存器
tui reg general  - 只显示通用寄存器
tui reg system  - 只显示系统寄存器

断点命令:

  • break(或者只是b)<function-name>
  • 打破<line-number>
  • 打破文件名:功能
  • 中断文件名:行号
  • 打破* <地址>
  • break + <offset>  
  • 打破 - <偏移>
  • tbreak(设置临时断点)
  • del <number>  (删除断点编号x)
  • 删除(删除所有断点)
  • 删除<range>(删除断点范围)
  • 禁用/启用<断点编号或范围>(不删除断点,只是启用/禁用它们)
  • 继续(或只是c) - (继续执行,直到下一个断点)
  • 继续<number>(继续,但是忽略当前的断点编号时间,对循环内的断点很有用。
  • 完成(继续结束功能)
gef> break _start
gef> info break
Num Type Disp Enb Address什么
1个断点保持y 0x00008054 <_start>
 断点已经达到了一次
gef> del 1
 gef> break * 0x0000805c
断点2在0x805c
gef> break _start

这将删除第一个断点并在指定的内存地址设置一个断点。当你运行这个程序时,它会在这个确切的位置中断。如果不删除第一个断点,只是设置一个新断点并运行,它将在第一个断点处断开。

开始和停止:

  • 从程序开始处开始执行程序

    • [R
    • 运行<command-line-argument>
  • 停止程序执行
  • 退出GDB调试器
    • 放弃
    • q
gef>运行

现在我们的程序正好打破了我们想要的地方,现在是检查内存的时候了。命令“x”以各种格式显示存储器内容。

语法:x / < count > < format > < unit >
格式 单元
x - 十六进制 b - 字节
d - 十进制 h - 半字(2字节)
我 - 指示 w - 字(4字节)
t - 二进制(二) g - 巨词(8字节)
o - 八进制  
你 - 无符号  
s - 字符串  
c - 字符  
gef> x / 10i $ pc
=> 0x8054 <_start>:push {r11,lr}
 0x8058 <_start + 4>:添加r11,sp,#0
 0x805c <_start + 8>:sub sp,sp,#16
 0x8060 <_start + 12>:mov r0,#1
 0x8064 <_start + 16>:mov r1,#2
 0x8068 <_start + 20>:bl 0x8074 <max>
 0x806c <_start + 24>:sub sp,r11,#0
 0x8070 <_start + 28>:pop {r11,pc}
 0x8074 <max>:push {r11}
 0x8078 <max + 4>:加上r11,sp,#0
gef> x / 16xw $ pc
0x8068 <_start + 20>:0xeb000001 0xe24bd000 0xe8bd8800 0xe92d0800
0x8078 <max + 4>:0xe28db000 0xe24dd00c 0xe1500001 0xb1a00001
0x8088 <max + 20>:0xe28bd000 0xe8bd0800 0xe12fff1e 0x00001741
0x8098:0x61656100 0x01006962 0x0000000d 0x01080206

单步执行代码的命令:

  • 进入下一行代码。将步入一个功能

    • 步骤1
    • 小号
    • 步骤<步骤数量>
  • 执行下一行代码。不会输入功能
    • nexti
    • ?
    • 下一个<number>
  • 继续处理,直到达到指定的行号,函数名称,地址,文件名:函数或文件名:行号
    • 直到
    • 直到<line-number>
  • 显示当前行号和您所在的功能
    • 哪里
gef> nexti 5
...
0x8068 <_start + 20> bl 0x8074 <max> < -  $ pc
0x806c <_start + 24> sub sp,r11,#0
0x8070 <_start + 28> pop {r11,pc}
0x8074 <max> push {r11}
0x8078 <max + 4>添加r11,sp,#0
0x807c <max + 8> sub sp,sp,#12
0x8080 <max + 12> cmp r0,r1
0x8084 <max + 16> movlt r0,r1
0x8088 <max + 20>添加sp,r11,#0

信息寄存器ir检查寄存器

gef> info寄存器
r0 0x1 1
r1 0x2 2
r2 0x0 0
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0xbefff7e8 3204446184
r12 0x0 0
sp 0xbefff7d8 0xbefff7d8
lr 0x0 0
pc 0x8068 0x8068 <_start + 20>
cpsr 0x10 16

命令“信息寄存器”给你当前的寄存器状态。我们可以看到通用寄存器r0-r12和专用寄存器SP,LR和PC,包括状态寄存器CPSR。函数的前四个参数通常存储在r0-r3中。在这种情况下,我们手动将值移到r0和r1。

显示进程内存映射:

gef> info proc map
过程10225
映射地址空间:

 开始地址结束地址大小偏移量objfile
     0x8000 0x9000 0x1000 0 / home / pi / lab / max
 0xb6fff000 0xb7000000 0x1000 0 [sigpage]
 0xbefdf000 0xbf000000 0x21000 0 [stack]
 0xffff0000 0xffff1000 0x1000 0 [矢量]

用“反汇编”命令查看max函数的反汇编输出。

gef>反汇编max
 汇编代码功能最大的转储:
 0x00008074 <+0>:push {r11}
 0x00008078 <+4>:添加r11,sp,#0
 0x0000807c <+8>:sub sp,sp,#12
 0x00008080 <+12>:cmp r0,r1
 0x00008084 <+16>:movlt r0,r1
 0x00008088 <+20>:添加sp,r11,#0
 0x0000808c <+24>:pop {r11}
 0x00008090 <+28>:bx lr
 汇编器转储结束。

GEF特定命令(更多命令可以使用命令“gef”查看):

  • 将所有加载的ELF图像的所有部分转储到进程内存中

    • X档案
  • proc map的增强版本在映射页面中包含RWX属性
    • 的VMMap
  • 内存属性在给定的地址
    • 信佛
  • 检查内置于运行二进制文件中的编译器级保护
    • checksec
gef> xfiles
     开始结束名称文件
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
0x00008054 0x00008094 .text / home / pi / lab / max
gef>  vmmap
     开始结束偏移Perm路径
0x00008000 0x00009000 0x00000000 rx / home / pi / lab / max
0xb6fff000 0xb7000000 0x00000000 rx [sigpage]
0xbefdf000 0xbf000000 0x00000000 rwx [stack]
0xffff0000 0xffff1000 0x00000000 rx [矢量]
gef> xinfo 0xbefff7e8
---------------------------------------- [xinfo:0xbefff7e8] ----- -----------------------------------
找到0xbefff7e8
页面:0xbefdf000  - > 0xbf000000(大小= 0x21000)
权限:rwx
路径名称:[stack]
偏移(从页面):+ 0x207e8
Inode:0
gef> checksec
[+] checksec for‘/ home / pi / lab / max‘
金丝雀:没有
NX支持:是的
PIE支持:没有
RPATH:没有
RUNPATH:没有
部分RelRO:没有
完整的RelRO:没有

故障排除

为了使GDB的调试更高效,知道某些分支/跳转将带给我们的位置是非常有用的。某些(较新的)GDB版本解析分支指令的地址并向我们显示目标函数的名称。例如,GDB的以下输出缺少这个功能:

...
0x000104f8 <+72>:bl 0x10334
0x000104fc <+76>:mov r0,#8
0x00010500 <+80>:bl 0x1034c
0x00010504 <+84>:mov r3,r0
...

这是GDB(native,没有gef)的输出,它具有我正在谈论的功能:

0x000104f8 <+72>:bl 0x10334 <free @ plt>
0x000104fc <+76>:mov r0,#8
0x00010500 <+80>:bl 0x1034c <malloc @ plt>
0x00010504 <+84>:mov r3,r0

如果你在GDB中没有这个特性,你可以更新Linux源代码(并且希望他们的代码库中已经有了一个更新的GDB)或者自己编译一个更新的GDB。如果您选择自行编译GDB,则可以使用以下命令:

cd / tmp
wget https://ftp.gnu.org/gnu/gdb/gdb-7.12.tar.gz
tar vxzf gdb-7.12.tar.gz
sudo apt-get更新
sudo apt-get install libreadline-dev python-dev texinfo -y
cd gdb-7.12
./configure --prefix = / usr --with-system-readline --with-python && make -j4
sudo make -j4 -C gdb / install
gdb --version

我用上面提供的命令在Raspbian(jessie)上下载,编译和运行GDB,没有任何问题。这些命令也将取代以前的GDB版本。如果你不想要,那么跳过以install为结尾的命令。而且,我在QEMU中模拟Raspbian的时候做了这个,所以花了我很长时间(小时),因为仿真环境中的资源(CPU)有限。我使用GDB版本7.12,但是即使使用更新的版本,您也很有可能成功(请点击这里查看其他版本)。

时间: 2024-08-30 13:20:34

使用GDB和GEF进行调试的相关文章

学习4412开发板gdb和gdbserver的调试

因为有很多的小伙伴是从单片机转过来的,用惯了单片机上的JLINK调试程序,换到Linux上非常的不习惯.确实,如果能设置断点,单步调试,查看变量,那确实是太爽了,那么在我们的Linux可以做到吗,答案当然是可以的. 在之前的文章中,对gdb调试做过一期简单的介绍番外篇|使用gdb对程序进行调试,但是之前的文章我们是在ubuntu上对应用程序进行的调试,不是在ARM板上进行的调试,相对于其他的软件开发,嵌入式软件的调试手段比较有限,我相信一定有很多人的调试手段依然是使用最原始的打印的办法.这一期我

利用GDB对程序进行调试

第一章初涉调试会话 调试工具 GDB Unix下最常用的调试工具 DDD 基于GUI的调试器,大多数工具都是GDB的GUI前端. Eclipse IDE也是一种调试工具 atoi( )把字符串变为整数,头文件是include<stdlib.h> gcc -g -Wall -o insert_sort ins.c -g选项可以让编译器将符号表(对应于程序的变量和代码行的内存地址列表)保存在生成的可执行文件里中. 这样才能在调试会话的过程中引用源代码中的变量和行号,比如"在第30行停止&

使用GDB进行嵌入式远程调试

PC主机:Ubuntu 10.4 目标板:TQ2440开发板,linux内核2.6.30 NOTE:为了使用gdb进行调试,强烈建议使用nfs服务,否则调试会非常麻烦. 使用nfs服务可以参考:S3C2440挂载NFS文件系统 1. 概述 所谓远程调试,就是开发板上建立一个gdb服务端,同时待调试的程序也位于开发板,然后在PC机上使用gdb发起远程连接来进行调试.也就是说,在PC端调试开发板上的程序.请注意,在PC端需要包含被调试程序的符号调试信息(symbolic debug informat

Linux学习: 使用gdb和gdbserver进行调试

1.引言 在日常程序开发中不免遇到类似空指针操作导致程序崩溃的问题,所以需要一定的手段去定位bug,而断点调试是普遍使用的技巧,比如Windows中用VC++的debug模式进单步运行.断点调试等,有很友好的图形化操作界面,而在Linux中使用的是没有图形界面的调试工具-GDB(听说eclipse支持GUI调试,暂未尝试),所以需要通过指令进行操作,下面逐步介绍gdb调试环境的搭建和使用方法. 2.gdb调试环境搭建 (1)下载gdb工具源码:http://www.gnu.org/softwar

gdb 支持 c++ stl 调试

http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt 将网页的文本保存到 ~/.gdbinit 之后就可以用 p 指令查看stl容器中的内容了 # # STL GDB evaluators/views/utilities - 1.03 # # The new GDB commands: # are entirely non instrumental # do not depend on any "inline"(s

进程内存和内存损坏

本教程的这一部分的先决条件是对ARM汇编的基本了解(在第一个教程系列" ARM汇编基础 "中有介绍).在本章中,您将了解32位Linux环境中进程的内存布局.之后,您将学习堆栈和堆相关的内存损坏的基本原理,以及它们在调试器中的样子. 缓冲区溢出 堆栈溢出 堆溢出 摇摇欲坠的指针 格式字符串 本教程中使用的示例是在ARMv6 32位处理器上编译的.如果您无法访问ARM设备,则可以按照以下教程创建自己的实验室并在VM中模拟Raspberry Pi发行版:使用QEMU模拟Raspberry

gdb调试命令

本篇摘自互联网,纯属自己学习笔记,然分享给看到我的博客的人们. 用GDB调试程序 GDB是一个强大的命令行调试工具.大家知道命令行的强大就是在于,其可以形成执行序列,形成脚本.UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能. 于是UNIX下的软件比Windows下的软件更能有机地结合,各自发挥各自的长处,组合成更为强劲的功能.而Windows下的图形软件基本上是各自为营,

GDB调试之二栈溢出

linux下应用程序中经常会发生段错误段错误基本上是由于访问非法内存所导致的如栈溢出.数组越界访问.malloc/free内存所引起的.在linux下发生段错误时会生成core dump核心转储文件里面记录了发生段错误时的函数调用关系. ubuntu14.04下默认发生段错误时并不产生核心转储文件需要额外的配置通过命令 ulimit -c查看是否允许的core dump文件大小.如果只是临时需要用到可以使用命令ulimit -c unlimited临时打开则发生段错误时会在当前目录下产生core

使用 gdb 调试运行中的 Python 进程

本文和大家分享的是使用 gdb 调试运行中的 Python 进程相关内容,一起来看看吧,希望对大家学习python有所帮助. 准备工作 安装 gdb 和 python2.7-dbg: $ sudo apt-get install gdb python2.7-dbg 设置 /proc/sys/kernel/yama/ptrace_scope: $ sudo su# echo 0 > /proc/sys/kernel/yama/ptrace_scope 运行 test.py: $ python te