AI智能
改变未来

Android ptrace注入基础


一、Android ptrace注入基础

1.可执行文件建立

首先建立一个ELF可执行文件target,使用ndk-build进行编译

需要先在任意地方建立一个jni,然后在jni目录下建立Android.mk,Application.mk,target.c

Android.mk:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := targetLOCAL_SRC_FILES := target.cinclude $(BUILD_EXECUTABLE)

Application.mk(ABI可以添加其他类型如x86)

APP_ABI := armeabi-v7a

target.c:

#include <stdio.h>int count = 0;void sevenWeapons(int number){char* str = \"Hello,lzh!\";printf(\"%s %d\\n\",str,number);}int main(){while(1){sevenWeapons(count);count++;sleep(1);}return 0;}

编写完成后,使用命令行进入jni目录,执行

ndk-build

然后在jni的上一个目录就能看到一个libs目录,进入找到target,将其push到安卓中,赋予其权限,然后执行,就能看到如下情况

Hello,lzh! 0Hello,lzh! 1...

2. ptrace注入实现(一)

首先还是建立相关文件jni目录
Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := hook1LOCAL_SRC_FILES := hook1.cinclude $(BUILD_EXECUTABLE)

Application.mk

APP_ABI := armeabi-v7a

hook1.c

long getSysCallNo(int pid, struct pt_regs *regs){long scno = 0;scno = ptrace(PTRACE_PEEKTEXT, pid, (void *)(regs->ARM_pc - 4), NULL);if(scno == 0)return 0;if (scno == 0xef000000) {scno = regs->ARM_r7;} else {if ((scno & 0x0ff00000) != 0x0f900000) {return -1;}scno &= 0x000fffff;}return scno;}void hookSysCallBefore(pid_t pid){struct pt_regs regs;int sysCallNo = 0;ptrace(PTRACE_GETREGS, pid, NULL, &regs);sysCallNo = getSysCallNo(pid, &regs);printf(\"Before SysCallNo = %d\\n\",sysCallNo);if(sysCallNo == __NR_write){printf(\"__NR_write: %ld %p %ld\\n\",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);}}void hookSysCallAfter(pid_t pid){struct pt_regs regs;int sysCallNo = 0;ptrace(PTRACE_GETREGS, pid, NULL, &regs);sysCallNo = getSysCallNo(pid, &regs);printf(\"After SysCallNo = %d\\n\",sysCallNo);if(sysCallNo == __NR_write){printf(\"__NR_write return: %ld\\n\",regs.ARM_r0);}printf(\"\\n\");}int main(int argc, char *argv[]){if(argc != 2) {printf(\"Usage: %s <pid to be traced>\\n\", argv[0]);return 1;}pid_t pid;int status;pid = atoi(argv[1]);if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL)){printf(\"Trace process failed:%d.\\n\", errno);return 1;}ptrace(PTRACE_SYSCALL, pid, NULL, NULL);while(1){wait(&status);hookSysCallBefore(pid);ptrace(PTRACE_SYSCALL, pid, NULL, NULL);wait(&status);hookSysCallAfter(pid);ptrace(PTRACE_SYSCALL, pid, NULL, NULL);}ptrace(PTRACE_DETACH, pid, NULL, NULL);return 0;}

hook1.c代码分析

getSysCallNo:

作用:获取system call编号

//获取system call编号long getSysCallNo(int pid, struct pt_regs *regs){long scno = 0;//获取系统调用的SWI指令,这里一共有两个指令EABI,OABI,分别对应两个机器码scno = ptrace(PTRACE_PEEKTEXT, pid, (void *)(regs->ARM_pc - 4), NULL);if(scno == 0)return 0;//为EABI的时候,从r7中直接获取调用号if (scno == 0xef000000) {scno = regs->ARM_r7;} else {//为OABI的时候通过公式立即数(scno)=调用号 | 0x900000,先获取立即数,在计算出调用号if ((scno & 0x0ff00000) != 0x0f900000) {return -1;}scno &= 0x000fffff;}return scno;}

获取到system call的编号之后,我们可以进而获取到各个参数的值,我们可以看到第一个SysCallNo是162,也就是sleep函数。第二个SysCallNo是4,也就是write函数,因为printf本质就是调用write这个系统调用来完成的。

//hook system call前void hookSysCallBefore(pid_t pid){struct pt_regs regs;int sysCallNo = 0;ptrace(PTRACE_GETREGS, pid, NULL, &regs);sysCallNo = getSysCallNo(pid, &regs);printf(\"Before SysCallNo = %d\\n\",sysCallNo);if(sysCallNo == __NR_write){printf(\"__NR_write: %ld %p %ld\\n\",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);}}//hook system call后void hookSysCallAfter(pid_t pid){struct pt_regs regs;int sysCallNo = 0;ptrace(PTRACE_GETREGS, pid, NULL, &regs);sysCallNo = getSysCallNo(pid, &regs);printf(\"After SysCallNo = %d\\n\",sysCallNo);if(sysCallNo == __NR_write){printf(\"__NR_write return: %ld\\n\",regs.ARM_r0);}printf(\"\\n\");}

编写完成后,还是执行ndk-build,得到文件push进安卓手机

然后执行重新执行target文件,再使用下面命令获取target的pid

adb shell \"ps |grep \"target\"\"


获取到pid后,在执行hook1

./hook1 19140

hook2.c代码分析

还是hook之前的代码,代码如下,想了解原理就直接看注释吧
Applicaton.mk

#APP_OPTIM := releaseAPP_PLATFORM := android-15APP_ABI := armeabi-v7aNDK_TOOLCHAIN_VERSION=4.9APP_PIE := false

Android.mk:

LOCAL_PATH := $(call my-dir)APP_CFLAGS := -std=c++11include $(CLEAR_VARS)LOCAL_MODULE    := hook2LOCAL_SRC_FILES := hook2.cinclude $(BUILD_EXECUTABLE)

hook2.c

//获取system call编号long getSysCallNo(int pid, struct pt_regs *regs){long scno = 0;scno = ptrace(PTRACE_PEEKTEXT, pid, (void *)(regs->ARM_pc - 4), NULL);if(scno == 0)return 0;if (scno == 0xef000000) {scno = regs->ARM_r7;} else {if ((scno & 0x0ff00000) != 0x0f900000) {return -1;}scno &= 0x000fffff;}return scno;}void getdata(pid_t child, long addr,char *str, int len){   char *laddr;int i, j;union u {long val;//字符地址char chars[long_size];//字符}data;i = 0;j = len / long_size;//在arm32下long类型长度为4laddr = str;//先处理能除的部分while(i < j) {data.val = ptrace(PTRACE_PEEKDATA,child, addr + i * 4,NULL);//注入memcpy(laddr, data.chars, long_size);//将data.chars复制到laddr处++i;laddr += long_size;//增加一个long的长度}//类似于链表j = len % long_size;//在处理剩下的if(j != 0) {data.val = ptrace(PTRACE_PEEKDATA,child, addr + i * 4,NULL);memcpy(laddr, data.chars, j);}str[len] = \'\\0\';}//原理和get差不多,使用ptrace注入的方式打印void putdata(pid_t child, long addr,char *str, int len){   char *laddr;int i, j;union u {long val;char chars[long_size];}data;i = 0;j = len / long_size;laddr = str;while(i < j) {memcpy(data.chars, laddr, long_size);ptrace(PTRACE_POKEDATA, child,addr + i * 4, data.val);++i;laddr += long_size;}j = len % long_size;if(j != 0) {memcpy(data.chars, laddr, j);ptrace(PTRACE_POKEDATA, child,addr + i * 4, data.val);}}void modifyString(pid_t pid, long addr, long strlen){	//注意这里的strlen是地址的长度!char* str;str = (char *)calloc((strlen+1) * sizeof(char), 1);getdata(pid, addr, str, strlen);//reverse(str);str[0]=\'l\';//将字符串的第一个字符改成\'l\'printf(\"Hook -------\\n\");printf(\"%s\\n\",str);putdata(pid, addr, str, strlen);}void hookSysCallBefore(pid_t pid){struct pt_regs regs;int sysCallNo = 0;ptrace(PTRACE_GETREGS, pid, NULL, &regs);sysCallNo = getSysCallNo(pid, &regs);//printf(\"Before SysCallNo = %d\\n\",sysCallNo);if(sysCallNo == __NR_write){printf(\"__NR_write: %ld %p %ld\\n\",regs.ARM_r0,(void*)regs.ARM_r1,regs.ARM_r2);modifyString(pid, regs.ARM_r1, regs.ARM_r2);}}void hookSysCallAfter(pid_t pid){struct pt_regs regs;int sysCallNo = 0;ptrace(PTRACE_GETREGS, pid, NULL, &regs);sysCallNo = getSysCallNo(pid, &regs);printf(\"After SysCallNo = %d\\n\",sysCallNo);if(sysCallNo == __NR_write){printf(\"__NR_write return: %ld\\n\",regs.ARM_r0);}printf(\"\\n\");}int main(int argc, char *argv[]){if(argc != 2) {printf(\"Usage: %s <pid to be traced>\\n\", argv[0]);return 1;}pid_t pid;int status,errno;pid = atoi(argv[1]);if(0 != ptrace(PTRACE_ATTACH, pid, NULL, NULL)){printf(\"Trace process failed:%d.\\n\", errno);return 1;}ptrace(PTRACE_SYSCALL, pid, NULL, NULL);while(1){wait(&status);hookSysCallBefore(pid);ptrace(PTRACE_SYSCALL, pid, NULL, NULL);//wait(&status);//hookSysCallAfter(pid);//ptrace(PTRACE_SYSCALL, pid, NULL, NULL);}ptrace(PTRACE_DETACH, pid, NULL, NULL);return 0;}

然后运行,效果如下:

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android ptrace注入基础