本文是對看雪上一篇文章的學習筆記。記錄自己的一些實踐和遇到的問題。
原文地址:注入安卓進程,并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, ®s) == -1)
goto exit;
/* save original registers */
memcpy(&original_regs, ®s, 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, ®s) == -1)
goto exit;
map_base = ptrace_retval(®s); // 第三步,獲取目標進程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, ®s) == -1)
goto exit;
void * sohandle = ptrace_retval(®s); // 第五步,在目標進程內調用 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, ®s) == -1)
goto exit;
void * hook_entry_addr = ptrace_retval(®s);
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, ®s) == -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, ®s) == -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中進行方法的替換,即修改了目標函數應該調用的函數指針。(具體替換還需要分析)
目前實現的功能:
- 按照原作者的意圖,實現了替換功能。