上一篇 android hook 框架 libinject 简介、编译、运行 实际运行了so的注入并调用了注入so里的一个函数,这篇开始分析其实现。
与之前分析的 abdi 项目一样,libinject2 也是依赖于linux系统的 ptrace 系统调用。
这个库首先对ptrace的调用封装了几个helper函数
int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size)
{
uint32_t i, j, remain;
uint8_t*laddr;
union u {longval;char chars[sizeof(long)];
} d;
j= size / 4;
remain= size % 4;
laddr=buf;for (i = 0; i < j; i ++) {
d.val= ptrace(PTRACE_PEEKTEXT, pid, src, 0);
memcpy(laddr, d.chars,4);
src+= 4;
laddr+= 4;
}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)
{
uint32_t i, j, remain;
uint8_t*laddr;
{if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
perror("ptrace_cont");return -1;
}return 0;
}intptrace_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;
}intptrace_detach(pid_t pid)
{if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
perror("ptrace_detach");return -1;
}return 0;
}
long ptrace_retval(struct pt_regs *regs)
{#if defined(__arm__)
return regs->ARM_r0;#elif defined(__i386__)
return regs->eax;
#else
#error "Not supported"
#endif}long ptrace_ip(struct pt_regs *regs)
{#if defined(__arm__)
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, (uint32_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=%x, pc=%x \n",
func_name, ptrace_retval(regs), ptrace_ip(regs));return 0;
}
下面两个函数实现了获取目标进程加载的动态库内部函数的地址,与 adbi 的原理一致,都是利用函数与动态库加载进内存的起始地址的offset一致,来计算的,个人觉得 libinject 在实现同样的功能时代码给 adbi 写得更舒服,这也是研究各种源码的好处,有对比才有高低。
void* get_module_base(pid_t pid, const char*module_name) // 这个函数获取动态库 module_name 加载在进程 pid 后的起始地址
{
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); // 同样是通过解析 maps 文件得到的
}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= strtoul( 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[%x], remote[%x]\n", local_handle, remote_handle);void * ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle -(uint32_t)local_handle);// 算法一致, local_addr - local_handle 得到 // offset, 然后再加上 remote_handle, 即得到目标进程的函数地址
returnret_addr;
}
下面这个是 libinject2 的核心函数:
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;structpt_regs regs;structpt_regs original_regs;externuint32_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);if (ptrace_attach(target_pid) == -1)gotoexit;if (ptrace_getregs(target_pid, ®s) == -1)gotoexit;/*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)gotoexit;
map_base= ptrace_retval(®s);
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);
parameters[0] =map_base;
parameters[1] = RTLD_NOW|RTLD_GLOBAL;if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1)gotoexit;void * sohandle = ptrace_retval(®s);#define FUNCTION_NAME_ADDR_OFFSET 0x100ptrace_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)gotoexit;void * hook_entry_addr = ptrace_retval(®s);
DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr);#define FUNCTION_PARAM_ADDR_OFFSET 0x200ptrace_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)gotoexit;
printf("Press enter to dlclose and detach\n");
getchar();
parameters[0] =sohandle;if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1)gotoexit;/*restore*/ptrace_setregs(target_pid,&original_regs);
ptrace_detach(target_pid);
ret= 0;
exit:returnret;
}
最后是main函数,libinject2 只是注入了一个So到目标进程,并执行了so里的一个函数,还没有真正劫持目标进程的函数
int main(int argc, char**argv) {
pid_t target_pid;//target_pid = find_pid_of("/system/bin/surfaceflinger");
target_pid = atoi(argv[1]);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/local/tmp/libhello.so", "hook_entry", "I‘m parameter!", strlen("I‘m parameter!"));return 0;
}
原文:http://www.cnblogs.com/jiayy/p/4286864.html