ptrace提供了一种使父进程得以监视和控制其它进程的方式,它还能够改变子进程中的寄存器和内核映像,因而可以实现断点调试和系统调用的跟踪。学习linux的ptrace是为学习android adbi框架和古河的libinject做基础。

  ptrace有四个参数:long ptrace(enum __ptrace_request request,pid_t pid,void *addr,void *data);第一个参数是重点,可设如下值:

  1. PTRACE_ME    ptrace(PTRACE_ME,0 ,0 ,0);本进程被其父进程所跟踪

  2. PTRACE_ATTACH  ptrace(PTRACE_ATTACH,pid);跟踪指定pid 进程

  3. PTRACE_PEEKTEXT  ptrace(PTRACE_PEEKTEXT, pid, addr, data);从内存地址中读取一个字节,内存地址由addr给出
  4.  
  5. PTRACE_PEEKDATA  ptrace(PTRACE_PEEKDATA, pid, addr, data);从内存地址中读取一个字节,内存地址由addr给出
  6.  
  7. PTRACE_PEEKUSER  ptrace(PTRACE_PEEKUSR, pid, addr, data)从USER区域中读取一个字节,偏移量为addr
  8.  
  9. PTRACE_POKETEXT  ptrace(PTRACE_POKETEXT, pid, addr, data);往内存地址中写入一个字节。内存地址由addr给出
  10.  
  11. PTRACE_POKEDATA  ptrace(PTRACE_POKEDATA, pid, addr, data);往内存地址中写入一个字节。内存地址由addr给出
  12.  
  13. PTRACE_POKEUSER  ptrace(PTRACE_POKEUSR, pid, addr, data);往USER区域中写入一个字节。偏移量为addr
  14.  
  15. PTRACE_GETREGS   ptrace(PTRACE_GETREGS, pid, 0, data);读取寄存器
  16.  
  17. PTRACE_GETFPREGS  ptrace(PTRACE_GETFPREGS, pid, 0, data);读取浮点寄存器  
  18.  
  19. PTRACE_SETREGS   ptrace(PTRACE_SETREGS, pid, 0, data);设置寄存器
  20.  
  21. PTRACE_SETFPREGS  ptrace(PTRACE_SETREGS, pid, 0, data);设置浮点寄存器
  22.  
  23. PTRACE_CONT   ptrace(PTRACE_CONT, pid, 0, signal);继续执行,signal0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal
  24.  
  25. PTRACE_SYSCALL  ptrace(PTRACE_SYS, pid, 0, signal);内核在子进程做出系统调用或者准备退出的时候暂停它;包含2个步骤:继续执行+系统调用是停止
  26.  
  27. PTRACE_SINGLESTEP  ptrace(PTRACE_KILL, pid, 0, signle);设置单步执行标志,单步执行一条指令
  28.  
  29. PTRACE_DETACH  ptrace(PTRACE_DETACH,pid);结束跟踪

    PTRACE_KILL  ptrace(PTRACE_KILL,pid);杀掉子进程,使它退出
  1.  
  1. 上面就是ptrace函数的用法,大体可以分为2类:控制流程——singleStepcont...;获取或设置内容——getRegssetRegs...。内容的获取和设置就不多说,拿张图来看下如何控制子进程执行:

  1. 1.子进程被ptrace
    2.父进程调用ptrace带参数PTRACE_SYSCALL,让子进程在进入和退出系统调用时暂停;调用完ptrace后,wait触发
    3.子进程要调用syscall(为何syscall?调用系统函数)了,此时因为父进程之前调用了PTRACE_SYSCALL,内核会暂停子进程并给父进程发信号
    4.父进程得到信号后结束wait函数,去调用自己的函数去处理;处理完后调用带PTRACE_SYSCALL参数的ptrace函数,并wait
    5.内核在父进程的ptrace函数后,让子进程继续执行;
    6.子进程如愿的完成系统调用,但在退出系统调用前。因为父进程调用了PTRACE_SYSCALL,所以内核又暂停子进程并发信号给父进程
    7.父进程得到信号从wait中退出,去执行自己的函数;处理完后调用带PTRACE_SYSCALL参数的ptrace函数,并wait
    8.内核在父进程的ptrace函数后,让子进程继续执行;子进程退出系统调用继续执行直到下一个系统调用
    ......
    通过上面这么啰嗦的步骤,我想你一定getptrace是如何控制代码的执行了。当然上面仅仅是PTRACE_SYSCALL,但方法是相通的。ok,本来接下去应该是要实践下了。但是鉴于网上代码太多(直接看参考资料),本人又玩不出新花样,就此略过。这里解释下关于参考资料中乌云的那篇文章的几个知识点:
    1.getSysCallNo
    ARM架构上,所有的系统调用都是通过SWI(Dosint指令类似)来实现的。并且在ARM 架构中有两个SWI指令,分别针对EABIOABI
  1. OABIold abi
  2. mov r0,#34 //设置子功能号位34
  3. SWI 12 //调用12号软中断
  4.  
  5. EABIextend abi
  6. mov r0,#12 // ;调用12号软中断
  7. mov r1,#34 // ;设置子功能号位34
  8. SWI  0
  9.  
  10. SWI{cond} immed_24 // ;immed_24为软中断号(服务类型)
  11. // 1110 1111 0000 0000 -- SWI 0
  1. 而为什么是获取(regs->ARM_pc - 4)地址的内容呢?先解释下pc的概念,pc是取指令的地址对于普通架构pc=当前执行指令地址+1*指令长度;而对于armv7的三级(取值、译码、执行)流水线来说pc=当前指令地址+8(2*4指令长度,arm指令32位,thunb指令16位)。而发生ptrace时,SWI指令是处于译码,故SWI指令的地址为PC-4(不同之处请一起探讨)。
    2.libinject
    libinject中利用ptrace加载自定义so去执行自定义函数,其中获取系统函数地址涉及到/proc/pid/maps(可以看Linux Tips)知识且运用了linux中类似list_entry技术:
    "因为libc.so在内存中的地址是随机的,所以我们需要先获取目标进程的libc.so的加载地址,再获取自己进程的libc.so的加载地址和sleep()在内存中的地址。然后我们就能计算出sleep()函数在目标进程中的地址了。”

ptrace android源码位于/bionic/libc/bionic/ptrace.cpp

  1. long ptrace(int req, ...) {
  2. bool is_peek = (req == PTRACE_PEEKUSR || req == PTRACE_PEEKTEXT || req == PTRACE_PEEKDATA);
  3. long peek_result;
  4.  
  5. va_list args;
  6. va_start(args, req);
  7. pid_t pid = va_arg(args, pid_t);
  8. void* addr = va_arg(args, void*);
  9. void* data;
  10. if (is_peek) {
  11. data = &peek_result;
  12. } else {
  13. data = va_arg(args, void*);
  14. }
  15. va_end(args);
  16.  
  17. long result = __ptrace(req, pid, addr, data);
  18. if (is_peek && result == 0) {
  19. return peek_result;
  20. }
  21. return result;
  22. }

看出实际是调用_ptrace来实现的,位于/bionic/libc/arch-arm/syscalls/__ptrace.S

  1. ENTRY(__ptrace)
  2. mov ip, r7
  3. ldr r7, =__NR_ptrace
  4. swi #0
  5. mov r7, ip
  6. cmn r0, #(MAX_ERRNO + 1)
  7. bxls lr
  8. neg r0, r0
  9. b __set_errno_internal
  10. END(__ptrace)

直接使用SWI调用系统函数ptrace,而_NR_ptrace是个宏定义为系统的调用号(参考android调用号和libc)。下篇以本文知识点开启adbi之旅。

参考资料:

  1 玩转ptrace

  2 安卓动态调试七种武器之离别钩 – Hooking(上)

为何ARM7中PC=PC+8

ptrace 跟踪多线程程序

linux ptrace学习的更多相关文章

  1. Linux内核学习笔记二——进程

    Linux内核学习笔记二——进程   一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...

  2. Linux.NET学习手记(7)

    前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...

  3. Linux.NET学习手记(8)

    上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中.这 ...

  4. 关于《Linux.NET学习手记(8)》的补充说明

    早前的一两天<Linux.NET学习手记(8)>发布了,这一篇主要是讲述OWIN框架与OwinHost之间如何根据OWIN协议进行通信构成一套完整的系统.文中我们还直接学习如何直接操作OW ...

  5. Linux LVM学习总结——扩展卷组VG

    Linux服务器由于应用变更或需求的缘故,有可能出现分区空间不足的情况,此时往往需要进行扩容(要增加分区的空间),而采用LVM的好处就是可以在不需停机的情况下可以方便地调整各个分区大小.如下所示,分区 ...

  6. linux的学习记录随笔

    为什么学习linux 因为操作系统是一种介质,你要接触其中的东西,首先必须要有介质,而linux在服务器端是老大哥的地位,所以呢,学习linux吧. 学习的方式 可以看视频 imooc.百度传课.网易 ...

  7. Linux LVM学习总结——创建卷组VG

    在Linux平台如何创建一个卷组(VG)呢?下面简单介绍一下卷组(VG)的创建步骤.本文实验平台为Red Hat Enterprise Linux Server release 6.6 (Santia ...

  8. 别出心裁的Linux命令学习法

    别出心裁的Linux命令学习法 操作系统操作系统为你完成所有"硬件相关.应用无关"的工作,以给你方便.效率.安全.操作系统的功能我总结为两点:管家婆和服务生: 管家婆:通过进程.虚 ...

  9. linux内核学习之二 一个精简内核的分析(基于时间片轮转)

    一   实验过程及效果 1.准备好相关的代码,分别是mymain.c,mypcb.h,myinterrupt.c ,如下图,make make成功: 在qemu创建的虚拟环境下的运行效果:(使用的命令 ...

随机推荐

  1. 面向青铜的java自学路线

    有经验的人都知道,java还是需要一些路线的,因为java有些知识前后关联挺大的,先学后面和先学前面难度是不一样的. 如果你是新手,只要你知道路线这个东西,起码要比别人强,至少知道可以怎么走(更重要的 ...

  2. web图像化服务管理工具

    在 CentOS 8 中安装 Cockpit Web 控制台 Cockpit 是红帽开发的网页版图像化服务管理工具,优点是无需中间层,且可以管理多种服务. 根据其项目主站描述,Cockpit 有如下特 ...

  3. Python爬虫学习三------requests+BeautifulSoup爬取简单网页

    第一次第一次用MarkDown来写博客,先试试效果吧! 昨天2018俄罗斯世界杯拉开了大幕,作为一个伪球迷,当然也得为世界杯做出一点贡献啦. 于是今天就编写了一个爬虫程序将腾讯新闻下世界杯专题的相关新 ...

  4. ElasticSearch入门篇(保姆级教程)

    本章将介绍:ElasticSearch的作用,搭建elasticsearch的环境(Windows/Linux),ElasticSearch集群的搭建,可视化客户端插件elasticsearch-he ...

  5. SpringBoot 访问jsp文件报错Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/welcome.jsp]的解决办法

    由于SpringBoot不在支持jsp,所以想使用jsp的情况下需要在pom.xml配置servlet依赖,jstl标签库和tomcat依赖.以下是我的pom.xml的配置: (ps:标记红色处为重点 ...

  6. python列表,元组,字典,集合的比较总结

    这四个都是python中的序列,用于存放数据,他们区别总结如下:   列表list 元组tuple 字典dictionary 集合set 是否可变 可变 不可变 可变 可变 是否有序 有序 有序 无序 ...

  7. Python-jet后台管理的使用

    python-django-jet库的使用 1.安装 pip install django-jet 2.配置 将'jet'应用添加到你的Django项目的设置文件settings.py中的INSTAL ...

  8. validator库参数校验

    目录 validator库参数校验若干实用技巧 基本示例 翻译校验错误提示信息 自定义错误提示信息的字段名 自定义结构体校验方法 自定义字段校验方法 自定义翻译方法 validator库参数校验若干实 ...

  9. SpringMVC请求映射handler源码解读

    请求映射源码 首先看一张请求完整流转图(这里感谢博客园上这位大神的图,博客地址我忘记了): 前台发送给后台的访问请求是如何找到对应的控制器映射并执行后续的后台操作呢,其核心为DispatcherSer ...

  10. 图文详解Java对象内存布局

    作为一名Java程序员,我们在日常工作中使用这款面向对象的编程语言时,做的最频繁的操作大概就是去创建一个个的对象了.对象的创建方式虽然有很多,可以通过new.反射.clone.反序列化等不同方式来创建 ...