内容纲要

ptrace

原理

当传入的request参数为PTRACE_ATTACH,就会起到调试功能

在使用ptrace之前需要在两个进程间建立追踪关系,其中trace可以不做任何事,也可使用prctlPTRACE_TRACEME来进行设置,ptrace编程的主要部分是tracer,它可以通过附着的方式与tracee建立追踪关系,建立之后,可以控制tracee在特定的时候暂停并向tracer发送相应信号,而tracer则通过循环等待waitpid来处理tracee发来的信号,如下图所示:

img

建立追踪关系

在进行追踪前需要先建立追踪关系,相关request有如下4个:

PTRACE_TRACEME:tracee表明自己想要被追踪,这会自动与父进程建立追踪关系,这也是唯一能被tracee使用的request,其他的request都由tracer指定。
PTRACE_ATTACH:tracer用来附着一个进程tracee,以建立追踪关系,并向其发送SIGSTOP信号使其暂停。
PTRACE_SEIZE:像PTRACE_ATTACH附着进程,但它不会让tracee暂停,addr参数须为0,data参数指定一位ptrace选项。
PTRACE_DETACH:解除追踪关系,tracee将继续运行。

反调试

进程跟踪器,类似于gdb watch的调试方法, Linux 系统gdb等调试器,都是通过ptrace系统调用实现,ptrace系统调用有一个特性就是当前进程已经被追踪了,就不能被其他父进程追踪,所以只要我们设计的反调试程序开头就先执行一次ptrace(PTRACE_TRACEME, 0, 0, 0),当gdb再想attach的时候就会发现已经执行了一次不能再执行了从而返回-1,就无法调试了。

识别反调试

image-20220723173912324

ptrace函数详解

#include <sys/ptrace.h>
int ptrace(int request, int pid, int addr, int data);

Ptrace 提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否忽略引起中止的信号。

函数返回值

成功返回0。错误返回-1。errno被设置。

错误
EPERM
特殊进程不可以被跟踪或进程已经被跟踪。
ESRCH
指定的进程不存在
EIO
请求非法

功能描述

PTRACE_TRACEME

形式:ptrace(PTRACE_TRACEME,0 ,0 ,0)
解释:本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。

PTRACE_PEEKTEXT, PTRACE_PEEKDATA

形式:ptrace(PTRACE_PEEKTEXT, pid, addr, data)
ptrace(PTRACE_PEEKDATA, pid, addr, data)
解释:从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。在Linux(i386)中用户代码段与用户数据段重合所以读取代码段和数据段数据处理是一样的。

PTRACE_POKETEXT, PTRACE_POKEDATA

形式:ptrace(PTRACE_POKETEXT, pid, addr, data)
ptrace(PTRACE_POKEDATA, pid, addr, data)
解释:往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据。

PTRACE_PEEKUSR

形式:ptrace(PTRACE_PEEKUSR, pid, addr, data)
解释:从USER区域中读取一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分,它描述了进程中止时的一些状态,如:寄存器值,代码、数据段大小,代码、数据段开始地址等。在Linux(i386)中通过PTRACE_PEEKUSER和PTRACE_POKEUSR可以访问USER结构的数据有寄存器和调试寄存器。

PTRACE_POKEUSR

形式:ptrace(PTRACE_POKEUSR, pid, addr, data)
解释:往USER区域中写入一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为需写入的数据。

PTRACE_CONT

形式:ptrace(PTRACE_CONT, pid, 0, signal)
解释:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。

PTRACE_SYSCALL

形式:ptrace(PTRACE_SYS, pid, 0, signal)
解释:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。

PTRACE_KILL

形式:ptrace(PTRACE_KILL,pid)
解释:杀掉子进程,使它退出。pid表示被跟踪的子进程。

PTRACE_SINGLESTEP

形式:ptrace(PTRACE_KILL, pid, 0, signle)
解释:设置单步执行标志,单步执行一条指令。pid表示被跟踪的子进程。signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。当被跟踪进程单步执行完一个指令后,被跟踪进程被中止,并通知父进程。

PTRACE_ATTACH

形式:ptrace(PTRACE_ATTACH,pid)
解释:跟踪指定pid 进程。pid表示被跟踪进程。被跟踪进程将成为当前进程的子进程,并进入中止状态。

PTRACE_DETACH

形式:ptrace(PTRACE_DETACH,pid)
解释:结束跟踪。 pid表示被跟踪的子进程。结束跟踪后被跟踪进程将继续执行。

PTRACE_GETREGS

形式:ptrace(PTRACE_GETREGS, pid, 0, data)
解释:读取寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有17个基本寄存器的值。

PTRACE_SETREGS

形式:ptrace(PTRACE_SETREGS, pid, 0, data)
解释:设置寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有17个基本寄存器的值。

PTRACE_GETFPREGS

形式:ptrace(PTRACE_GETFPREGS, pid, 0, data)
解释:读取浮点寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。

PTRACE_SETFPREGS

形式:ptrace(PTRACE_SETREGS, pid, 0, data)
解释:设置浮点寄存器值,pid表示被跟踪的子进程,data为用户数据地址。

用途

  1. 编写动态分析工具,如gdb,strace
  2. 反追踪,一个进程只能被一个进程追踪(注:一个进程能同时追踪多个进程),若此进程已被追踪,其他基于ptrace的追踪器将无法再追踪此进程,更进一步可以实现子母进程双线执行动态解密代码等更高级的反分析技术
  3. 代码注入,往其他进程里注入代码。
  4. 不退出进程,进行在线升级。

参考

ptrace函数详解

https://blog.csdn.net/MyArrow/article/details/9617673

最强的反反反ptrace&linux ptrace原理详解