ptrace注入游戏介绍

news/2024/11/17 18:48:11/

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);


http://www.ppmy.cn/news/38536.html

相关文章

云计算基础——云存储技术简介

云存储的种类及其合适的应用 可以把云存储分成块存储与文件存储两类。 块存储 快速更改的单一文件系统针对单一文件大量写的高性能计算&#xff08;HPC&#xff09; 文件存储 文件及内容搜寻Tier-2 NAS多文件大量写入的应用数据大量读写的应用多个使用端都希望读取同一个文…

2007-2020年上市公司数字化转型数字化无形资产占比仅计算结果

1、时间&#xff1a;2007-2020年 2、范围&#xff1a;包括3600多家公司 3、方法说明&#xff1a; 借鉴祁怀锦等&#xff08;2020&#xff09;的方法&#xff0c;根据数字化相关词频手工识别企业数字化相关无形资产占比&#xff0c;相关词频在附件中。 据企业数字化转型的定…

Java 缺失的特性:扩展方法

作者&#xff1a;周密(之叶) 什么是扩展方法 扩展方法&#xff0c;就是能够向现有类型直接“添加”方法&#xff0c;而无需创建新的派生类型、重新编译或以其他方式修改现有类型。调用扩展方法的时候&#xff0c;与调用在类型中实际定义的方法相比没有明显的差异。 为什么需…

jsp056ssm客户关系管理系统的设计与实现hsg570685程序

1&#xff0e;系统登录&#xff1a;系统登录是管理员访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括管理员名、密码和验证码&#xff0c;然后对登录进来的管理员判断身份信息&#xff0c;判断是管理员管理员还是普通用户。 2&#xff0e;管理员管理&#xff1a…

【《C Primer Plus》读书笔记】第14章:结构和其他数据形式

【《C Primer Plus》读书笔记】第14章&#xff1a;结构和其他数据形式14.1 C 结构体定义结构结构体变量的初始化访问结构成员结构作为函数参数指向结构的指针复合字面量和结构&#xff08;C99&#xff09;伸缩型数组成员&#xff08;C99&#xff09;匿名结构&#xff08;C11&am…

Docker之Docker简介

问题&#xff1a;为什么会有docker出现 环境配置如此麻烦&#xff0c;换一台机器&#xff0c;就要重来一次&#xff0c;费力费时。很多人想到&#xff0c;能不能从根本上解决问题&#xff0c;软件可以带环境安装&#xff1f;也就是说&#xff0c;安装的时候&#xff0c;把原始…

网络安全专家最爱用的9大工具

网络安全专家&#xff0c;不是你认为的那种搞破坏的 “黑客”。网络安全专家&#xff0c;即 “ethical hackers”&#xff0c;是一群专门模拟网络安全专家攻击&#xff0c;帮助客户了解自己网络的弱点&#xff0c;并为客户提出改进建议的网络安全专家。 网络安全专家在工作中&a…

python中append()和extend()的区别

在python中&#xff0c;append()和extend()方法都是用于在原有列表上添加元素&#xff0c;两者有什么区别呢&#xff1f; append()方法是将新对象添加到列表的末尾&#xff0c;会修改原有列表&#xff0c;无返回值&#xff0c;示例如下&#xff1a; #!/usr/bin/env python # …