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    := target
LOCAL_SRC_FILES := target.c

include $(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! 0
Hello,lzh! 1
...

2. ptrace注入实现(一)

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

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := hook1
LOCAL_SRC_FILES := hook1.c

include $(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 := release
APP_PLATFORM := android-15
APP_ABI := armeabi-v7a
NDK_TOOLCHAIN_VERSION=4.9
APP_PIE := false

Android.mk:

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

include $(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;
}

然后运行,效果如下:

Android ptrace注入基础_第1张图片

你可能感兴趣的:(安卓,字符串,android)