使用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,但是即使使用更新的版本,您也很有可能成功(请点击这里查看其他版本)。

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

  1. 调试多线程 & 查死锁的bug & gcore命令 & gdb对多线程的调试 & gcore & pstack & 调试常用命令

    gdb thread apply all bt 如果你发现有那么几个栈停在 pthread_wait 或者类似调用上,大致就可以得出结论:就是它们几个儿女情长,耽误了整个进程. 注意gdb的版本要高于 ...

  2. GDB:从单线程调试到多线程调试(MFiX单步调试)

    GDB:从单线程调试到多线程调试 1. 裸跑GDB 1.1 安装GDB sudo apt-get install gdb 1.2 编译程序 由于需要调试,因此编译的时候需要添加-g编译参数: 1.3 ...

  3. GDB+GdbServer: ARM程序调试

    arm-linux-gdb+gdbserver环境搭建以及远程调试 GDB+GdbServer: ARM程序调试 嵌入式arm linux环境中gdb+gdbserver调试 建立交叉调试环境 编译过 ...

  4. 用qemu+gdb tcp server+CDT调试linux内核启动-起步

    用qemu+gdb tcp server+CDT调试linux内核启动-起步 说明: 环境信息与 用virtualbox+模拟串口+CDT调试linux内核 TCP IP协议栈-起步 提到的一样,并且 ...

  5. 学汇编的时候可以拿IDA之类的反汇编工具辅助学习,再用gdb或者IDA动态调试,跟踪每条指令的 执行结果。都不难

    作者:潘安仁链接:https://www.zhihu.com/question/40720890/answer/87926792来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

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

    因为有很多的小伙伴是从单片机转过来的,用惯了单片机上的JLINK调试程序,换到Linux上非常的不习惯.确实,如果能设置断点,单步调试,查看变量,那确实是太爽了,那么在我们的Linux可以做到吗,答案 ...

  7. 利用QEMU+GDB搭建Linux内核调试环境

    前言 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试. 其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qe ...

  8. gdb插件gef安装爬坑

    0x00: gdb是linux下的调试利器,但无奈界面不太友好,所以需要一些辅助插件. 0x01:关于插件选择 之前我一直使用的是pead,之前了解到还有个插件gef,因为gef支持多构架,而且hea ...

  9. 在ubuntu纯字符gdb界面下来开发调试嵌入式ARM

    前面一个帖子介绍了使用eclipse来开发STM32的固件,但有的时候使用Eclipse的GDB调试器会崩溃掉,反复这样造成我们开发的效率降低,信心也会受一打击. 最近接触到的许多源码,就是在linu ...

随机推荐

  1. spring整合mybatis(代理的方式)【我】

    创建项目等同上一篇非代理方式随笔,只说不一样的部分: 项目结构主要是多了下面红框部分: 配置文件: 主要是dao配置文件中多了Mapper代理java类的扫描包路径: applicationConte ...

  2. php中应用redis

    下载软件包wget https://codeload.github.com/phpredis/phpredis/zip/develop mv develop phpredis.zip 解压unzip ...

  3. 阶段3 3.SpringMVC·_05.文件上传_1 文件上传之上传原理分析和搭建环境

    分成几个部分 里面可能就包含文件上传的值 提交方式要改成post 第三个就是提供一个input file的文件选择域 新建项目 新建一个项目 当前项目没有父工程 跳过联网下载 改成02 构建 编译和目 ...

  4. Qt QLabel添加cliked事件

    #ifndef MYLABEL_H #define MYLABEL_H #include <QObject> #include <QLabel> class MyLabel : ...

  5. Object.assign()的用法 -- 用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,返回目标对象

    语法: Object.assign(target, …sources) target: 目标对象,sources: 源对象用于将所有可枚举属性的值从一个或多个源对象复制到目标对象.它将返回目标对象. ...

  6. Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock)

    Leetcode之广度优先搜索(BFS)专题-752. 打开转盘锁(Open the Lock) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary ...

  7. 【机器学习】Learning to Rank入门小结 + 漫谈

    Learning to Rank入门小结 + 漫谈 Learning to Rank入门小结 Table of Contents 1 前言 2 LTR流程 3 训练数据的获取4 特征抽取 3.1 人工 ...

  8. linux下Eclipse进行C编程时动态链接库的生成和使用

    引用 http://linux.chinaitlab.com/soft/864157.html 欢迎进入Linux社区论坛,与200万技术人员互动交流 >>进入 一.创建动态链接库1.创建 ...

  9. logstash 处理信息规律研究

    1.input file path => "/opt/50910627.log" path => "/path/to/%{+yyyy/MM/dd/hh}.lo ...

  10. SpringBoot上传文件

    1.pom文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w ...