以前对Android so的注入只是通过现有的框架,并没有去研究so注入原理,趁现在有时间正好拿出来研究一下。
首先来看注入流程。Android so的注入流程如下:
attach到远程进程 -> 保存寄存器环境 -> 获取目标程序的mmap, dlopen, dlsym, dlclose 地址 -> 远程调用mmap函数申请内存空间用来保存参数信息 -> 向远程进程内存空间写入加载模块名和调用函数->远程调用dlopen函数加载so文件 -> 远程调用dlsym函数获取目标函数地址->使用ptrace_call远程调用被注入模块的函数 -> 调用 dlclose 卸载so文件 -> 恢复寄存器环境 -> 从远程进程detach(进程暂停->ptrace函数调用,其他函数远程调用->进程恢复)
下面我们通过代码来实现这个流程。首先创建目录及文件:
jni
inject.c
Android.mk
Application.mk
在编写代码之前,我们先熟悉一下pt_regs结构体:
1pt_regs结构的定义: 2 struct pt_regs{ 3 long uregs[18]; 4 }; 5 #define ARM_cpsr uregs[16] 存储状态寄存器的值 6 #define ARM_pc uregs[15] 存储当前的执行地址 7 #define ARM_lr uregs[14] 存储返回地址 8 #define ARM_sp uregs[13] 存储当前的栈顶地址 9 #define ARM_ip uregs[12] 10 #define ARM_fp uregs[11] 11 #define ARM_10 uregs[10] 12 #define ARM_9 uregs[9] 13 #define ARM_8 uregs[8] 14 #define ARM_7 uregs[7] 15 #define ARM_6 uregs[6] 16 #define ARM_5 uregs[5] 17 #define ARM_4 uregs[4] 18 #define ARM_3 uregs[3] 19 #define ARM_2 uregs[2] 20 #define ARM_1 uregs[1] 21 #define ARM_0 uregs[0] 存储R0寄存器的值,函数调用后的返回值会存储在R0寄存器中
在通过ptrace改变远程进程的执行流程之前,需要先读取和保存远程进程的所有寄存器的值,在ARM处理器下,ptrace函数中data参数的regs为pt_regs结构的指针,从远程进程获取的寄存器值将存储到该结构中。在远程进程执行detach操作之前,需要将远程进程的原寄存器的环境恢复,保证远程进程原有的执行流程不被破坏。如果不恢复寄存器的值,则执行detach操作之后会导致远程进程崩溃。
inject.c的代码如下:
1 1 #include <stdio.h> 2 2 #include <stdlib.h> 3 3 #include <sys/user.h> 4 4 #include <asm/ptrace.h> 5 5 #include <sys/ptrace.h> 6 6 #include <sys/wait.h> 7 7 #include <sys/mman.h> 8 8 #include <dlfcn.h> 9 9 #include <dirent.h> 10 10 #include <unistd.h> 11 11 #include <string.h> 12 12 #include <elf.h> 13 13 #include <android/log.h> 14 14 15 15 #if defined(__i386__) 16 16 #define pt_regs user_regs_struct 17 17 #endif 18 18 19 19 #define LOG_TAG "INJECT" 20 20 #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 21 21 #define CPSR_T_MASK (1u << 5) 22 22 23 23 const char* libc_path = "/system/lib/libc.so"; 24 24 const char* linker_path = "/system/bin/linker"; 25 25 26 26 /*-------------------------------------------------- 27 27 * 功能: 向目标进程指定的地址中读取数据 28 28 * 29 29 * 参数: 30 30 * pid 需要注入的进程pid 31 31 * src 需要读取的目标进程地址 32 32 * buf 需要读取的数据缓冲区 33 33 * size 需要读取的数据长度 34 34 * 35 35 * 返回值: -1 36 36 *--------------------------------------------------*/ 37 37 int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size){ 38 38 uint32_t i, j, remain; 39 39 uint8_t *laddr; 40 40 41 41 union u{ 42 42 long val; 43 43 char chars[sizeof(long)]; 44 44 }d; 45 45 46 46 j = size/4; 47 47 remain = size%4; 48 48 laddr = buf; 49 49 50 50 for(i = 0; i<j; i++){ 51 51 //从内存地址src中读取四个字节 52 52 d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0); 53 53 memcpy(laddr, d.chars, 4); 54 54 src += 4; 55 55 laddr += 4; 56 56 } 57 57 58 58 if(remain > 0){ 59 59 d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0); 60 60 memcpy(laddr, d.chars, remain); 61 61 } 62 62 return 0; 63 63 } 64 64 65 65 /*-------------------------------------------------- 66 66 * 功能: 向目标进程指定的地址中写入数据 67 67 * 68 68 * 参数: 69 69 * pid 需要注入的进程pid 70 70 * dest 需要写入的目标进程地址 71 71 * data 需要写入的数据缓冲区 72 72 * size 需要写入的数据长度 73 73 * 74 74 * 返回值: -1 75 75 *--------------------------------------------------*/ 76 76 int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size){ 77 77 uint32_t i, j, remain; 78 78 uint8_t *laddr; 79 79 80 80 union u{ 81 81 long val; 82 82 char u_data[sizeof(long)]; 83 83 }d; 84 84 85 85 j = size/4; 86 86 remain = size%4; 87 87 88 88 laddr = data; 89 89 90 90 //先4字节拷贝 91 91 for(i = 0; i<j; i++){ 92 92 memcpy(d.u_data, laddr, 4); 93 93 //往内存地址中写入四个字节,内存地址由dest给出 94 94 ptrace(PTRACE_POKETEXT, pid, dest, d.val); 95 95 96 96 dest += 4; 97 97 laddr += 4; 98 98 } 99 99 100100 //最后不足4字节的,单字节拷贝 101101 //为了最大程度的保持原栈的数据,需要先把原程序最后四字节读出来 102102 //然后把多余的数据remain覆盖掉四字节中前面的数据 103103 if(remain > 0){ 104104 d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0); //从内存地址中读取四个字节,内存地址由dest给出 105105 for(i = 0; i<remain; i++){ 106106 d.u_data[i] = *laddr++; 107107 } 108108 ptrace(PTRACE_POKETEXT, pid, dest, d.val); 109109 } 110110 return 0; 111111 } 112112 113113 /*-------------------------------------------------- 114114 * 功能: 获取指定进程的寄存器信息 115115 * 116116 * 返回值: 失败返回-1 117117 *--------------------------------------------------*/ 118118 int ptrace_getregs(pid_t pid, struct pt_regs *regs){ 119119 if(ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0){ 120120 perror("ptrace_getregs: Can not get register values."); 121121 return -1; 122122 } 123123 return 0; 124124 } 125125 126126 /*-------------------------------------------------- 127127 * 功能: 修改目标进程寄存器的值 128128 * 129129 * 参数: 130130 * pid 需要注入的进程pid 131131 * pt_regs 需要修改的新寄存器信息 132132 * 133133 * 返回值: -1 134134 *--------------------------------------------------*/ 135135 int ptrace_setregs(pid_t pid, struct pt_regs *regs){ 136136 if(ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0){ 137137 perror("ptrace_setregs:Can not set regsiter values."); 138138 return -1; 139139 } 140140 return 0; 141141 } 142142 143143 /*-------------------------------------------------- 144144 * 功能: 恢复程序运行 145145 * 146146 * 参数: 147147 * pid 需要注入的进程pid 148148 * 149149 * 返回值: -1 150150 *--------------------------------------------------*/ 151151 int ptrace_continue(pid_t pid){ 152152 if(ptrace(PTRACE_CONT, pid, NULL, 0) < 0){ 153153 perror("ptrace_cont"); 154154 return -1; 155155 } 156156 return 0; 157157 } 158158 159159 /*-------------------------------------------------- 160160 * 功能: 附加进程 161161 * 162162 * 返回值: 失败返回-1 163163 *--------------------------------------------------*/ 164164 int ptrace_attach(pid_t pid){ 165165 if(ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0){ 166166 perror("ptrace_attach"); 167167 return -1; 168168 } 169169 return 0; 170170 } 171171 172172 // 释放对目标进程的附加调试 173173 int ptrace_detach(pid_t pid) 174174 { 175175 if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) { 176176 perror("ptrace_detach"); 177177 return -1; 178178 } 179179 180180 return 0; 181181 } 182182 /*-------------------------------------------------- 183183 * 功能: 获取进程中指定模块的首地址 184184 * 原理: 通过遍历/proc/pid/maps文件,来找到目的module_name的内存映射起始地址。 185185 * 由于内存地址的表达方式是startAddrxxxxxxx-endAddrxxxxxxx的,所以通过使用strtok(line,"-")来分割字符串获取地址 186186 * 如果pid = -1,表示获取本地进程的某个模块的地址,否则就是pid进程的某个模块的地址 187187 * 参数: 188188 * pid 需要注入的进程pid, 如果为0则获取自身进程 189189 * module_name 需要获取模块路径 190190 * 191191 * 返回值: 失败返回NULL, 成功返回addr 192192 *--------------------------------------------------*/ 193193 void *get_module_base(pid_t pid, const char* module_name) 194194 { 195195 FILE* fp; 196196 long addr = 0; 197197 char* pch; 198198 char filename[32]; 199199 char line[1024]; 200200 201201 if(pid < 0){ 202202 snprintf(filename, sizeof(filename), "/proc/self/maps"); 203203 }else{ 204204 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 205205 } 206206 207207 fp = fopen(filename, "r"); 208208 209209 if(fp != NULL){ 210210 while(fgets(line, sizeof(line), fp)){ 211211 if(strstr(line, module_name)){ 212212 pch = strtok(line, "-"); 213213 //将参数pch字符串根据参数base(表示进制)来转换成无符号的长整型数 214214 addr = strtoul(pch, NULL, 16); 215215 if(addr == 0x8000) 216216 addr = 0; 217217 break; 218218 } 219219 } 220220 fclose(fp); 221221 } 222222 return (void*)addr; 223223 } 224224 225225 226226 /*-------------------------------------------------- 227227 * 功能: 获取目标进程中函数指针 228228 * 229229 * 参数: 230230 * target_pid 需要注入的进程pid 231231 * module_name 需要获取的函数所在的lib库路径 232232 * local_addr 需要获取的函数所在当前进程内存中的地址 233233 * 234234 * 目标进程中函数指针 = 目标进程模块基址 - 自身进程模块基址 + 内存中的地址 235235 * 236236 * 返回值: 失败返回NULL, 成功返回ret_addr 237237 *--------------------------------------------------*/ 238238 void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr){ 239239 void* local_handle, *remote_handle; 240240 //获取本地某个模块的起始地址 241241 local_handle = get_module_base(-1, module_name); 242242 //获取远程pid的某个模块的起始地址 243243 remote_handle = get_module_base(target_pid, module_name); 244244 245245 LOGD("[+]get remote address: local[%x], remote[%x]\n", local_handle, remote_handle); 246246 247247 //local_addr - local_handle的值为指定函数(如mmap)在该模块中的偏移量,然后再加上remote_handle,结果就为指定函数在目标进程的虚拟地址 248248 void* ret_addr = (void*)((uint32_t)local_addr - (uint32_t)local_handle) + (uint32_t)remote_handle; 249249 return ret_addr; 250250 } 251251 252252 /*-------------------------------------------------- 253253 * 功能: 通过进程的名称获取对应的进程pid 254254 * 原理: 通过遍历/proc目录下的所有子目录,获取这些子目录的目录名(一般就是进程的进程号pid)。 255255 * 获取子目录名后,就组合成/proc/pid/cmdline文件名,然后依次打开这些文件,cmdline文件 256256 * 里面存放的就是进程名,通过这样就可以获取进程的pid了 257257 * 返回值: 未找到返回-1 258258 *--------------------------------------------------*/ 259259 int find_pid_of(const char* process_name){ 260260 int id; 261261 pid_t pid = -1; 262262 DIR* dir; 263263 FILE* fp; 264264 char filename[32]; 265265 char cmdline[296]; 266266 267267 struct dirent* entry; 268268 269269 if(process_name == NULL){ 270270 return -1; 271271 } 272272 273273 dir = opendir("/proc"); 274274 if(dir == NULL){ 275275 return -1; 276276 } 277277 278278 while((entry = readdir(dir)) != NULL){ 279279 id = atoi(entry->d_name); 280280 if(id != 0){ 281281 sprintf(filename, "/proc/%d/cmdline", id); 282282 fp = fopen(filename, "r"); 283283 if(fp){ 284284 fgets(cmdline, sizeof(cmdline), fp); 285285 fclose(fp);// 释放对目标进程的附加调试 286286 287287 if(strcmp(process_name, cmdline) == 0){ 288288 pid = id; 289289 break; 290290 } 291291 } 292292 } 293293 } 294294 closedir(dir); 295295 return pid; 296296 } 297297 298298 long ptrace_retval(struct pt_regs* regs){ 299299 return regs->ARM_r0; 300300 } 301301 302302 long ptrace_ip(struct pt_regs* regs){ 303303 return regs->ARM_pc; 304304 } 305305 306306 /*-------------------------------------------------- 307307 * 功能: 调用远程函数指针 308308 * 原理: 1,将要执行的指令写入寄存器中,指令长度大于4个long的话,需要将剩余的指令通过ptrace_writedata函数写入栈中; 309309 * 2,使用ptrace_continue函数运行目的进程,直到目的进程返回状态值0xb7f(对该值的分析见后面红字); 310310 * 3,函数执行完之后,目标进程挂起,使用ptrace_getregs函数获取当前的所有寄存器值,方便后面使用ptrace_retval函数获取函数的返回值。 311311 * 参数: 312312 * pid 需要注入的进程pid 313313 * addr 调用的函数指针地址 314314 * params 调用的参数 315315 * num_params 调用的参数个数 316316 * regs 远程进程寄存器信息(ARM前4个参数由r0 ~ r3传递) 317317 * 318318 * 返回值: 失败返回-1 319319 *--------------------------------------------------*/ 320320 int ptrace_call(pid_t pid, uint32_t addr, long* params, uint32_t num_params, struct pt_regs* regs){ 321321 uint32_t i; 322322 for(i = 0; i<num_params && i < 4; i++){ 323323 regs->uregs[i] = params[i]; 324324 } 325325 326326 if(i < num_params){ 327327 regs->ARM_sp -= (num_params - i) * sizeof(long); 328328 ptrace_writedata(pid, (void*)regs->ARM_sp, (uint8_t*)¶ms[i], (num_params - i)*sizeof(long)); 329329 } 330330 //将PC寄存器值设为目标函数的地址 331331 regs->ARM_pc = addr; 332332 ////指令集判断 333333 if(regs->ARM_pc & 1){ 334334 /* thumb */ 335335 regs->ARM_pc &= (~1u); 336336 regs->ARM_cpsr |= CPSR_T_MASK; 337337 }else{ 338338 /* arm */ 339339 regs->ARM_cpsr &= ~CPSR_T_MASK; 340340 } 341341 ///设置子程序的返回地址为空,以便函数执行完后,返回到null地址,产生SIGSEGV错误 342342 regs->ARM_lr = 0; 343343 344344 //将修改后的regs写入寄存器中,然后调用ptrace_continue来执行我们指定的代码 345345 if(ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1){ 346346 printf("error.\n"); 347347 return -1; 348348 } 349349 350350 int stat = 0; 351351 /* WUNTRACED告诉waitpid,如果子进程进入暂停状态,那么就立即返回。如果是被ptrace的子进程,那么即使不提供WUNTRACED参数,也会在子进程进入暂停状态的时候立即返回。 352352 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。这里的0xb7f就表示子进程进入了暂停状态, 353353 且发送的错误信号为11(SIGSEGV),它表示试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。那么什么时候会发生这种错误呢?显然,当子进程执行完注入的 354354 函数后,由于我们在前面设置了regs->ARM_lr = 0,它就会返回到0地址处继续执行,这样就会产生SIGSEGV了! 355355 */ 356356 waitpid(pid, &stat, WUNTRACED); 357357 /*stat的值:高2字节用于表示导致子进程的退出或暂停状态信号值,低2字节表示子进程是退出(0x0)还是暂停(0x7f)状态。 358358 0xb7f就表示子进程为暂停状态,导致它暂停的信号量为11即sigsegv错误。*/ 359359 while(stat != 0xb7f){ 360360 if(ptrace_continue(pid) == -1){ 361361 printf("error.\n"); 362362 return -1; 363363 } 364364 waitpid(pid, &stat, WUNTRACED); 365365 } 366366 return 0; 367367 } 368368 369369 /*-------------------------------------------------- 370370 * 功能: 调用远程函数指针 371371 * 372372 * 参数: 373373 * pid 需要注入的进程pid 374374 * func_name 调用的函数名称, 此参数仅作Debug输出用 375375 * func_addr 调用的函数指针地址 376376 * param 调用的参数 377377 * param_num 调用的参数个数 378378 * regs 远程进程寄存器信息(ARM前4个参数由r0 ~ r3传递) 379379 * 380380 * 返回值: 失败返回-1 381381 *--------------------------------------------------*/ 382382 int ptrace_call_wrapper(pid_t target_pid, const char* func_name, void* func_addr, long* param, int param_num, struct pt_regs* regs){ 383383 LOGD("[+]Calling %s in target process.\n", func_name); 384384 if(ptrace_call(target_pid, (uint32_t)func_addr, param, param_num, regs) == -1) 385385 return -1; 386386 if(ptrace_getregs(target_pid, regs) == -1){ 387387 return -1; 388388 } 389389 LOGD("[+] Target process returned from %s, return value = %x, pc = %x \n", func_name, ptrace_retval(regs), ptrace_ip(regs)); 390390 return 0; 391391 } 392392 393393 /*-------------------------------------------------- 394394 * 功能: 远程注入 395395 * 396396 * 参数: 397397 * target_pid 需要注入的进程Pid 398398 * library_path 需要注入的.so路径 399399 * function_name .so中导出的函数名 400400 * param 函数的参数 401401 * param_size 参数大小,以字节为单位 402402 * 403403 * 返回值: 注入失败返回-1 404404 *--------------------------------------------------*/ 405405 int inject_remote_process(pid_t target_pid, const char* library_path, const char* function_name, const char* param, size_t param_size){ 406406 int ret = -1; 407407 void* mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; 408408 void *local_handle, *remote_handle, *dlhandle; 409409 uint8_t *map_base = 0; 410410 uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; 411411 412412 struct pt_regs regs, original_regs; 413413 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; 414414 415415 uint32_t code_length; 416416 long parameters[10]; 417417 418418 LOGD("[+] Injecting process: %d\n", target_pid); 419419 420420 //①ATTATCH,指定目标进程,开始调试 421421 if(ptrace_attach(target_pid) == -1){ 422422 goto exit; 423423 } 424424 425425 //②GETREGS,获取目标进程的寄存器,保存现场 426426 if(ptrace_getregs(target_pid, ®s) == -1) 427427 goto exit; 428428 429429 //保存原始寄存器 430430 memcpy(&original_regs, ®s, sizeof(regs)); 431431 432432 //③通过get_remote_addr函数获取目标进程的mmap函数的地址,以便为libxxx.so分配内存 433433 //由于mmap函数在libc.so库中,为了将libxxx.so加载到目标进程中,就需要使用目标进程的mmap函数,所以需要查找到libc.so库在目标进程的起始地址。 434434 mmap_addr = get_remote_addr(target_pid, libc_path, (void*)mmap); //libc_path = "/system/lib/libc.so" 435435 LOGD("[+] Remote mmap address: %x\n", mmap_addr); 436436 437437 parameters[0] = 0; // 设置为NULL表示让系统自动选择分配内存的地址 438438 parameters[1] = 0x4000; // 映射内存的大小 439439 parameters[2] = PROT_READ | PROT_WRITE |PROT_EXEC; // 表示映射内存区域可读可写可执行 440440 parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // 建立匿名映射 441441 parameters[4] = 0; //若需要映射文件到内存中,则为文件的fd 442442 parameters[5] = 0; //文件映射偏移量 443443 444444 //④通过ptrace_call_wrapper调用mmap函数,在目标进程中为libxxx.so分配内存 445445 if(ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1) 446446 goto exit; 447447 //⑤从寄存器中获取mmap函数的返回值,即申请的内存首地址: 448448 map_base = ptrace_retval(®s); 449449 450450 //⑥依次获取linker中dlopen、dlsym、dlclose、dlerror函数的地址 451451 dlopen_addr = get_remote_addr(target_pid, linker_path, (void*)dlopen); 452452 dlsym_addr = get_remote_addr(target_pid, linker_path, (void*)dlsym); 453453 dlclose_addr = get_remote_addr(target_pid, linker_path, (void*)dlclose); 454454 dlerror_addr = get_remote_addr(target_pid, linker_path, (void*)dlerror); 455455 456456 LOGD("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n", dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); 457457 458458 printf("library path = %s\n", library_path); 459459 //⑦调用dlopen函数 460460 //(1)将要注入的so名写入前面mmap出来的内存 461461 ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1); 462462 463463 parameters[0] = map_base; 464464 parameters[1] = RTLD_NOW | RTLD_GLOBAL; 465465 466466 //(2)执行dlopen 467467 if(ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1){ 468468 goto exit; 469469 } 470470 //(3)取得dlopen的返回值,存放在sohandle变量中 471471 void* sohandle = ptrace_retval(®s); 472472 473473 //⑧调用dlsym函数 474474 //为functionname另找一块区域 475475 #define FUNCTION_NAME_ADDR_OFFSET 0X100 476476 ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1); 477477 parameters[0] = sohandle; 478478 parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET; 479479 480480 //调用dlsym 481481 if(ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1) 482482 goto exit; 483483 void* hook_entry_addr = ptrace_retval(®s); 484484 LOGD("hooke_entry_addr = %p\n", hook_entry_addr); 485485 486486 //⑨调用被注入函数hook_entry 487487 #define FUNCTION_PARAM_ADDR_OFFSET 0X200 488488 ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, parameters, strlen(parameters) + 1); 489489 parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET; 490490 491491 if(ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1) 492492 goto exit; 493493 //⑩调用dlclose关闭lib 494494 printf("Press enter to dlclose and detach.\n"); 495495 getchar(); 496496 parameters[0] = sohandle; 497497 498498 if(ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1) 499499 goto exit; 500500 501501 //⑪恢复现场并退出ptrace 502502 ptrace_setregs(target_pid, &original_regs); 503503 ptrace_detach(target_pid); 504504 ret = 0; 505505 506506 exit: 507507 return ret; 508508 } 509509 510510 int main(int argc, char** argv) { 511511 pid_t target_pid; 512512 target_pid = find_pid_of("com.bbk.appstore"); 513513 if (-1 == target_pid) { 514514 printf("Can't find the process\n"); 515515 return -1; 516516 } 517517 //target_pid = find_pid_of("/data/test"); 518518 inject_remote_process(target_pid, "/data/local/tmp/libentry.so", "hook_entry", "Fuck you!", strlen("Fuck you!")); 519519 return 0; 520520 }
上述代码中,我们要hook的进程名为"com.bbk.appstore",我们要将“libentry.so”注入到该进程中去。
Android.mk内容为:
1LOCAL_PATH := $(call my-dir) 2 3include $(CLEAR_VARS) 4LOCAL_MODULE := inject 5LOCAL_SRC_FILES := inject.c 6 7#shellcode.s 8 9LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 10 11#LOCAL_FORCE_STATIC_EXECUTABLE := true 12 13include $(BUILD_EXECUTABLE)
Application.mk内容为:
1# 编译生成的模块文件运行支持的平台 2APP_ABI := armeabi-v7a 3# 编译生成模块运行支持的Andorid版本 4APP_PLATFORM := android-19
在jni目录下运行nkd-build编译成生arm平台下的可执行文件:
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk
再来生成要注入的so,创建目录及文件:
jni
entry.c
Android.mk
Application.mk
entry.c的代码为:
1#include <unistd.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <android/log.h> 5#include <elf.h> 6#include <fcntl.h> 7 8#define LOG_TAG "DEBUG" 9#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 10 11int hook_entry(char * a){ 12 LOGD("Hook success, pid = %d\n", getpid()); 13 LOGD("Hello %s\n", a); 14 return 0; 15}
Android.mk文件:
1LOCAL_PATH := $(call my-dir) 2 3include $(CLEAR_VARS) 4 5LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 6#LOCAL_ARM_MODE := arm 7LOCAL_MODULE := entry 8LOCAL_SRC_FILES := entry.c 9include $(BUILD_SHARED_LIBRARY)
Application.mk文件内容跟上面一样。同样将entry.c进行编译。然后将得到inject和libentry.so push到/data/local/tmp目录下,执行:

通过“/proc/pid/maps”查看被注入进程("com.bbk.appstore")的mmap,可以看到我们的so已经被加载了:

通过“adb logcat -s INJECT”命令打印出log:

这就说明我们的注入成功了。
参考资料:
https://blog.csdn.net/qq1084283172/article/details/53942648
https://melonwxd.github.io/2017/12/01/inject-3-hook/
https://www.cnblogs.com/wanyuanchun/p/4020756.html