一、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, &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 \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类型长度为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, &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 \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;}

然后运行,效果如下:

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. android关于快捷方式的检测和添加
  3. Android笔记(1)——不同apk之间传递参数与数据
  4. 【高通SDM660平台 Android(安卓)10.0】(10) --- Camera Sensor l
  5. Android(安卓)在Java代码中设置style属性--使用代码创建Progress
  6. [置顶] 【博客目录】 Start here o(∩_∩)o
  7. 主题:ListView(带图片)显示用法案例
  8. android中三种onClick事件的实现,与对比
  9. Android:监听应用前后台切换及思考

随机推荐

  1. android开发的3种方式
  2. android事件拦截处理机制详解
  3. Android(安卓)数据存储五种方式使用与总
  4. Android(安卓)采用HttpClient提交数据到
  5. Android利用已有控件实现自定义控件
  6. Android开发规范最新详尽版下载
  7. Android(安卓)Intent详解
  8. Unity自动打包工具——Mac上打包android
  9. android修炼进阶之法
  10. 初学Android,数据存储之使用SQLite数据库