android hook之注入安卓進程,并hook java世界的方法

本文是對看雪上一篇文章的學習筆記。記錄自己的一些實踐和遇到的問題。
原文地址:注入安卓進程,并hook java世界的方法.

hook流程:

  • 通過系統ptrace調用可以控制被traced進程的寄存器和程序映象。
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; // 存放目標進程mmap獲取的內存塊的地址
        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;
        struct pt_regs original_regs;
       
        uint32_t code_length;
        long parameters[10];

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

        if (ptrace_attach(target_pid) == -1) // 第一步: attach 到目標進程
                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: %x\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);  // 第三步,獲取目標進程mmap調用的地址,并執行mmap調用,在目標進程分配一塊地址,用于存放后面要注入的庫路徑和相關函數地址等

        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: %x, dlsym: %x, dlclose: %x, dlerror: %x\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);// 第四步,獲取目標進程動態庫的幾個函數,并將要注入的so的路徑寫入剛剛申請的內存的初始地址

        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); // 第五步,在目標進程內調用 dlopen函數加載要注入的 so ,這一步成功后,so已經被注入目標進程的地址空間內了

#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); // 第六步,在目標進程內調用 dlsym函數獲取剛剛注入的so里的hook函數

#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"); // 第七步,在目標進程內調用hook函數
      //  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); // 第八步,恢復目標進程的寄存器,detach 退出對目標進程的 ptrace
        ret = 0;

exit:
        return ret;
}
  • 在將so1注入到目標進程后,會執行so1中的一個方法,在這個方法中,我們加載一個新的so2庫到進程。
  • 在so2庫中封裝了需要hook的函數的一些信息
  • 在so1中進行方法的替換,即修改了目標函數應該調用的函數指針。(具體替換還需要分析)

目前實現的功能:

  • 按照原作者的意圖,實現了替換功能。

參考鏈接:

JNI介紹

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容