当前位置: 首页 > news >正文

ptrace 调式详解

在程序出现bug的时候,最好的解决办法就是通过 GDB 调试程序,然后找到程序出现问题的地方。比如程序出现 段错误(内存地址不合法)时,就可以通过 GDB 找到程序哪里访问了不合法的内存地址而导致的。

本文不是介绍GDB不是使用方式,而是大概介绍 GDB 的实现原理,当然是 GDB 是一个庞大而复杂的项目,不可能只通过一篇文章就能解释清楚,所以本文主要是介绍 GDB 使用的核心的技术 - ptrace。

一,ptrace系统调用

ptrace() 系统调用是 Linux 提供的一个调试进程的工具,ptrace() 系统调用非常强大,它提供非常多的调试方式让我们去调试某一个进程,下面是 ptrace() 系统调用的定义:

long ptrace(enum __ptrace_request request,  pid_t pid, void *addr,  void *data);

下面解释一下 ptrace() 各个参数的作用:

request:指定调试的指令,指令的类型很多,如:PTRACE_TRACEME、PTRACE_PEEKUSER、PTRACE_CONT、PTRACE_GETREGS等等,下面会介绍不同指令的作用。

pid:进程的ID(这个不用解释了)。

addr:进程的某个地址空间,可以通过这个参数对进程的某个地址进行读或写操作。

data:根据不同的指令,有不同的用途,下面会介绍。

二,ptrace使用示例

下面通过一个简单例子来说明 ptrace() 系统调用的使用,这个例子主要介绍怎么使用 ptrace() 系统调用获取当前被调试(追踪)进程的各个寄存器的值,代码如下(ptrace.c):

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <stdio.h>
int main()
{   pid_t child;struct user_regs_struct regs;child = fork();  // 创建一个子进程if(child == 0) { // 子进程ptrace(PTRACE_TRACEME, 0, NULL, NULL); // 表示当前进程进入被追踪状态execl("/bin/ls", "ls", NULL);          // 执行 `/bin/ls` 程序} else { // 父进程wait(NULL); // 等待子进程发送一个 SIGCHLD 信号ptrace(PTRACE_GETREGS, child, NULL, ®s); // 获取子进程的各个寄存器的值printf("Register: rdi[%ld], rsi[%ld], rdx[%ld], rax[%ld], orig_rax[%ld]\n",regs.rdi, regs.rsi, regs.rdx,regs.rax, regs.orig_rax); // 打印寄存器的值ptrace(PTRACE_CONT, child, NULL, NULL); // 继续运行子进程sleep(1);}return 0;
}

通过命令 gcc ptrace.c -o ptrace 编译并运行上面的程序会输出如下结果:

Register: rdi[0], rsi[0], rdx[0], rax[0], orig_rax[59]
ptrace  ptrace.c

上面结果的第一行是由父进程输出的,主要是打印了子进程执行 /bin/ls 程序后各个寄存器的值。而第二行是由子进程输出的,主要是打印了执行 /bin/ls 程序后面输出的结果。

下面解释一下上面程序的执行流程:

主进程调用 fork() 系统调用创建一个子进程。

的进程调用 ptrace(PTRACE_TRACEME,...) 把自己设置为被追踪状态,并且调用 execl() 执行 /bin/ls 程序。

被设置为追踪(TRACE)状态的子进程执行 execl() 的程序后,会向父进程发送 SIGCHLD 信号,并且暂停自身的执行。

父进程通过调用 wait() 接收子进程发送过来的信号,并且开始追踪子进程。

父进程通过调用 ptrace(PTRACE_GETREGS, child, ...) 来获取到子进程各个寄存器的值,并且打印寄存器的值。

父进程通过调用 ptrace(PTRACE_CONT, child, ...) 让子进程继续执行下去。

从上面的例子可以知道,通过向 ptrace() 函数的 request 参数传入不同的值时,就会有不同的效果。比如传入 PTRACE_TRACEME 就可以让进程进入被追踪状态,而转入 PTRACE_GETREGS 时,就可以获取被追踪的子进程各个寄存器的值等。

http://www.lryc.cn/news/23994.html

相关文章:

  • 【AI绘画】绝美春天插画,人人都是插画师
  • 蓝桥杯入门即劝退(二十四)重复的子字符串(被秒杀)
  • 针对序列级和词元级应用微调BERT(需修改)
  • (四十七)大白话表锁和行锁互相之间的关系以及互斥规则是什么呢?
  • 织梦TXT批量导入TAG标签并自动匹配相关文章插件
  • Sentinel架构篇 - 10分钟带你看滑动窗口算法的应用
  • redis主从复制
  • 近期常见组件漏洞更新:
  • 深度学习常用的激活函数总结
  • Java编程问题top100---基础语法系列(二)
  • 网页打印与导出word实现在A4纸上相同效果
  • 备战英语6级——记录复习进度
  • 实例10:四足机器人运动学逆解可视化与实践
  • Elasticsearch7.8.0版本优化——路由选择
  • Go常量的定义和使用const,const特性“隐式重复前一个表达式”,以及iota枚举常量的使用
  • Git学习(1)pro git阅读
  • PHY自协商
  • 【大数据离线开发】8.2 Hive的安装和配置
  • Capture Modules:车载网络报文捕获模块
  • 数据结构与算法系列之时间与空间复杂度
  • Python代码使用PyQt5制作界面并封装
  • 【Node.js】MySQL数据库的第三方模块(mysql)
  • Docker中安装并配置单机版redis
  • 模拟微信聊天-课后程序(JAVA基础案例教程-黑马程序员编著-第八章-课后作业)
  • html2canvas将页面dom元素内容渲染成图片保存至本地
  • 前端进阶JS运行原理
  • Python识别二维码的两种方法(cv2)
  • 用一个例子告诉你 怎样使用Spark中RDD的算子
  • 什么是跨域? 出现原因及解决方法
  • 低代码系统能够解决哪些痛点?