Conceptos básicos de la inyección de Android ptrace

Uno, conceptos básicos de inyección de Android ptrace

1. Creación de archivos ejecutables

Primero cree un destino de archivo ejecutable ELF y use ndk-build para compilar

Debe crear un jni en cualquier lugar y luego crear Android.mk, Application.mk, target.c en el directorio jni

Android.mk:

LOCAL_PATH := $(call my-dir)

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

include $(BUILD_EXECUTABLE)

Application.mk (ABI puede agregar otros tipos como 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;
}

Después de escribir, use la línea de comando para ingresar al directorio jni , ejecute ndk-build
y luego puede ver un directorio libs en el directorio anterior de jni , ingrese y encuentre el objetivo , empújelo a Android, déle permiso y ejecute, puede ver lo siguiente Sucediendo

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

2. implementación de la inyección ptrace (1)

Primero, cree el directorio
jni del archivo relevante 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 <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;
}

análisis de código hook1.c

getSysCallNo:

Rol: obtener el número de llamada del sistema

//获取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;    
}

Después de obtener el número de la llamada al sistema, podemos obtener el valor de cada parámetro, podemos ver que el primer SysCallNo es 162, que es la función de suspensión. El segundo SysCallNo es 4, que es la función de escritura, porque printf esencialmente llama a completar la llamada al sistema de escritura.

//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");
}

Después de escribir, ejecute ndk-build para obtener el archivo e introdúzcalo en el teléfono Android

Luego ejecute el archivo de destino nuevamente, y luego use el siguiente comando para obtener el pid del destino

adb shell "ps |grep "target""

Inserte la descripción de la imagen aquí
Después de obtener el pid, ejecute hook1

./hook1 19140

análisis de código hook2.c

El código antes del gancho es el siguiente. Si quieres comprender el principio, solo mira los comentarios.
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 <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;
}

Luego ejecuta, el efecto es el siguiente:

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_43632667/article/details/106738951
Recomendado
Clasificación