之前對Android so的注入只是經過現有的框架,並無去研究so注入原理,趁如今有時間正好拿出來研究一下。html
首先來看注入流程。Android so的注入流程以下:android
attach到遠程進程 -> 保存寄存器環境 -> 獲取目標程序的mmap, dlopen, dlsym, dlclose 地址 -> 遠程調用mmap函數申請內存空間用來保存參數信息 -> 向遠程進程內存空間寫入加載模塊名和調用函數->遠程調用dlopen函數加載so文件 -> 遠程調用dlsym函數獲取目標函數地址->使用ptrace_call遠程調用被注入模塊的函數 -> 調用 dlclose 卸載so文件 -> 恢復寄存器環境 -> 從遠程進程detach(進程暫停->ptrace函數調用,其餘函數遠程調用->進程恢復)git
下面咱們經過代碼來實現這個流程。首先建立目錄及文件:github
jni
inject.c
Android.mk
Application.mkshell
在編寫代碼以前,咱們先熟悉一下pt_regs結構體: app
pt_regs結構的定義: struct pt_regs{ long uregs[18]; }; #define ARM_cpsr uregs[16] 存儲狀態寄存器的值 #define ARM_pc uregs[15] 存儲當前的執行地址 #define ARM_lr uregs[14] 存儲返回地址 #define ARM_sp uregs[13] 存儲當前的棧頂地址 #define ARM_ip uregs[12] #define ARM_fp uregs[11] #define ARM_10 uregs[10] #define ARM_9 uregs[9] #define ARM_8 uregs[8] #define ARM_7 uregs[7] #define ARM_6 uregs[6] #define ARM_5 uregs[5] #define ARM_4 uregs[4] #define ARM_3 uregs[3] #define ARM_2 uregs[2] #define ARM_1 uregs[1] #define ARM_0 uregs[0] 存儲R0寄存器的值,函數調用後的返回值會存儲在R0寄存器中
在經過ptrace改變遠程進程的執行流程以前,須要先讀取和保存遠程進程的全部寄存器的值,在ARM處理器下,ptrace函數中data參數的regs爲pt_regs結構的指針,從遠程進程獲取的寄存器值將存儲到該結構中。在遠程進程執行detach操做以前,須要將遠程進程的原寄存器的環境恢復,保證遠程進程原有的執行流程不被破壞。若是不恢復寄存器的值,則執行detach操做以後會致使遠程進程崩潰。框架
inject.c的代碼以下: 函數
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/user.h> 4 #include <asm/ptrace.h> 5 #include <sys/ptrace.h> 6 #include <sys/wait.h> 7 #include <sys/mman.h> 8 #include <dlfcn.h> 9 #include <dirent.h> 10 #include <unistd.h> 11 #include <string.h> 12 #include <elf.h> 13 #include <android/log.h> 14 15 #if defined(__i386__) 16 #define pt_regs user_regs_struct 17 #endif 18 19 #define LOG_TAG "INJECT" 20 #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 21 #define CPSR_T_MASK (1u << 5) 22 23 const char* libc_path = "/system/lib/libc.so"; 24 const char* linker_path = "/system/bin/linker"; 25 26 /*-------------------------------------------------- 27 * 功能: 向目標進程指定的地址中讀取數據 28 * 29 * 參數: 30 * pid 須要注入的進程pid 31 * src 須要讀取的目標進程地址 32 * buf 須要讀取的數據緩衝區 33 * size 須要讀取的數據長度 34 * 35 * 返回值: -1 36 *--------------------------------------------------*/ 37 int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size){ 38 uint32_t i, j, remain; 39 uint8_t *laddr; 40 41 union u{ 42 long val; 43 char chars[sizeof(long)]; 44 }d; 45 46 j = size/4; 47 remain = size%4; 48 laddr = buf; 49 50 for(i = 0; i<j; i++){ 51 //從內存地址src中讀取四個字節 52 d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0); 53 memcpy(laddr, d.chars, 4); 54 src += 4; 55 laddr += 4; 56 } 57 58 if(remain > 0){ 59 d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0); 60 memcpy(laddr, d.chars, remain); 61 } 62 return 0; 63 } 64 65 /*-------------------------------------------------- 66 * 功能: 向目標進程指定的地址中寫入數據 67 * 68 * 參數: 69 * pid 須要注入的進程pid 70 * dest 須要寫入的目標進程地址 71 * data 須要寫入的數據緩衝區 72 * size 須要寫入的數據長度 73 * 74 * 返回值: -1 75 *--------------------------------------------------*/ 76 int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size){ 77 uint32_t i, j, remain; 78 uint8_t *laddr; 79 80 union u{ 81 long val; 82 char u_data[sizeof(long)]; 83 }d; 84 85 j = size/4; 86 remain = size%4; 87 88 laddr = data; 89 90 //先4字節拷貝 91 for(i = 0; i<j; i++){ 92 memcpy(d.u_data, laddr, 4); 93 //往內存地址中寫入四個字節,內存地址由dest給出 94 ptrace(PTRACE_POKETEXT, pid, dest, d.val); 95 96 dest += 4; 97 laddr += 4; 98 } 99 100 //最後不足4字節的,單字節拷貝 101 //爲了最大程度的保持原棧的數據,須要先把原程序最後四字節讀出來 102 //而後把多餘的數據remain覆蓋掉四字節中前面的數據 103 if(remain > 0){ 104 d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0); //從內存地址中讀取四個字節,內存地址由dest給出 105 for(i = 0; i<remain; i++){ 106 d.u_data[i] = *laddr++; 107 } 108 ptrace(PTRACE_POKETEXT, pid, dest, d.val); 109 } 110 return 0; 111 } 112 113 /*-------------------------------------------------- 114 * 功能: 獲取指定進程的寄存器信息 115 * 116 * 返回值: 失敗返回-1 117 *--------------------------------------------------*/ 118 int ptrace_getregs(pid_t pid, struct pt_regs *regs){ 119 if(ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0){ 120 perror("ptrace_getregs: Can not get register values."); 121 return -1; 122 } 123 return 0; 124 } 125 126 /*-------------------------------------------------- 127 * 功能: 修改目標進程寄存器的值 128 * 129 * 參數: 130 * pid 須要注入的進程pid 131 * pt_regs 須要修改的新寄存器信息 132 * 133 * 返回值: -1 134 *--------------------------------------------------*/ 135 int ptrace_setregs(pid_t pid, struct pt_regs *regs){ 136 if(ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0){ 137 perror("ptrace_setregs:Can not set regsiter values."); 138 return -1; 139 } 140 return 0; 141 } 142 143 /*-------------------------------------------------- 144 * 功能: 恢復程序運行 145 * 146 * 參數: 147 * pid 須要注入的進程pid 148 * 149 * 返回值: -1 150 *--------------------------------------------------*/ 151 int ptrace_continue(pid_t pid){ 152 if(ptrace(PTRACE_CONT, pid, NULL, 0) < 0){ 153 perror("ptrace_cont"); 154 return -1; 155 } 156 return 0; 157 } 158 159 /*-------------------------------------------------- 160 * 功能: 附加進程 161 * 162 * 返回值: 失敗返回-1 163 *--------------------------------------------------*/ 164 int ptrace_attach(pid_t pid){ 165 if(ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0){ 166 perror("ptrace_attach"); 167 return -1; 168 } 169 return 0; 170 } 171 172 // 釋放對目標進程的附加調試 173 int ptrace_detach(pid_t pid) 174 { 175 if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) { 176 perror("ptrace_detach"); 177 return -1; 178 } 179 180 return 0; 181 } 182 /*-------------------------------------------------- 183 * 功能: 獲取進程中指定模塊的首地址 184 * 原理: 經過遍歷/proc/pid/maps文件,來找到目的module_name的內存映射起始地址。 185 * 因爲內存地址的表達方式是startAddrxxxxxxx-endAddrxxxxxxx的,因此經過使用strtok(line,"-")來分割字符串獲取地址 186 * 若是pid = -1,表示獲取本地進程的某個模塊的地址,不然就是pid進程的某個模塊的地址 187 * 參數: 188 * pid 須要注入的進程pid, 若是爲0則獲取自身進程 189 * module_name 須要獲取模塊路徑 190 * 191 * 返回值: 失敗返回NULL, 成功返回addr 192 *--------------------------------------------------*/ 193 void *get_module_base(pid_t pid, const char* module_name) 194 { 195 FILE* fp; 196 long addr = 0; 197 char* pch; 198 char filename[32]; 199 char line[1024]; 200 201 if(pid < 0){ 202 snprintf(filename, sizeof(filename), "/proc/self/maps"); 203 }else{ 204 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 205 } 206 207 fp = fopen(filename, "r"); 208 209 if(fp != NULL){ 210 while(fgets(line, sizeof(line), fp)){ 211 if(strstr(line, module_name)){ 212 pch = strtok(line, "-"); 213 //將參數pch字符串根據參數base(表示進制)來轉換成無符號的長整型數 214 addr = strtoul(pch, NULL, 16); 215 if(addr == 0x8000) 216 addr = 0; 217 break; 218 } 219 } 220 fclose(fp); 221 } 222 return (void*)addr; 223 } 224 225 226 /*-------------------------------------------------- 227 * 功能: 獲取目標進程中函數指針 228 * 229 * 參數: 230 * target_pid 須要注入的進程pid 231 * module_name 須要獲取的函數所在的lib庫路徑 232 * local_addr 須要獲取的函數所在當前進程內存中的地址 233 * 234 * 目標進程中函數指針 = 目標進程模塊基址 - 自身進程模塊基址 + 內存中的地址 235 * 236 * 返回值: 失敗返回NULL, 成功返回ret_addr 237 *--------------------------------------------------*/ 238 void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr){ 239 void* local_handle, *remote_handle; 240 //獲取本地某個模塊的起始地址 241 local_handle = get_module_base(-1, module_name); 242 //獲取遠程pid的某個模塊的起始地址 243 remote_handle = get_module_base(target_pid, module_name); 244 245 LOGD("[+]get remote address: local[%x], remote[%x]\n", local_handle, remote_handle); 246 247 //local_addr - local_handle的值爲指定函數(如mmap)在該模塊中的偏移量,而後再加上remote_handle,結果就爲指定函數在目標進程的虛擬地址 248 void* ret_addr = (void*)((uint32_t)local_addr - (uint32_t)local_handle) + (uint32_t)remote_handle; 249 return ret_addr; 250 } 251 252 /*-------------------------------------------------- 253 * 功能: 經過進程的名稱獲取對應的進程pid 254 * 原理: 經過遍歷/proc目錄下的全部子目錄,獲取這些子目錄的目錄名(通常就是進程的進程號pid)。 255 * 獲取子目錄名後,就組合成/proc/pid/cmdline文件名,而後依次打開這些文件,cmdline文件 256 * 裏面存放的就是進程名,經過這樣就能夠獲取進程的pid了 257 * 返回值: 未找到返回-1 258 *--------------------------------------------------*/ 259 int find_pid_of(const char* process_name){ 260 int id; 261 pid_t pid = -1; 262 DIR* dir; 263 FILE* fp; 264 char filename[32]; 265 char cmdline[296]; 266 267 struct dirent* entry; 268 269 if(process_name == NULL){ 270 return -1; 271 } 272 273 dir = opendir("/proc"); 274 if(dir == NULL){ 275 return -1; 276 } 277 278 while((entry = readdir(dir)) != NULL){ 279 id = atoi(entry->d_name); 280 if(id != 0){ 281 sprintf(filename, "/proc/%d/cmdline", id); 282 fp = fopen(filename, "r"); 283 if(fp){ 284 fgets(cmdline, sizeof(cmdline), fp); 285 fclose(fp);// 釋放對目標進程的附加調試 286 287 if(strcmp(process_name, cmdline) == 0){ 288 pid = id; 289 break; 290 } 291 } 292 } 293 } 294 closedir(dir); 295 return pid; 296 } 297 298 long ptrace_retval(struct pt_regs* regs){ 299 return regs->ARM_r0; 300 } 301 302 long ptrace_ip(struct pt_regs* regs){ 303 return regs->ARM_pc; 304 } 305 306 /*-------------------------------------------------- 307 * 功能: 調用遠程函數指針 308 * 原理: 1,將要執行的指令寫入寄存器中,指令長度大於4個long的話,須要將剩餘的指令經過ptrace_writedata函數寫入棧中; 309 * 2,使用ptrace_continue函數運行目的進程,直到目的進程返回狀態值0xb7f(對該值的分析見後面紅字); 310 * 3,函數執行完以後,目標進程掛起,使用ptrace_getregs函數獲取當前的全部寄存器值,方便後面使用ptrace_retval函數獲取函數的返回值。 311 * 參數: 312 * pid 須要注入的進程pid 313 * addr 調用的函數指針地址 314 * params 調用的參數 315 * num_params 調用的參數個數 316 * regs 遠程進程寄存器信息(ARM前4個參數由r0 ~ r3傳遞) 317 * 318 * 返回值: 失敗返回-1 319 *--------------------------------------------------*/ 320 int ptrace_call(pid_t pid, uint32_t addr, long* params, uint32_t num_params, struct pt_regs* regs){ 321 uint32_t i; 322 for(i = 0; i<num_params && i < 4; i++){ 323 regs->uregs[i] = params[i]; 324 } 325 326 if(i < num_params){ 327 regs->ARM_sp -= (num_params - i) * sizeof(long); 328 ptrace_writedata(pid, (void*)regs->ARM_sp, (uint8_t*)¶ms[i], (num_params - i)*sizeof(long)); 329 } 330 //將PC寄存器值設爲目標函數的地址 331 regs->ARM_pc = addr; 332 ////指令集判斷 333 if(regs->ARM_pc & 1){ 334 /* thumb */ 335 regs->ARM_pc &= (~1u); 336 regs->ARM_cpsr |= CPSR_T_MASK; 337 }else{ 338 /* arm */ 339 regs->ARM_cpsr &= ~CPSR_T_MASK; 340 } 341 ///設置子程序的返回地址爲空,以便函數執行完後,返回到null地址,產生SIGSEGV錯誤 342 regs->ARM_lr = 0; 343 344 //將修改後的regs寫入寄存器中,而後調用ptrace_continue來執行咱們指定的代碼 345 if(ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1){ 346 printf("error.\n"); 347 return -1; 348 } 349 350 int stat = 0; 351 /* WUNTRACED告訴waitpid,若是子進程進入暫停狀態,那麼就當即返回。若是是被ptrace的子進程,那麼即便不提供WUNTRACED參數,也會在子進程進入暫停狀態的時候當即返回。 352 對於使用ptrace_cont運行的子進程,它會在3種狀況下進入暫停狀態:①下一次系統調用;②子進程退出;③子進程的執行發生錯誤。這裏的0xb7f就表示子進程進入了暫停狀態, 353 且發送的錯誤信號爲11(SIGSEGV),它表示試圖訪問未分配給本身的內存, 或試圖往沒有寫權限的內存地址寫數據。那麼何時會發生這種錯誤呢?顯然,當子進程執行完注入的 354 函數後,因爲咱們在前面設置了regs->ARM_lr = 0,它就會返回到0地址處繼續執行,這樣就會產生SIGSEGV了! 355 */ 356 waitpid(pid, &stat, WUNTRACED); 357 /*stat的值:高2字節用於表示致使子進程的退出或暫停狀態信號值,低2字節表示子進程是退出(0x0)仍是暫停(0x7f)狀態。 358 0xb7f就表示子進程爲暫停狀態,致使它暫停的信號量爲11即sigsegv錯誤。*/ 359 while(stat != 0xb7f){ 360 if(ptrace_continue(pid) == -1){ 361 printf("error.\n"); 362 return -1; 363 } 364 waitpid(pid, &stat, WUNTRACED); 365 } 366 return 0; 367 } 368 369 /*-------------------------------------------------- 370 * 功能: 調用遠程函數指針 371 * 372 * 參數: 373 * pid 須要注入的進程pid 374 * func_name 調用的函數名稱, 此參數僅做Debug輸出用 375 * func_addr 調用的函數指針地址 376 * param 調用的參數 377 * param_num 調用的參數個數 378 * regs 遠程進程寄存器信息(ARM前4個參數由r0 ~ r3傳遞) 379 * 380 * 返回值: 失敗返回-1 381 *--------------------------------------------------*/ 382 int ptrace_call_wrapper(pid_t target_pid, const char* func_name, void* func_addr, long* param, int param_num, struct pt_regs* regs){ 383 LOGD("[+]Calling %s in target process.\n", func_name); 384 if(ptrace_call(target_pid, (uint32_t)func_addr, param, param_num, regs) == -1) 385 return -1; 386 if(ptrace_getregs(target_pid, regs) == -1){ 387 return -1; 388 } 389 LOGD("[+] Target process returned from %s, return value = %x, pc = %x \n", func_name, ptrace_retval(regs), ptrace_ip(regs)); 390 return 0; 391 } 392 393 /*-------------------------------------------------- 394 * 功能: 遠程注入 395 * 396 * 參數: 397 * target_pid 須要注入的進程Pid 398 * library_path 須要注入的.so路徑 399 * function_name .so中導出的函數名 400 * param 函數的參數 401 * param_size 參數大小,以字節爲單位 402 * 403 * 返回值: 注入失敗返回-1 404 *--------------------------------------------------*/ 405 int inject_remote_process(pid_t target_pid, const char* library_path, const char* function_name, const char* param, size_t param_size){ 406 int ret = -1; 407 void* mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; 408 void *local_handle, *remote_handle, *dlhandle; 409 uint8_t *map_base = 0; 410 uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; 411 412 struct pt_regs regs, original_regs; 413 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; 414 415 uint32_t code_length; 416 long parameters[10]; 417 418 LOGD("[+] Injecting process: %d\n", target_pid); 419 420 //①ATTATCH,指定目標進程,開始調試 421 if(ptrace_attach(target_pid) == -1){ 422 goto exit; 423 } 424 425 //②GETREGS,獲取目標進程的寄存器,保存現場 426 if(ptrace_getregs(target_pid, ®s) == -1) 427 goto exit; 428 429 //保存原始寄存器 430 memcpy(&original_regs, ®s, sizeof(regs)); 431 432 //③經過get_remote_addr函數獲取目標進程的mmap函數的地址,以便爲libxxx.so分配內存 433 //因爲mmap函數在libc.so庫中,爲了將libxxx.so加載到目標進程中,就須要使用目標進程的mmap函數,因此須要查找到libc.so庫在目標進程的起始地址。 434 mmap_addr = get_remote_addr(target_pid, libc_path, (void*)mmap); //libc_path = "/system/lib/libc.so" 435 LOGD("[+] Remote mmap address: %x\n", mmap_addr); 436 437 parameters[0] = 0; // 設置爲NULL表示讓系統自動選擇分配內存的地址 438 parameters[1] = 0x4000; // 映射內存的大小 439 parameters[2] = PROT_READ | PROT_WRITE |PROT_EXEC; // 表示映射內存區域可讀可寫可執行 440 parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // 創建匿名映射 441 parameters[4] = 0; //若須要映射文件到內存中,則爲文件的fd 442 parameters[5] = 0; //文件映射偏移量 443 444 //④經過ptrace_call_wrapper調用mmap函數,在目標進程中爲libxxx.so分配內存 445 if(ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1) 446 goto exit; 447 //⑤從寄存器中獲取mmap函數的返回值,即申請的內存首地址: 448 map_base = ptrace_retval(®s); 449 450 //⑥依次獲取linker中dlopen、dlsym、dlclose、dlerror函數的地址 451 dlopen_addr = get_remote_addr(target_pid, linker_path, (void*)dlopen); 452 dlsym_addr = get_remote_addr(target_pid, linker_path, (void*)dlsym); 453 dlclose_addr = get_remote_addr(target_pid, linker_path, (void*)dlclose); 454 dlerror_addr = get_remote_addr(target_pid, linker_path, (void*)dlerror); 455 456 LOGD("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n", dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); 457 458 printf("library path = %s\n", library_path); 459 //⑦調用dlopen函數 460 //(1)將要注入的so名寫入前面mmap出來的內存 461 ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1); 462 463 parameters[0] = map_base; 464 parameters[1] = RTLD_NOW | RTLD_GLOBAL; 465 466 //(2)執行dlopen 467 if(ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1){ 468 goto exit; 469 } 470 //(3)取得dlopen的返回值,存放在sohandle變量中 471 void* sohandle = ptrace_retval(®s); 472 473 //⑧調用dlsym函數 474 //爲functionname另找一塊區域 475 #define FUNCTION_NAME_ADDR_OFFSET 0X100 476 ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1); 477 parameters[0] = sohandle; 478 parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET; 479 480 //調用dlsym 481 if(ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1) 482 goto exit; 483 void* hook_entry_addr = ptrace_retval(®s); 484 LOGD("hooke_entry_addr = %p\n", hook_entry_addr); 485 486 //⑨調用被注入函數hook_entry 487 #define FUNCTION_PARAM_ADDR_OFFSET 0X200 488 ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, parameters, strlen(parameters) + 1); 489 parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET; 490 491 if(ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1) 492 goto exit; 493 //⑩調用dlclose關閉lib 494 printf("Press enter to dlclose and detach.\n"); 495 getchar(); 496 parameters[0] = sohandle; 497 498 if(ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1) 499 goto exit; 500 501 //⑪恢復現場並退出ptrace 502 ptrace_setregs(target_pid, &original_regs); 503 ptrace_detach(target_pid); 504 ret = 0; 505 506 exit: 507 return ret; 508 } 509 510 int main(int argc, char** argv) { 511 pid_t target_pid; 512 target_pid = find_pid_of("com.bbk.appstore"); 513 if (-1 == target_pid) { 514 printf("Can't find the process\n"); 515 return -1; 516 } 517 //target_pid = find_pid_of("/data/test"); 518 inject_remote_process(target_pid, "/data/local/tmp/libentry.so", "hook_entry", "Fuck you!", strlen("Fuck you!")); 519 return 0; 520 }
上述代碼中,咱們要hook的進程名爲"com.bbk.appstore",咱們要將「libentry.so」注入到該進程中去。ui
Android.mk內容爲:spa
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := inject LOCAL_SRC_FILES := inject.c #shellcode.s LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog #LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE)
Application.mk內容爲:
# 編譯生成的模塊文件運行支持的平臺 APP_ABI := armeabi-v7a # 編譯生成模塊運行支持的Andorid版本 APP_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的代碼爲:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <android/log.h> #include <elf.h> #include <fcntl.h> #define LOG_TAG "DEBUG" #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) int hook_entry(char * a){ LOGD("Hook success, pid = %d\n", getpid()); LOGD("Hello %s\n", a); return 0; }
Android.mk文件:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog #LOCAL_ARM_MODE := arm LOCAL_MODULE := entry LOCAL_SRC_FILES := entry.c include $(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