一、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, ®s);sysCallNo = getSysCallNo(pid, ®s);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, ®s);sysCallNo = getSysCallNo(pid, ®s);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, ®s);sysCallNo = getSysCallNo(pid, ®s);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, ®s);sysCallNo = getSysCallNo(pid, ®s);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, ®s);sysCallNo = getSysCallNo(pid, ®s);//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, ®s);sysCallNo = getSysCallNo(pid, ®s);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;}
然后运行,效果如下: