gdb调试原理及qemu中的gdbserver
(一)gdb调试原理
此部分转自:https://blog.csdn.net/u012658346/article/details/51159971 https://www.cnblogs.com/xsln/p/ptrace.html
gdb调试的原理是基于ptrace系统调用,ptrace()系统调用提供了一个方法,该方法使一个程序(追踪者)可以观察和控制另外一个程序(被追踪者)的执行,并检查和改变被追踪者的内存及寄存器。它主要用于实现断点调试和追踪系统调用。
当被追踪时,被追踪线程在接收信号时会被停止,即使那个信号是被忽略的也是如此(SIGKILL除外)。追踪程序会在一个调用waitpid(或者其他类wait系统调用)时收到通知,该调用会返回一个包含被追踪线程停止的原因的状态值。当被追踪线程停止时,追踪程序可以使用多种ptrace请求来检查和编辑被追踪线程。追踪程序可以让被追踪线程继续运行,有选择地忽略发过来的信号(甚至可以发送一个完全不同的信号给被追踪线程)
利用ptrace系统调用,可在被调试程序和gdb之间建立追踪关系。然后所有发送给被调试程序(被追踪线程)的信号(除SIGKILL)都会被gdb截获,gdb根据截获的信号,查看被调试程序相应的内存地址,并控制被调试的程序继续运行。
ptrace系统调用原型:
long ptrace(enum __ptrace_request request, pid_t pid,void *addr,void *data);
request参数的主要选项:
PTRACE_TRACEME:由子进程调用,表示本进程将被其父进程跟踪,交付给这个进程的所有信号,即使信号是忽略处理的(除SIGKILL之外),都将使其停止,父进程将通过wait()获知这一情况。
PTRACE_ATTACH: attach到一个指定的进程,使其成为当前进程跟踪的子进程,而子进程的行为等同于它进行了一次PTRACE_TRACEME操作。但是,需要注意的是,虽然当前进程成为被跟踪进程的父进程,但是子进程使用getppid()的到的仍将是其原始父进程的pid。当你在gdb中使用attach命令来跟踪一个指定进程/线程的时候,gdb就自动成为改进程的父进程,而被跟踪的进程则使用了一次PTRACE_TRACEME,gdb也就顺理成章的接管了这个进程。
PTRACE_CONT:继续运行之前停止的子进程。可同时向子进程交付指定的信号。
gdb三种调试方式:
1)attach并调试一个已经运行的进程:
确定需要进行调试的进程id,运行gdb,输入attch pid,如:gdb 12345。gdb将对指定进行执行如下操作:ptrace(PTRACE_ATTACH,pid,0,0), 建立自己与进程号为pid的进程间的跟踪关系。即利用PTRACE_ATTACH,使自己变成被调试程序的父进程。用attach建立起来的跟踪关系,可以调用ptrace(PTRACE_DETACH,pid,...)来解除。注意attach进程时的权限问题,如一个非root权限的进程是不能attach到一个root进程上的 。
2)运行并调试一个新的进程,利用fork+execve执行被测试的程序,子进程在执行execve之前调用ptrace(PTRACE_TRACEME),建立了与父进程(debugger)的跟踪关系:
运行gdb,通过命令行参数或file指定目标调试程序,如gdb ./test
输入run命令,gdb执行下述操作:
通过fork()系统调用创建一个新进程
在新创建的子进程中调用ptrace(PTRACE_TRACEME,0,0,0)
在子进程中通过execv()系统调用加载用户指定的可执行文件
3)远程调试目标主机上新创建的进程
gdb运行在调试机,gdbserver运行在目标机,通过二者之间定义的数据格式进行通信
gdb调试基础--信号
gdb调试的实现都是建立在信号的基础上的,在使用参数为PTRACE_TRACEME或PTRACE_ATTACH的ptrace系统调用建立调试关系后,交付给目标程序的任何信号首先都会被gdb截获。 因此gdb可以先行对信号进行相应处理,并根据信号的属性决定是否要将信号交付给目标程序。
.断点原理:
1) 断点的实现原理,就是在指定的位置插入断点指令,当被调试的程序运行到断点的时候,产生SIGTRAP信号。该信号被gdb捕获并进行断点命中判定,当gdb判断出这次SIGTRAP是断点命中之后就会转入等待用户输入进行下一步处理,否则继续。
2) 断点的设置原理: 在程序中设置断点,就是先将该位置的原来的指令保存,然后向该位置写入int 3。当执行到int 3的时候,发生软中断,内核会给子进程发出SIGTRAP信号,当然这个信号会被转发给父进程。然后用保存的指令替换int3,等待恢复运行。
3) 断点命中判定:gdb把所有的断点位置都存放在一个链表中,命中判定即把被调试程序当前停止的位置和链表中的断点位置进行比较,看是断点产生的信号,还是无关信号。
4) 条件断点的判定:原理同3),只是恢复断点处的指令后,再多加一步条件判断。若表达式为真,则触发断点。由于需要判断一次,因此加入条件断点后,不管有没有触发到条件断点,都会影响性能。在x86平台,某些硬件支持硬件断点,在条件断点处不插入int 3,而是插入一个其他指令,当程序走到这个地址的时候,不发出int 3信号,而是先去比较一下特定寄存器和某个地址的内容,再决定是否发送int 3。因此,当你的断点的位置会被程序频繁地“路过”时,尽量使用硬件断点,会对提高性能有帮助。
单步跟踪:
next指令可以实现单步调试,即每次只执行一行语句。一行语句可能对应多条及其指令,当执行next指令时,gdb会计算下一条语句对应的第一条指令的地址,然后控制目标程序走到该位置停止。
(二)qemu中的gdbserver
正常情况下进行远程调试需要被调试端安装有gdbserver程序,而qemu中内置了gdbserver模块,基于此可使用gdb实现对qemu虚拟机的远程调试,GDB/GDBSERVER调试模型的原理如下:
在GDB/GDBSERVER调试模型中,GDBSERVER是一个轻量级的GDB调试器,在调试过程中担任着调试代理的角色。在调试过程中,主机和目标机之间使用串口或者网络作为通信的通道。在主机上GDB通过这条通道使用一种基于ASCII的简单通讯协议RSP与在目标机上运行的GDBSERVER进行通讯。GDB发送指令,如内存、寄存器读写,GDBSERVER则首先与运行被调试程序映像的进程进行绑定,然后等待GDB发来的数据,对包含命令的数据包进行解析之后便进行相关处理,然后将结果返回给主机上的GDB。
RSP协议将GDB/GDBSERVER间通讯的内容更看做是数据包,数据包的内容都使用ASCII字符。每一个数据包都遵循这样的格式:$ <调试信息>#<校验码>.
如上图所示,包的内容会以16进制的形式来编码(enhex),#后面的两位数字是校验码,具体的计算方式是数据包中所有字符求和再用256求余数。而数据包的内容,也就是RSP协议的载体,将会是gdb接收的命令。接受方在收到数据包之后,对数据包进行校验,若正确回应“+”,反之回应“-”。
RSP 协议中定义的主要命令可以分为 3 类:
(1)寄存器/内存读写命令
命令 g: 读所有寄存器的值
命令 G:写所有寄存器的值
命令 P: 写某个寄存器
命令 m: 读某个内存单元
命令 M:写某个内存单元
(2)程序控制命令
命令?: 报告上一次的信号
命令 s: 单步执行
命令 c: 继续执行
命令 k: 终止程序
(3)其它命令
命令 O:控制台输出(Console Output )
命令 E:出错回应(Error response)
当主机使用gdb调试时,gdb和qemu中内置的gdbserver就使用上述模型进行交互,从而对qemu虚拟机进行调试。如gdb调试端发送x/ <n/f/u> <addr>表示读取addr处的内容,命令经RSP协议封装成数据包发送至qemu的gdbserver端,gdbserver收到数据包后对其进行校验,校验成功后进行解析处理并返回至gdb客户端。
开启gdbserver之后,会等待来自gdb的连接请求,默认端口为1234,gdb使用ip和端口与gdbserver连接:
连接建立后会调用gdb_handlesig()等待stdin传来的gdb指令,调用gdb_read_byte()解析用户的输入,并对数据包进行校验,若校验正确调用gdb_handle_packet()进行gdb命令的处理。
如下解析字符为m时,表示读取某个内存单元,则调用函数target_memory_rw_debug()进行内存单元的读取,该函数最后调用cpu_memory_rw_debug()读取内存内容
当解析字符为g时,使用gdb_read_register读取寄存器信息,该函数会调用特定CPU类型的回调函数:
x86下调用如下函数,通过qemu为虚拟机维护的CPUX86State结构体得到虚拟机的寄存器信息:
类似地,插入一个断点时:
在kvm_enabled的情况下,调用kvm_insert_breakpoint:
该函数进行断点的插入并最终使用kvm_update_guest_debug向kvm更新客户机的debug状态,该函数调用kvm_invoke_set_guest_debug,进一步调用kvm_vcpu_ioctl(cpu, KVM_SET_GUEST_DEBUG,&dbg_data->dbg)执行ioctl至kvm中设置相关异常向量,BP(breakpoint,int3),DB(int 1)(插一句,可通过设置异常位图中的这两个位对上述指令进行拦截)
gdb调试原理及qemu中的gdbserver的更多相关文章
- GDB调试原理——ptrace系统调用
本文由霸气的菠萝原创,转载请注明出处:http://www.cnblogs.com/xsln/p/ptrace.html 全部关于gdb的文章索引请点这里 引子: gdb基本上大家都在用,你有没有想过 ...
- gdb调试已在运行中的进程
一.在服务器上调试进程,服务器上并没有源代码,所以需要将源码上传至服务器,才能调试看到源码,以下是步骤: 1.查看服务进程id:pgrep 服务名 [user@user-MP app]$ pgrep ...
- 使用gdb调试游戏服务器
前言 谈论gdb重要性 一般来说.提gdb,命令用于调试."命令",用户是几乎相同的复杂话.而事实确实如此,实际的开发调试必须用到gdb. 如今.大多数Linux系统是存在于ser ...
- gdb调试段错误及使用
在编程调试中,经常出现段错误,此时可用gdb调试.具体方法为注册段错误信号处理函数,在处理函数中启动gdb.具体代码如下: void segv_handler(int no) { ]; ]; FILE ...
- ubuntu: qemu+gdb 调试linux kernel 学习笔记
声明: 本笔记内容并非本人原创,90%来自网络资料的整合.同时,由于自己是刚刚接触qemu & gdbserver remote debug,本文也就算不得教程,仅供有缘人参考而已. ---- ...
- 在QEMU中调试ARM程序【转】
转自:http://linuxeden.com/html/develop/20100820/104409.html 最近我想调试一个运行在QEMU模拟ARM系统中的Linux程序.我碰到过一些麻烦,因 ...
- 在qemu环境中用gdb调试Linux内核
简介 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试.其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qem ...
- QEMU+GDB调试方法
两年前调试usb/ip开源项目时,就曾用虚拟机远程调试过Windows和Linux系统内核,当时在VMware Workstation上创建两个虚拟机进行调试,也没有记录下如何配置调试,只是大体的还记 ...
- Linux下交叉编译gdb,gdbserver+gdb的使用以及通过gdb调试core文件
交叉编译gdb和gdbserver 1.下载gdb:下载地址为:http://ftp.gnu.org/gnu/gdb/按照一般的想法,最新版本越好,因此下载7.2这个版本.当然,凡事无绝对.我们以gd ...
随机推荐
- Charles 抓包的简单使用
1.准备工具: 软件 Charles 手机 随意哪个现代手机 2.基本配置 安装Charles的电脑和手机在同一个局域网下, 点击手机上的和电脑练得同一个局域网的名字进行配置,里面有个代理,选择手动, ...
- 【原创】大数据基础之Zookeeper(1)介绍、安装及使用
zookeeper3.4.11 http://zookeeper.apache.org/ 一 简介 ZooKeeper is a centralized service for maintaining ...
- 快速搭建一个Spring Boot + MyBatis的开发框架
前言:Spring Boot的自动化配置确实非常强大,为了方便大家把项目迁移到Spring Boot,特意总结了一下如何快速搭建一个Spring Boot + MyBatis的简易文档,下面是简单的步 ...
- javaWeb之使用servlet搭建服务器入门
servlet: 百度百科说法: Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改 ...
- numpy安装-【老鱼学numpy】
要玩numpy,就得要安装numpy. 安装python 3.6.3 64位 首先需要安装python,安装python的具体方法这里就不细讲了. 可以到官网上下载相应的python版本就可以了,目前 ...
- 4.17 小发现(dalao勿点)
洛谷上: (1)iso::sync_with_stio(0); 虽然可以提高cin的速度; 但是有时会RE或WA(如果是WA一般提示Too shot on line); (2)函数最好写上return ...
- 想对list里面的对象进行排序
不必使用排序算法.实现Comparator接口就行
- java在cmd下编译引用第三方jar包
java在cmd下编译引用第三方jar包 转 https://blog.csdn.net/qq_21439971/article/details/53924594 获取第三方jar包 第三包我们可以引 ...
- udf提权
0x00前言: udf提权是通过数据库来实现获取目标的管理员的shell,来达到从低权限提权到高权限 0x01什么是udf: udf(Userdefined function)是用户自定义函数 在my ...
- UVA 548 Tree 建树
题意: 输入中序和后序的权值,输出哪个叶子使它到根的路径上权和最小. 思路: 输入后建树,然后dfs求最小的叶子. #include<iostream> #include<cstdi ...