美文网首页
Android中进程注入(二)

Android中进程注入(二)

作者: Sharkchilli | 来源:发表于2021-03-29 21:38 被阅读0次

    前言

    接下来我们就要实现一个向目标进程注入so的函数,并使用ndk交叉编译代码让他能在我们arm手机上运行

    环境

    nexus5
    Android4.4
    ndk11

    所有文件我都放到git上了
    https://github.com/bigGreenPeople/android_inject.git

    代码实现

    注入远程进程so函数

    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;
        extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
     _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
     _saved_cpsr_s, _saved_r0_pc_s;
    
        uint32_t code_length;
        long parameters[10];
    
        DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);
        //1.首先挂载到目标进程
        if (ptrace_attach(target_pid) == -1)
            goto exit;
        //2.读取目标进程寄存器数据
        if (ptrace_getregs(target_pid, &regs) == -1)
            goto exit;
    
        /*3.保存原来的寄存器数据*/
        memcpy(&original_regs, &regs, sizeof(regs));
        //获取目标进程mmap函数的地址
        mmap_addr = get_remote_addr(target_pid, libc_path, (void *) mmap);
         //获取目标进程 dlopen 函数的地址
        dlopen_addr = get_remote_addr(target_pid, linker_path, (void *) dlopen);
        //获取目标进程 dlsym 函数的地址
        dlsym_addr = get_remote_addr(target_pid, linker_path, (void *) dlsym);
        //获取目标进程 dlclose 函数的地址
        dlclose_addr = get_remote_addr(target_pid, linker_path, (void *) dlclose);
        //获取目标进程 dlerror 函数的地址
        dlerror_addr = get_remote_addr(target_pid, linker_path, (void *) dlerror);
    
        DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
                    dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);
        DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);
    
            //4.使用mmap函数分配字符串内存
        /* call mmap 准备mmap参数 这里分配0x4000大小的内存*/
        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
    
        //在目标进程中调用mmap函数
        if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, &regs) == -1)
            goto exit;
    
        map_base = ptrace_retval(&regs);
       
    
        printf("library path = %s\n", library_path);
        //5.往目标进程写入library_path中的字符串
        ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);
    
        parameters[0] = map_base;
        parameters[1] = RTLD_NOW | RTLD_GLOBAL;
        //6.让目标进程调用 dlopen 函数
        if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)
            goto exit;
    
        void *sohandle = ptrace_retval(&regs);
            //7.往目标进程写入function_name中的字符串 (这里分配到0x100后面 一般上面的字符串不会超过)
    #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;
        //8.让目标进程调用 dlsym 函数(得到我们需要调用的函数地址)
        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);
            //9.为调用的函数参数,拷贝字符串 (这里分配到0x200后面 一般上面的字符串不会超过)
    #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;
    
        //10.让目标进程调用hook_entry 函数
        if (ptrace_call_wrapper(target_pid, function_name, hook_entry_addr, parameters, 1, &regs) == -1)
            goto exit;
    
        printf("Press enter to dlclose and detach\n");
    
    
        /* 11.恢复寄存器*/
        ptrace_setregs(target_pid, &original_regs);
        //12.卸载目标进程
        ptrace_detach(target_pid);
        ret = 0;
    
    exit:
        return ret;
    }
    

    这些都是之前封装好的函数,这里就实现了远程加载so。大致的步骤如下:
    1.首先挂载到目标进程
    2.读取目标进程寄存器数据
    3.保存原来的寄存器数据
    4.使用mmap函数分配字符串内存
    5.往目标进程写入library_path中的字符串
    6.让目标进程调用 dlopen 函数
    7.往目标进程写入function_name中的字符串
    8.让目标进程调用 dlsym 函数
    9.为调用的函数参数,拷贝字符串
    10.让目标进程调用hook_entry 函数
    11.恢复寄存器
    12.卸载目标进程

    最后写一个main函数调用,让目标进程加载我们自己写的so

    int main(int argc, char **argv) {
        pid_t target_pid;
        target_pid = find_pid_of(
                "com.shark.initapp");
        if (-1 == target_pid) {
            printf("Can't find the process\n");
            return -1;
        }
        printf("target_pid=%d argc=%d\n ", target_pid, argc);
        char *sopath = "/data/local/tmp/inject_shark.so";
        if (argc > 1) {
            sopath = argv[1];
        }
        char *main_entry = "main_entry";
        if (argc > 2) {
            main_entry = argv[2];
        }
        char *parameter = "parameter";
        if (argc > 3) {
            parameter = argv[3];
        }
    
        printf("inject_remote_process start\n");
        inject_remote_process(target_pid, sopath, main_entry, parameter, strlen(parameter));
        return 0;
    }
    

    inject_shark.so的代码如下

    #include <jni.h>
    #include <string>
    #include<android/log.h>
    
    extern "C" JNIEXPORT void main_entry(char * parameter){
        __android_log_print(ANDROID_LOG_ERROR, "SharkChilli",
                            "main_entry");
    }
    

    使用as将其编译成so后拷贝到/data/local/tmp/inject_shark.so

    ndk编译

    操作步骤可以参考我以前的文章NDK 编译可执行程序( 独立编译)(一)

    这里我就直接贴上mk文件了

    Android.mk

    # 一个Android.mk file首先必须定义好LOCAL_PATH变量。
    # 它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’, 
    # 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。
    LOCAL_PATH := $(call my-dir)
    # CLEAR_VARS由编译系统提供,
    # 指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,
    # 因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
    include $(CLEAR_VARS)
    # LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。
    # 注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。
    LOCAL_MODULE    := shark_inject
    # 加入日志库
    LOCAL_LDLIBS    :=  -llog
    # LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,
    # 因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。
    LOCAL_SRC_FILES := shark_inject.c
    # BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译
    # BUILD_SHARED_LIBRARY 表示动态链接库的方式进行编译
    include $(BUILD_EXECUTABLE)
    

    Application.mk

    #### 0APP_ABI := arm64-v8a 后面接的是需要生成的.so平台文件,
    #### 正常手机使用arm64处理器的即可。 all是全平台
    #### APP_PLATFORM :=后面接的是使用SDK的最低等级
    APP_ABI := armeabi-v7a
    

    在文件的父目录使用ndk进行编译,警告可以不用理睬。
    将libs/armeabi-v7a目录下的shark_inject文件拷贝到手机上

    adb push .\shark_inject /data/local/tmp/
    

    打开com.shark.initapp,给予shark_inject 权限。

    运行

     # ./shark_inject
    target_pid=26833 argc=1
     inject_remote_process start
    library path = /data/local/tmp/inject_shark.so
    Press enter to dlclose and detach
    

    ddms中的日志信息


    image.png

    可以看到我们成功调用了so中的main_entry函数

    使用objection查看目标进程加载的so库


    image.png

    可以看到我们的so确实被加载了

    相关文章

      网友评论

          本文标题:Android中进程注入(二)

          本文链接:https://www.haomeiwen.com/subject/wjqihltx.html