ptrace注入游戏介绍

Android系统采用的是Linux内核,很多Linux系统上的技术都可以应用在Android系统上,Android系统上ptrace注入远程进程的技术就是其中一种。本章节将对ptrace注入的完整流程进行介绍。

一、ptrace函数介绍

ptrace注入技术的核心就是ptrace函数,在ptrace注入过程中,将多次调用ptrace函数。Linux的man文档(超链接至: http://man7.org/linux/man-pages/man2/ptrace.2.html)中提到,ptrace函数为一个进程提供了监视和控制其他进程的方法,在注入进程后,父进程还可以读取和修改子进程的内存空间以及寄存器值。

ptrace函数的原型如下所示,其中request参数为一个联合体,该参数决定了ptrace函数的行为,pid参数为远程进程的ID,addr参数与data参数在不同的request参数取值下表示不同的含义。
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

request参数取值较多,由于篇幅所限这里仅介绍一部分ptrace注入进程的过程中需要使用到的request参数。

PTRACE_ATTACH,表示附加到指定远程进程;

PTRACE_DETACH,表示从指定远程进程分离

PTRACE_GETREGS,表示读取远程进程当前寄存器环境

PTRACE_SETREGS,表示设置远程进程的寄存器环境

PTRACE_CONT,表示使远程进程继续运行

PTRACE_PEEKTEXT,从远程进程指定内存地址读取一个word大小的数据

PTRACE_POKETEXT,往远程进程指定内存地址写入一个word大小的数据

二、ptrace注入进程流程

2.1 ptrace注入综述
Ptrace注入的目的是为了将外部的模块注入到游戏进程中,然后执行被注入模块的代码,可以对游戏进程的代码和数据进行修改。目前有两种实现ptrace注入模块到远程进程的方法,其中一种是使用ptrace将shellcode注入到远程进程的内存空间中,然后通过执行shellcode加载远程进程模块;另外一种方法是直接远程调用dlopen\dlsym等函数加载被注入模块并执行指定的代码。两种方法的实现方式稍有差异,但总体来说都是通过ptrace函数在远程进程空间执行代码,本章仅就第二种方式进行详细介绍,附件中包含两种注入方法实现的代码,有兴趣的读者可以了解。

如下图1-1所示,为ptrace注入远程进程的整体流程,整个注入过程并不复杂,但其中有一些细节需要注意,稍有不慎就会造成远程进程崩溃。下面小节将详细介绍流程中的每个步骤。
在这里插入图片描述
2.2 Attach到远程进程
ptrace注入的第一个步骤是先附加到远程进程上,如下所示,附加到远程进程是通过调用request参数为PTRACE_ATTACH的ptrace函数,pid为对应需要附加的远程进程的ID,addr参数和data参数为NULL。

ptrace(PTRACE_ATTACH, pid, NULL, NULL);

在附加到远程进程后,远程进程的执行会被中断,此时父进程可以通过调用waitpid函数来判断子进程是否进入暂停状态。waitpid的函数原型如下所示,其中当options参数为WUNTRACED ,表示若对应pid的远程进程进入暂停状态,则马上返回,可用于等待远程进程进入暂停状态。

pid_t waitpid(pid_t pid,int * status,int options);

2.3 读取和写入寄存器值
在通过ptrace改变远程进程执行流程前,需要先读取远程进程的所有寄存器值进行保存,在detach时向远程进程写入保存的原寄存器值用于恢复远程进程原有的执行流程。

如下所示,为读取和写入寄存器值的ptrace调用,request参数分别为PTRACE_GETREGS和PTRACE_SETREGS,pid为对应进程的ID。

ptrace(PTRACE_GETREGS, pid, NULL, regs);

ptrace(PTRACE_SETREGS, pid, NULL, regs);

在ARM处理器下,data参数的regs为pt_regs结构的指针,从远程进程获取的寄存器值将存储到该结构中,pt_regs结构的定义如下所示,其中ARM_r0成员用于存储R0寄存器的值,函数调用后的返回值会存储在R0寄存器中,ARM_pc成员存储当前执行地址,ARM_sp成员存储当前栈顶地址,ARM_lr成员存储返回地址,ARM_cpsr成员存储状态寄存器的值。

struct pt_regs {

long uregs[18];

};

#define ARM_cpsr uregs[16]

#define ARM_pc uregs[15]

#define ARM_lr uregs[14]

#define ARM_sp uregs[13]

#define ARM_ip uregs[12]

#define ARM_fp uregs[11]

#define ARM_r10 uregs[10]

#define ARM_r9 uregs[9]

#define ARM_r8 uregs[8]

#define ARM_r7 uregs[7]

#define ARM_r6 uregs[6]

#define ARM_r5 uregs[5]

#define ARM_r4 uregs[4]

#define ARM_r3 uregs[3]

#define ARM_r2 uregs[2]

#define ARM_r1 uregs[1]

#define ARM_r0 uregs[0]

#define ARM_ORIG_r0 uregs[17]

2.4 远程进程内存读取和写入数据
调用request参数为PTRACE_PEEKTEXT的ptrace函数可以从远程进程的内存空间中读取数据,一次读取一个word大小的数据。如下所示,其中addr参数为需读取数据的远程进程内存地址,返回值为读取出的数据。

ptrace(PTRACE_PEEKTEXT, pid, pCurSrcBuf, 0);

ptrace(PTRACE_POKETEXT, pid, pCurDestBuf, lTmpBuf) ;

调用request参数为PTRACE_POKETEXT的ptrace函数可以将数据写入到远程进程的内存空间中,同样一次写入一个word大小的数据,ptrace函数的addr参数为要写入数据的远程进程内存地址,data参数为要写入的数据。

写入数据时需要注意,若写入数据长度不是一个word大小的倍数,写入最后一个不足word大小的数据时,要先保存原地址处的高位数据。

如下代码所示,首先通过request参数为PTRACE_PEEKTEXT的ptrace函数读取原内存中的一个word大小的数据,然后将要写入的数据复制到读取出的数据的低位,然后调用ptrace函数将修改后的数据写入远程进程的内存地址处。

lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurDestBuf, NULL);

memcpy((void *)(&lTmpBuf), pCurSrcBuf, nRemainCount);

if (ptrace(PTRACE_POKETEXT, pid, pCurDestBuf, lTmpBuf) < 0)

{

LOGD("Write Remote Memory error, MemoryAddr:0x%lx", (long)pCurDestBuf);

return -1;

}

2.5 远程调用函数
在ARM处理器中,函数调用的前四个参数通过R0-R3寄存器来传递,剩余参数按从右到左的顺序压入栈中进行传递。如下代码所示,在远程调用函数前,需要先判断函数调用的参数个数,如果小于4个,则将参数按顺序分别写入R0-R3寄存器中,若大于4个,则首先调整SP寄存器在栈中分配空间,然后通过调用ptrace函数将剩余参数写入到栈中。

for (i = 0; i < num_params && i < 4; i ++) {

 regs->uregs[i] = parameters[i];    

}

if (i < num_params) {

regs->ARM_sp -= (num_params - i) * sizeof(long) ;

if (ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶meters[i], (num_params - i) * sizeof(long)) == -1)

return -1;

}

在写入函数的参数后,修改进程的PC寄存器为需要执行的函数地址。这里有一点需要注意,在ARM架构下有ARM和Thumb两种指令,因此在调用函数前需要判断函数被解析成哪种指令,如下所示的代码就是通过地址的最低位是否为1来判断调用地址处指令为ARM或Thumb,若为Thumb指令,则需要将最低位重新设置为0,并且将CPSR寄存器的T标志位置位,若为ARM指令,则将CPSR寄存器的T标志位复位。

if (regs->ARM_pc & 1) { /* thumb */

regs->ARM_pc &= (~1u);

regs->ARM_cpsr |= CPSR_T_MASK;

} else { /* arm */

regs->ARM_cpsr &= ~CPSR_T_MASK;

}

在使远程进程恢复运行前,还需要设置远程进程的LR寄存器值为0,并且在在本地进程调用options参数为WUNTRACED的waitpid函数等待远程进程重新进入暂停状态。远程进程的函数调用结束后,会跳转到LR寄存器存储的地址处,但由于LR寄存器被设置为0,会导致远程进程执行出错,此时进程会进入暂停状态,本地进程等待结束,通过读取远程进程的R0寄存器可以获取远程函数调用的返回结果,以上就是一次完整地调用远程函数的过程。

在ptrace注入流程中需要多次调用函数,除了调用被注入模块的函数外,还需要调用mmap函数在远程进程地址空间内分配内存,调用dlopen函数来远程加载被注入模块,调用dlsym函数来获取被注入模块对应函数的地址,调用dlclose函数来关闭加载的模块。这些函数的原型如下所示,

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);

void * dlopen( const char * pathname, int mode);

voiddlsym(voidhandle,constchar*symbol);

int dlclose (void *handle);

在调用这些函数前,需要首先获取到这些系统函数在远程进程中的地址,mmap函数是在”/system/lib/libc.so”模块中,dlopen、dlsym与dlclose函数均是在”/system/bin/linker”模块中。

读取”/proc/pid/maps”可以获取到系统模块在本地进程和远程进程的加载基地址,要获取远程进程内存空间中mmap等函数的虚拟地址,可通过计算本地进程中mmap等函数相对于模块的地址偏移,然后使用此地址偏移加上远程进程对应模块的基地址,这个地址就是远程进程内存空间中对应函数的虚拟地址。

2.6 恢复寄存器值
在从远程进程detach前,需要将远程进程的原寄存器环境恢复,保证远程进程原有的执行流程不被破坏,如果不恢复寄存器值,detach时会导致远程进程的崩溃。

2.7 Detach进程
从远程进程脱离是ptrace注入的最后一个步骤,在detach后被注入进程将继续运行。如下所示,从远程进程detach是调用request参数为PTRACE_DETACH的ptrace函数。

ptrace(PTRACE_DETACH, pid, NULL, 0);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
shim ptrace是一个用于操作进程的系统调用接口。它允许一个进程跟踪、控制和检查另一个进程的执行情况。通过shim ptrace,一个进程可以检查另一个进程的寄存器、内存和系统调用,并有能力修改它们的执行过程。 shim ptrace通常用于调试、监视和诊断进程。调试器可以使用shim ptrace来实现断点、单步执行和修改变量等调试功能。另外,shim ptrace还可以用于分析和检查进程的运行,例如查找进程中的内存泄漏、跟踪系统调用和信号处理。 在使用shim ptrace时,一个进程可以作为被跟踪进程,另一个进程则作为跟踪进程。跟踪进程使用ptrace系统调用来发送指令,而被跟踪进程则接收并执行指令。通过这种方式,跟踪进程可以获取被跟踪进程的状态信息,并对其进行操作。 对于被跟踪进程,它会在指令执行之前接收到跟踪进程发送的指令,并根据指令的要求进行操作。例如,跟踪进程可以用ptrace(PTRACE_PEEKDATA, pid, addr, data)来读取被跟踪进程中地址为addr的内存数据,并将结果保存在data中。类似地,跟踪进程也可以使用ptrace(PTRACE_POKEDATA, pid, addr, data)来修改被跟踪进程的内存值。 总之,shim ptrace是一个强大的工具,允许进程间相互跟踪、控制和修改执行过程。它在调试、监视和诊断进程方面扮演着重要角色,为开发人员提供了有效的方法来分析和改进程序的执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

douluo998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值