Linux ptrace 原理,从gdb原理学习ptrace调用
Linux的ptrace系统调用,是Android二进制hook框架adbi的核心。因此学习adbi之前,先学习一下ptrace()函数。
ptrace介绍
ptrace可以拆开来,看作Process Trace,也就是进程跟踪,它提供了父进程观察并修改子进程的能力,并允许父进程检查和替换子进程的内核镜像(包括寄存器)等。
它的基本原理是:调用ptrace后,所有发给子进程的信号(SIGKILL除外),都会先发送到父进程,子进程被阻塞,此时子进程处于TASK_TRACED状态。父进程接收信号后,即可对子进程进程观察或修改后运行之。
ptrace函数原型为:
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data)
1). enum __ptrace_request request:ptrace要执行的命令。
2). pid_t pid: ptrace要跟踪的进程id。
3). void *addr: 要监控的内存地址。
4). void *data: 存放读取出的或者要写入的数据。
gdb原理
ptrace调用的功能强大,包括gdb等工具都依靠它来实现,下面通过gdb原理介绍。
gdb原理是利用ptrace系统调用,使被调试程序成为gdb子进程,发送给调试程序的信号(SIGKILL除外)被gdb先截获。gdb根据截获的信号,查看/修改程序相应内存、寄存器内容,并控制被调试程序继续执行。调试程序的核心是断点和单步,下面分开讲如何实现这两个机制。
gdb建立调试关系
使用gdb调试程序,可以直接gdb ./test,也可以gdb 的方式。这对应ptrace建立跟踪关系的两种方法。
1)通过fork+execve执行被调试程序。子进程在执行execve之前调用ptrace(PTRACE_TRACEME)。
2)通过attach的方式。gdb通过调用ptrace(PTRACE_ATTACH,pid,…),建立也被调试进程间的父子关系,使自己成为被调试程序的父进程,通过attach建立的调试关系可以通过ptrace (PTRACE_DETACH,pid,…)解除。
断点原理
程序调试时,常用的一个断点功能是“break ”,当执行到断点行时被调试程序会停止,等待gdb操作。
断点的实现原理,就是在指定位置插入断点指令,当执行到断点时cpu产生SIGTRAP信号。该信号被gdb捕获并进行断点命中判断,当判断此信号是由断点产生时等待用户输入,并执行下一步操作。否则继续。
在程序中设置断点,就是先将原来的指令保存,然后向该位置写入int 3中断指令。断点执行完后,恢复int 3处指令并将cpu的IP寄存器指向前移。这在前面写IA-32处理器的调试支持时有讲过。
单步执行原理
单步执行指在运行调试程序时,让程序运行一条指令后停下。gdb中常用的命令有next/step(语句单步)和nexti/stepi(指令单步)。在Linux上,指令单步是通过ptrace来实现。调用ptrace(PTRACE_SINGLESTEP,pid,…)可以使被调试的进程在每执行完一条指令后,产生一个SIGTRAP信号,gdb捕获后运行。下面看一个例子:
child = fork();
if(child == 0) {
execl("./HelloWorld", "HelloWorld", NULL);
} else {
ptrace(PTRACE_ATTACH,child,NULL,NULL);
while(1) {
wait(&val);
if(WIFEXITED(val))
break;
count++;
ptrace(PTRACE_SINGLESTEP,child,NULL,NULL);
}
printf("Total Instruction number= %d\n",count);
}
这个程序中,子进程调用execl执行HelloWorld程序,父进程通过ptrace(PTRACE_ATTACH,child,NULL,NULL);使子进程一步一停,以统计子进程一共执行了多少条指令。当然这时你也可以查看EIP寄存器存放的指令,或是某个变量的值。当然前提是要知识这个变量在子进程内存映像中的地址。
指令单步可以通过硬件实现,如x86架构处理器通过设置EFLAGS寄存器的TF标志位实现,每执行一条指令,产生一个异常。也可以通过软件实现,即在每条指令后面加入一条断点指令。这与上面讲的断点原理类似。而语句单步的实现基于指令单步,在《软件调试》里面有相当精彩的表述。
当然gdb的实现远比上面讲的要复杂。它能使我们方便的观察、修改被调试进程,比如通过行号、函数名、变量名等。而要实现这些,一是要在编译时提供足够信息,如使用gcc的-g调试选项,gcc会将一些程序信息放到ELF文件中,包括函数符号表、行号、变量信息、宏定义等,以给gdb提供调试资料。二是要对ELF文件格式,进程的内存映像布局以及程序指令码十分熟悉。这样才能保证在正确的时机,找到正确的地址并链接回正确的代码片段。这些可以通过阅读gdb源码分析了解。
小结
ptrace可以实时观察与修改另一个进程的运行。掌握了它的使用,就能开发出很多用户态下不可能实现的应用,当然这可能需要我们掌握编译、文件格式、程序内存布局等相当多的底层知识。
回顾一下ptrace的使用:
用PTRACE_ATTACH或者PTRACE_TRACEME 建立进程间的跟踪关系。
PTRACE_PEEKTEXT, PTRACE_PEEKDATA, PTRACE_PEEKUSR等读取子进程内存/寄存器中保留的值。
PTRACE_POKETEXT, PTRACE_POKEDATA, PTRACE_POKEUSR等把值写入到被跟踪进程的内存/寄存器中。
用PTRACE_CONT,PTRACE_SYSCALL, PTRACE_SINGLESTEP控制被跟踪进程以何种方式继续运行。
PTRACE_DETACH, PTRACE_KILL 脱离进程间的跟踪关系。
提示
进程状态TASK_TRACED用以表示当前进程因为被父进程跟踪而被系统停止。
如在子进程结束前,父进程结束,则trace关系解除。
利用attach建立起来的跟踪关系,虽然ps看到双方为父子关系,但在”子进程”中调用getppid()仍会返回原来的父进程id。
已经被trace的进程,不能再次被attach。
即使是用PTRACE_TRACEME建立起来的跟踪关系,也可以用DETACH的方式予以解除。
因为进入、退出系统调用都会触发一次SIGTRAP,所以通常的做法是在进入的时候读取系统调用的参数,在退出的时候读取系统调用的返回值。
Linux ptrace 原理,从gdb原理学习ptrace调用相关推荐
- linux命令封装sh,shell脚本学习之调用脚本将文件打包zip的方法示例
前言 本文主要给大家介绍的是关于调用脚本将文件打包zip的相关资料,分享出来供大家参考学习,下面来一起看看详细的介绍: 最近刚刚接触shell脚本,写了一点简单的练手.这里是用python调用脚本执行 ...
- GDB调试之ptrace实现原理
目录 ptrace系统调用 ptrace使用示例 ptrace实现原理 进入被追踪模式(PTRACE_TRACEME操作) 获取被调试进程的内存数据(PTRACE_PEEKTEXT / PTRACE_ ...
- linux设备驱动程序架构的研究,Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介)...
Linux设备驱动程序学习(12) -Linux设备模型(底层原理简介) 以<LDD3>的说法:Linux设备模型这部分内容可以认为是高级教材,对于多数程序作者来说是不必要的.但是我个人认 ...
- Linux下调试器工作原理
Linux下调试器工作原理之一-基础篇 介绍关于Linux下的调试器实现的主要组成部分--ptrace系统调用.本文中出现的代码都在32位的Ubuntu系统上开发.请注意,这里出现的代码是同平台紧密相 ...
- Linux ptrace系统调用详解:利用 ptrace 设置硬件断点
<GDB调试之ptrace实现原理> <C语言程序调用栈:backtrace+backtrace_symbols+backtrace_symbols_fd> <strac ...
- Linux jprobe的使用和原理
文章目录 前言 一.demo 1.1 demo演示 1.2 struct jprobe 二.jprobe 原理 2.1 原理简介 1.2 原理详解 三.源码解析 3.1 struct jprobe 3 ...
- linux代码工具tag,gcov-dump原理分析_Linux平台代码覆盖率测试
第 16 页 LINES tag: tag_lines() 函数 3.4 LINES tag: tag_lines() 函数static void tag_lines ( const char * f ...
- 【Linux内核】内存映射原理
[Linux内核]内存映射原理 物理地址空间 物理地址是处理器在总线上能看到的地址,使用RISC(Reduced Instruction Set Computing精简指令集)的处理器通常只实现一个物 ...
- Linux select函数用法和原理
select函数的用法和原理 Linux上的select函数 select函数用于检测一组socket中是否有事件就绪.这里的事件为以下三类: 读事件就绪 在socket内核中,接收缓冲区中的字节数大 ...
最新文章
- .NET(C#):警惕PLINQ结果的无序性
- 庆祝Dojo中文博客成为CSDN博客专家!
- 【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术
- 在鹅厂当程序媛是什么体验?
- jzoj2152-终极数【堆】
- linux内核驱动子系统,linux内核中的MFD子系统
- 案例学习BlazeDS+Spring之十:Chat(
- C++中algorithm头文件中一些函数使用记录
- Java快递物流运输管理系统源码
- 下载pyboard的flash中的驱动程序_如何安装爱普生打印机驱动程序
- ODAC的tnsnames.ora文件
- 《东周列国志》第十四回 卫侯朔抗王入国 齐襄公出猎遇鬼
- 2021最新域名授权系统网站源码 全新一键安装源码+卡密自助授权+全新UI界面
- 测试开发进阶——常用中间件概念——线程与线程池理解
- [译] 海量视频时代下的内容发现之旅
- Photoshop 抠图方式
- 最终分化的SH-SY5Y细胞为研究多巴胺激动剂的神经保护作用提供了一个模型系统
- 穷养儿,富养女一一原来是指这样
- 近期研究方向 (内部参考)
- ROS 机器人模型节点的运动控制原理