Android(安卓)ptrace注入基础
16lz
2021-01-25
一、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 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 \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类型长度为4 laddr = 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 \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;}
然后运行,效果如下:
更多相关文章
- 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
- android关于快捷方式的检测和添加
- Android笔记(1)——不同apk之间传递参数与数据
- 【高通SDM660平台 Android(安卓)10.0】(10) --- Camera Sensor l
- Android(安卓)在Java代码中设置style属性--使用代码创建Progress
- [置顶] 【博客目录】 Start here o(∩_∩)o
- 主题:ListView(带图片)显示用法案例
- android中三种onClick事件的实现,与对比
- Android:监听应用前后台切换及思考