Android arm64(aarch64)中的so注入(inject) - 兼容x86 and arm

实现Android arm64(aarch64)中的so注入(inject) ,并且兼容x86和arm。如果没有搞错,这是国内外第一份公开的arm64注入的针对性完整资料~~

代码基于 ariesjzj 的 《Android中的so注入(inject)和挂钩(hook)
- For both x86 and arm
》,增加了对arm64的支持。(目前已经开始有64位安卓手机)

同样,代码由jni中以下三个文件构成。

inject.c

Android.mk

Application.mk

其中Android.mk一样,Application.mk中

APP_ABI := arm64-v8a

inject.c如下:

#include <stdio.h>
#include <stdlib.h>
#include <asm/user.h>
#include <asm/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>
#include <android/log.h>
#include <sys/uio.h>

#if defined(__i386__)
#define pt_regs         user_regs_struct
#elif defined(__aarch64__)
#define pt_regs         user_pt_regs
#define uregs	regs
#define ARM_pc	pc
#define ARM_sp	sp
#define ARM_cpsr	pstate
#define ARM_lr		regs[30]
#define ARM_r0		regs[0]
#define PTRACE_GETREGS PTRACE_GETREGSET
#define PTRACE_SETREGS PTRACE_SETREGSET
#endif    

#define ENABLE_DEBUG 1    

#if ENABLE_DEBUG
#define  LOG_TAG "INJECT"
#define  LOGD(fmt, args...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, fmt, ##args)
#define DEBUG_PRINT(format,args...) \
    LOGD(format, ##args)
#else
#define DEBUG_PRINT(format,args...)
#endif    

#define CPSR_T_MASK     ( 1u << 5 )    

#if defined(__aarch64__)
const char *libc_path = "/system/lib64/libc.so";
const char *linker_path = "/system/bin/linker64";
#else
const char *libc_path = "/system/lib/libc.so";
const char *linker_path = "/system/bin/linker";
#endif

int ptrace_readdata(pid_t pid,  uint8_t *src, uint8_t *buf, size_t size)
{
    long i, j, remain;
    uint8_t *laddr;
    size_t bytes_width = sizeof(long);

    union u {
        long val;
        char chars[bytes_width];
    } d;    

    j = size / bytes_width;
    remain = size % bytes_width;    

    laddr = buf;    

    for (i = 0; i < j; i ++) {
        d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
        memcpy(laddr, d.chars, bytes_width);
        src += bytes_width;
        laddr += bytes_width;
    }    

    if (remain > 0) {
        d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
        memcpy(laddr, d.chars, remain);
    }    

    return 0;
}    

int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size)
{
    long i, j, remain;
    uint8_t *laddr;
    size_t bytes_width = sizeof(long);

    union u {
        long val;
        char chars[bytes_width];
    } d;    

    j = size / bytes_width;
    remain = size % bytes_width;    

    laddr = data;    

    for (i = 0; i < j; i ++) {
        memcpy(d.chars, laddr, bytes_width);
        ptrace(PTRACE_POKETEXT, pid, dest, d.val);    

        dest  += bytes_width;
        laddr += bytes_width;
    }    

    if (remain > 0) {
        d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0);
        for (i = 0; i < remain; i ++) {
            d.chars[i] = *laddr ++;
        }    

        ptrace(PTRACE_POKETEXT, pid, dest, d.val);
    }    

    return 0;
}    

#if defined(__arm__) || defined(__aarch64__)
int ptrace_call(pid_t pid, uintptr_t addr, long *params, int num_params, struct pt_regs* regs)
{
    int i;
#if defined(__arm__)
    int num_param_registers = 4;
#elif defined(__aarch64__)
    int num_param_registers = 8;
#endif

    for (i = 0; i < num_params && i < num_param_registers; i ++) {
        regs->uregs[i] = params[i];
    }    

    //
    // push remained params onto stack
    //
    if (i < num_params) {
        regs->ARM_sp -= (num_params - i) * sizeof(long) ;
        ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long));
    }    

    regs->ARM_pc = addr;
    if (regs->ARM_pc & 1) {
        /* thumb */
        regs->ARM_pc &= (~1u);
        regs->ARM_cpsr |= CPSR_T_MASK;
    } else {
        /* arm */
        regs->ARM_cpsr &= ~CPSR_T_MASK;
    }    

    regs->ARM_lr = 0;        

    if (ptrace_setregs(pid, regs) == -1
            || ptrace_continue(pid) == -1) {
        printf("error\n");
        return -1;
    }    

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f) {
        if (ptrace_continue(pid) == -1) {
            printf("error\n");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }  

    return 0;
}    

#elif defined(__i386__)
long ptrace_call(pid_t pid, uintptr_t addr, long *params, int num_params, struct user_regs_struct * regs)
{
    regs->esp -= (num_params) * sizeof(long) ;
    ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long));    

    long tmp_addr = 0x00;
    regs->esp -= sizeof(long);
    ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr));     

    regs->eip = addr;    

    if (ptrace_setregs(pid, regs) == -1
            || ptrace_continue( pid) == -1) {
        printf("error\n");
        return -1;
    }    

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f) {
        if (ptrace_continue(pid) == -1) {
            printf("error\n");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }  

    return 0;
}
#else
#error "Not supported"
#endif    

int ptrace_getregs(pid_t pid, struct pt_regs * regs)
{
#if defined (__aarch64__)
		int regset = NT_PRSTATUS;
		struct iovec ioVec;

		ioVec.iov_base = regs;
		ioVec.iov_len = sizeof(*regs);
    if (ptrace(PTRACE_GETREGSET, pid, (void*)regset, &ioVec) < 0) {
        perror("ptrace_getregs: Can not get register values");
        printf(" io %llx, %d", ioVec.iov_base, ioVec.iov_len);
        return -1;
    }    

    return 0;
#else
    if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) {
        perror("ptrace_getregs: Can not get register values");
        return -1;
    }    

    return 0;
#endif
}    

int ptrace_setregs(pid_t pid, struct pt_regs * regs)
{
#if defined (__aarch64__)
		int regset = NT_PRSTATUS;
		struct iovec ioVec;

		ioVec.iov_base = regs;
		ioVec.iov_len = sizeof(*regs);
    if (ptrace(PTRACE_SETREGSET, pid, (void*)regset, &ioVec) < 0) {
        perror("ptrace_setregs: Can not get register values");
        return -1;
    }    

    return 0;
#else
    if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) {
        perror("ptrace_setregs: Can not set register values");
        return -1;
    }    

    return 0;
#endif
}    

int ptrace_continue(pid_t pid)
{
    if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
        perror("ptrace_cont");
        return -1;
    }    

    return 0;
}    

int ptrace_attach(pid_t pid)
{
    if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {
        perror("ptrace_attach");
        return -1;
    }    

    int status = 0;
    waitpid(pid, &status , WUNTRACED);    

    return 0;
}    

int ptrace_detach(pid_t pid)
{
    if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
        perror("ptrace_detach");
        return -1;
    }    

    return 0;
}    

void* get_module_base(pid_t pid, const char* module_name)
{
    FILE *fp;
    long addr = 0;
    char *pch;
    char filename[32];
    char line[1024];    

    if (pid < 0) {
        /* self process */
        snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
    } else {
        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
    }    

    fp = fopen(filename, "r");    

    if (fp != NULL) {
        while (fgets(line, sizeof(line), fp)) {
            if (strstr(line, module_name)) {
                pch = strtok( line, "-" );
                addr = strtoull( pch, NULL, 16 );   

                if (addr == 0x8000)
                    addr = 0;    

                break;
            }
        }    

        fclose(fp) ;
    }    

    return (void *)addr;
}    

void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
{
    void* local_handle, *remote_handle;    

    local_handle = get_module_base(-1, module_name);
    remote_handle = get_module_base(target_pid, module_name);    

    DEBUG_PRINT("[+] get_remote_addr: local[%llx], remote[%llx]\n", local_handle, remote_handle);    

    void * ret_addr = (void *)((uintptr_t)local_addr + (uintptr_t)remote_handle - (uintptr_t)local_handle);    

#if defined(__i386__)
    if (!strcmp(module_name, libc_path)) {
        ret_addr += 2;
    }
#endif
    return ret_addr;
}    

int find_pid_of(const char *process_name)
{
    int id;
    pid_t pid = -1;
    DIR* dir;
    FILE *fp;
    char filename[32];
    char cmdline[256];    

    struct dirent * entry;    

    if (process_name == NULL)
        return -1;    

    dir = opendir("/proc");
    if (dir == NULL)
        return -1;    

    while((entry = readdir(dir)) != NULL) {
        id = atoi(entry->d_name);
        if (id != 0) {
            sprintf(filename, "/proc/%d/cmdline", id);
            fp = fopen(filename, "r");
            if (fp) {
                fgets(cmdline, sizeof(cmdline), fp);
                fclose(fp);    

                if (strcmp(process_name, cmdline) == 0) {
                    /* process found */
                    pid = id;
                    break;
                }
            }
        }
    }    

    closedir(dir);
    return pid;
}    

uint64_t ptrace_retval(struct pt_regs * regs)
{
#if defined(__arm__) || defined(__aarch64__)
    return regs->ARM_r0;
#elif defined(__i386__)
    return regs->eax;
#else
#error "Not supported"
#endif
}    

uint64_t ptrace_ip(struct pt_regs * regs)
{
#if defined(__arm__) || defined(__aarch64__)
    return regs->ARM_pc;
#elif defined(__i386__)
    return regs->eip;
#else
#error "Not supported"
#endif
}    

int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs)
{
    DEBUG_PRINT("[+] Calling %s in target process.\n", func_name);
    if (ptrace_call(target_pid, (uintptr_t)func_addr, parameters, param_num, regs) == -1)
        return -1;    

    if (ptrace_getregs(target_pid, regs) == -1)
        return -1;
    DEBUG_PRINT("[+] Target process returned from %s, return value=%llx, pc=%llx \n",
            func_name, ptrace_retval(regs), ptrace_ip(regs));
    return 0;
}    

int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size)
{
    int ret = -1;
    void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;
    void *local_handle, *remote_handle, *dlhandle;
    uint8_t *map_base = 0;
    uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;    

    struct pt_regs regs, original_regs;
    long parameters[10];    

    DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);    

    if (ptrace_attach(target_pid) == -1)
        goto exit;    

    if (ptrace_getregs(target_pid, &regs) == -1)
        goto exit;    

    /* save original registers */
    memcpy(&original_regs, &regs, sizeof(regs));    

    mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
    DEBUG_PRINT("[+] Remote mmap address: %llx\n", mmap_addr);    

    /* call mmap */
    parameters[0] = 0;  // addr
    parameters[1] = 0x4000; // size
    parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC;  // prot
    parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
    parameters[4] = 0; //fd
    parameters[5] = 0; //offset    

    if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, &regs) == -1)
        goto exit;    

    map_base = ptrace_retval(&regs);  

    dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen );
    dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym );
    dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose );
    dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror );    

    DEBUG_PRINT("[+] Get imports: dlopen: %llx, dlsym: %llx, dlclose: %llx, dlerror: %llx\n",
            dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);    

    printf("library path = %s\n", library_path);
    ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);  

    parameters[0] = map_base;
    parameters[1] = RTLD_NOW| RTLD_GLOBAL;     

    if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)
        goto exit;    

    void * sohandle = ptrace_retval(&regs);
    if(!sohandle) {
    		if (ptrace_call_wrapper(target_pid, "dlerror", dlerror_addr, 0, 0, &regs) == -1)
      	  goto exit;    

    		uint8_t *errret = ptrace_retval(&regs);
    		uint8_t errbuf[100];
    		ptrace_readdata(target_pid, errret, errbuf, 100);
  	}

#define FUNCTION_NAME_ADDR_OFFSET       0x100
    ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1);
    parameters[0] = sohandle;
    parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET;     

    if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, &regs) == -1)
        goto exit;    

    void * hook_entry_addr = ptrace_retval(&regs);
    DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr);    

#define FUNCTION_PARAM_ADDR_OFFSET      0x200
    ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1);
    parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET;      

    if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, &regs) == -1)
        goto exit;        

    printf("Press enter to dlclose and detach\n");
    getchar();
    parameters[0] = sohandle;       

    if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, &regs) == -1)
        goto exit;    

    /* restore */
    ptrace_setregs(target_pid, &original_regs);
    ptrace_detach(target_pid);
    ret = 0;    

exit:
    return ret;
}    

int main(int argc, char** argv) {
    pid_t target_pid;
    target_pid = find_pid_of("system_server");
    if (-1 == target_pid) {
        printf("Can't find the process\n");
        return -1;
    }
    //target_pid = find_pid_of("/data/test");
    inject_remote_process(target_pid, "/data/libhello.so", "hook_entry",  "I'm parameter!", strlen("I'm parameter!"));
    return 0;
}    

另外,也用到libhello.so,代码基本不变,只是其中Application.mk中也指定生成arm64。

APP_ABI := arm64-v8a

最后要注意一点,目前arm64的手机应该都是Android 5.0+的,可能会因selinux的权限控制造成注入失败,比如例子中的system_server会失败,但注入calendar没问题。

此时可以暂时关闭掉selinux(执行setenforce 0),要针对解决就需要配置selinux了。

时间: 2024-10-11 03:15:44

Android arm64(aarch64)中的so注入(inject) - 兼容x86 and arm的相关文章

Android下的挂钩(hook)和代码注入(inject)

Android是基于linux内核的操作系统,根据语言环境可以简单的划分为java层.native C层.linux内核层.java层通过jni与native层交互,使用linux提供的底层函数功能. 因此,类似linux系统,我们可以在Android下实现对另一个进程的挂钩和代码注入.在这简单介绍下挂钩和代码注入的方法和两个库,以及针对<刀塔传奇>实现的代码注入. 利用libinject实现so注入和API Hook 一. so注入 Linux上有一个强大的系统调用ptrace,它提供了父进

Android中通过进程注入技术修改系统返回的Mac地址

致谢 感谢看雪论坛中的这位大神,分享了这个技术:http://bbs.pediy.com/showthread.php?t=186054,从这篇文章中学习到了很多内容,如果没有这篇好文章,我在研究的过程中会遇到很多困难,说不定我就放弃了~~在此感谢他. 前言 之前的几篇文章都是在介绍了OC的相关知识,之前的半个月也都是在搞IOS的相关东西,白天上班做Android工作,晚上回家还有弄IOS,感觉真的很伤了.不过OC的知识也学习了差不多了.不过在这段时间遗留了很多Android方面的问题都没有进行

Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook

前文介绍了导入表hook,现在来说下导出表的hook.导出表的hook的流程如下.1.获取动态库基值 1 void* get_module_base(pid_t pid, const char* module_name){ 2 FILE* fp; 3 long addr = 0; 4 char* pch; 5 char filename[32]; 6 char line[1024]; 7 8 // 格式化字符串得到 "/proc/pid/maps" 9 if(pid < 0){

Android中通过进程注入技术修改广播接收器的优先级

前言 这个周末又没有吊事,在家研究了如何通过进程的注入技术修改广播接收器的优先级,关于这个应用场景是很多的,而且也很重要,所以就很急的去fixed了. Android中的四大组件中有一个广播:Broadcast 关于它的相关知识可以转战:http://blog.csdn.net/jiangwei0910410003/article/details/19150705 我们这里就不做太多解释了,现在来看一下问题: 知识前提 这篇文章和我之前介绍一篇文章: Andrdoid中对应用程序的行为拦截实现方

使用APT实现Android中View的注入

个人博客 http://www.milovetingting.cn 使用APT实现Android中View的注入 前言 APT是Annotation Processing Tool的简写,通过在Java编译时期,处理注解,生成代码.APT在ButterKnife.Dagger2等框架中都有应用.下面通过使用APT,实现一个类似ButterKnife的简单的View注入的框架.(参考Jett老师的课程) ButterKnife的实现原理 既然准备实现类似ButterKnife的框架,那么我们就需要

SignalR中的依赖注入

什么是依赖注入? 如果你已经熟悉依赖注入可以跳过此节. 依赖注入 (DI) 模式下,对象并不为自身的依赖负责. 下边的例子是一个主动 DI. 假设你有个对象需要消息日志.你可能定义了一个日志接口: C# interface ILogger { void LogMessage(string message); } 在你的对象中,你可以创建一个 ILogger来记录消息. C# // 不用依赖注入. class SomeComponent { ILogger _logger = new FileLo

JavaEE开发之Spring中的依赖注入与AOP编程

一.快速创建Mava管理的Spring工程 因为本篇博客是讨论关于Spring的东西,所以我们就不创建WebApp的工程了.我们使用Spring来快速的创建一个Maven管理的工程.如下所示找到File->New->Maven Project选项来创建一个新的Maven Project,具体如下所示: 下方我们选择创建一个简单的Maven工程,跳过模板的选择.上篇博客我们在创建Maven工程时,是没有选择下方这个选项的,然后我们选择了一个WebApp的模板.而本篇博客,我们不需要WebApp的

详解AngularJS中的依赖注入

依赖注入 一般来说,一个对象只能通过三种方法来得到它的依赖项目: 我们可以在对象内部创建依赖项目 我们可以将依赖作为一个全局变量来进行查找或引用 我们可以将依赖传递到需要它的地方 在使用依赖注入时,我们采用的是第三种方式(另外两种方式都会引起其他困难的挑战,例如污染全局作用域以及使隔离变得几乎不可能).依赖注入是一种设计模式,它移除了硬编码依赖,因此使得我们可以在运行中随时移除并改变依赖项目. 在运行过程中能够修改依赖项目的能力允许我们创建隔离环境,这对于测试来说是非常理想的.我们可以用测试环境

深入理解net core中的依赖注入、Singleton、Scoped、Transient(四)

相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(二) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(三) 一.什么是依赖注入(Denpendency Injection) 这也是个老身常谈的问题,到底依赖注入是什么? 为什么要用它? 初学者特别容易对控制反转IOC(Iversion of Contr