一、前言
總結一下這兩天學習的Android注入so文件,通過遍歷got表hook函數調用
1.注入so文件
2.so文件中遍歷got表hook函數
二、注入so文件
1)注入進程
1.編程思路分為以下幾個步驟
①.每個進程都在/proc目錄下,以進程id為文件夾名,所以可以通過/proc/<pid>/cmdline文件中中讀取進程名稱,和我們需要注入的進程名稱比較,獲得進程id
②.以root身份運行注入程序,通過ptrace函數,傳入PTRACE_ATIACH附加到目標進程,PTRACE_SETREGS設置進程寄存器,PTRACE_GETREGS獲得目標寄存器.更多可以訪問ptrace的使用
③.調用mmap在對方進程空間分配內存,保存要加載的so文件路徑,so中函數的名稱,so中函數需要傳入的參數。
由於每個模塊在進程中加載地址不一致,所以我們首先獲得目標進程中libc.so文件基址TargetBase,再獲得自身libc.so基址SelfBase,再根據mmap-SelfBase+TargetBase獲得目標進程中mmap的地址。
同理獲得目標進程中dlopen()函數地址、dlsym()函數地址、dlclose()函數地址
④.調用dlopen()函數加載so庫,調用dlsym()函數獲得so庫中函數的地址,調用so庫中函數的地址,測試注入成功!調用dlclose()函數卸載so庫。
2.創建文件以及編碼實現
首先創建一個jni目錄,在jni下創建三個文件分別為inject.c、Android.mk、Application.mk。(注意:必須在jni目錄下,否則編譯報錯)
jni
inject.c
Android.mk
Application.mk

#include <stdio.h> #include <stdlib.h> #include <asm/ptrace.h> #include <asm/user.h> #include <asm/ptrace.h> #include <sys/wait.h> #include <sys/mman.h> #include <dlfcn.h> #include <dirent.h> #include <unistd.h> #include <string.h> #include <android/log.h> #include <elf.h> #define ENABLE_DEBUG 1 #define PTRACE_PEEKTEXT 1 #define PTRACE_POKETEXT 4 #define PTRACE_ATTACH 16 #define PTRACE_CONT 7 #define PTRACE_DETACH 17 #define PTRACE_SYSCALL 24 #define CPSR_T_MASK ( 1u << 5 ) #define MAX_PATH 0x100 #define REMOTE_ADDR( addr, local_base, remote_base ) ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) ) const char *libc_path = "/system/lib/libc.so"; const char *linker_path = "/system/bin/linker"; #if defined(__i386__) #define pt_regs user_regs_struct #endif #if ENABLE_DEBUG #define LOG_TAG "INJECT" #define LOGD(fmt,args...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,fmt,##args) #define DEBUG_PRINT(format,args...) \ LOGD(format, ##args) #else #define DEBUG_PRINT(format,args...) #endif 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 { long val; 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; union u { long val; char chars[sizeof(long)]; } d; j = size / 4; remain = size % 4; laddr = data; for ( i = 0; i < j; i ++ ) { memcpy( d.chars, laddr, 4 ); ptrace( PTRACE_POKETEXT, pid, dest, d.val ); dest += 4; laddr += 4; } if ( remain > 0 ) { d.val = ptrace( PTRACE_PEEKTEXT, pid, dest, 0 ); for ( i = 0; i < remain; i ++ ) { d.chars[i] = *laddr ++; } ptrace( PTRACE_POKETEXT, pid, dest, d.val ); } return 0; } int ptrace_writestring( pid_t pid, uint8_t *dest, char *str ) { return ptrace_writedata( pid, dest, str, strlen(str)+1 ); } //在目標進程中執行指定函數 #if defined(__arm__) 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; if ( regs->ARM_pc & 1 ) { /* thumb 判斷最后一位,如果是1就是thumb指令集 0 arm指令集 */ 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; } #elif defined(__i386__) long ptrace_call(pid_t pid, uint32_t addr, long* params, uint32_t num_params, struct user_regs_struct* regs) { regs->esp -= (num_params)*sizeof(long); /*開辟堆棧空間 存儲參數*/ ptrace_writedata(pid,(void*)regs->esp,(uint8_t*)params,(num_params)*sizeof(long)); long tmp_addr = 0x00; regs->esp -= sizeof(long); ptrace_writedata(pid,regs->esp,(char*)&tmp_addr,sizeof(tmp_addr)); regs->eip = addr; //修改指令指針寄存器,指向要運行的函數 if(ptrace_setregs(pid,regs)==-1 || ptrace_continue(pid) == -1) //恢復函數狀態,函數入口處運行 { printf("error\n"); return -1; } int stat = 0; waitpid(pid,&stat,WUNTRACED); while(stat!= 0xb7f) { if(ptrace_continue(pid)==-1) { printf("error\n"); return -1; } waitpid(pid,&stat,WUNTRACED); } return 0; } #else #error "Not supported" #endif //獲取目標進程寄存器 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; } int ptrace_continue( pid_t pid ) { if ( ptrace( PTRACE_CONT, pid, NULL, 0 ) < 0 ) { perror( "ptrace_cont" ); return -1; } return 0; } //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_detach( pid_t pid ) { if ( ptrace( PTRACE_DETACH, pid, NULL, 0 ) < 0 ) { perror( "ptrace_detach" ); 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函數在目標進程的絕對地址 void* ret_addr = (void *)( (uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle ); #if defined(__i386__) if(!strcmp(module_name,libc_path)){ ret_addr += 2; } #endif return ret_addr; } //讀取/proc目錄下以id為文件夾名的文件夾內cmdline的內容 int find_pid_of( const char *process_name ) { int id; pid_t pid = -1; DIR* dir; FILE *fp; char filename[32]; char cmdline[256]; struct dirent * entry; if ( process_name == NULL ) return -1; dir = opendir( "/proc" ); if ( dir == NULL ) return -1; while( (entry = readdir( dir )) != NULL ) { id = atoi( entry->d_name ); if ( id != 0 ) { sprintf( filename, "/proc/%d/cmdline", id ); fp = fopen( filename, "r" ); if ( fp ) { fgets( cmdline, sizeof(cmdline), fp ); fclose( fp ); if ( strcmp( process_name, cmdline ) == 0 ) { /* process found */ pid = id; break; } } } } closedir( dir ); return pid; } 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) //修改eip,運行函數 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; } int inject_remote_process( pid_t target_pid, const char *library_path, const char *func_name, void *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; 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; 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 ); /*attach到指定進程*/ if ( ptrace_attach( target_pid ) == -1 ) return EXIT_SUCCESS; /*獲得進程寄存器*/ if ( ptrace_getregs( target_pid, ®s ) == -1 ) goto exit; /*保存進程寄存器值*/ memcpy( &original_regs, ®s, sizeof(regs) ); /*通過自己進程中mmap函數相對與libc.so基址的偏移,在目標進程中通過libc.so基址獲得mmap地址*/ mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap ); DEBUG_PRINT( "[+] Remote mmap address: %x\n", mmap_addr ); /* 調用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_wrapper(target_pid,"mmap",mmap_addr,parameters,6,®s)==-1) //調用mmap在目標進程中分配內存空間 goto exit; map_base = ptrace_retval(®s); //取回分配的地址 DEBUG_PRINT("mmap_base is %x",map_base); dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen ); //獲得目標進程中dlopen函數地址 dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym ); //獲得目標進程中dlsym函數地址 dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose );//獲得目標進程中dlclose函數地址 dlerror_addr = get_remote_addr(target_pid,linker_path,(void *)dlerror); //獲得目標進程中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) //調用dlopen函數,加載動態庫 goto exit; void* sohandle = ptrace_retval(®s); //返回加載動態庫句柄 #define FUNCTION_NAME_ADDR_OFFSET 0x100 ptrace_writedata(target_pid,map_base+FUNCTION_NAME_ADDR_OFFSET,func_name,strlen(func_name)+1); //將動態庫中函數hook_entry的名稱寫入 分配地址+0x100的地方 parameters[0] = sohandle; parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET; if(ptrace_call_wrapper(target_pid,"dlsym",dlsym_addr,parameters,2,®s)==-1) //調用dlsym,獲得動態庫中hook_entry的地址 goto exit; void* hook_entry_addr = ptrace_retval(®s); //獲得hook_entry函數的地址 DEBUG_PRINT("hook_entry_addr = %p\n",hook_entry_addr); #define FUNCTION_PARAM_ADDR_OFFSET 0x200 ptrace_writedata(target_pid,map_base+FUNCTION_PARAM_ADDR_OFFSET,param,strlen(param)+1); //將傳入參數 "I'm parameter!" 寫入分配地址空間+0x200處 parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET; if(ptrace_call_wrapper(target_pid,"hook_entry",hook_entry_addr,parameters,1,®s)==-1) //調用注入的動態庫中hook_entry函數,傳入參數"I'm parameter!" goto exit; printf("Press enter to dlclose and detach\n"); //結束,等待 getchar(); parameters[0] = sohandle; if(ptrace_call_wrapper(target_pid,"dlclose",dlclose,parameters,1,®s)==-1) //調用dlclose卸載動態庫 goto exit; ptrace_setregs(target_pid,&original_regs); //還原寄存器 ptrace_detach(target_pid); //關閉 ret = 0; exit: return ret; } int main(int argc, char** argv) { pid_t target_pid; target_pid = find_pid_of("/system/bin/surfaceflinger"); inject_remote_process( target_pid, "/data/libhello.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!") ); }
使用ps命令可以查看進程列表,獲取進程ID和路徑,然后在main中輸入進程的路徑,注入進程
Android.mk:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := inject LOCAL_SRC_FILES := inject.c LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog include $(BUILD_EXECUTABLE)
Application.mk
APP_ABI := x86 armeabi-v7a
然后使用ndk-build命令編譯生成可執行文件,一定要在jni目錄下,不然編譯會報錯,記住 __android_log_print函數前面有兩個下划線,在Android.mk中申明的庫 LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
2)so文件測試Demo
我們創建so文件測試是否inject是否能注入成功,並調用so中函數
創建目錄和文件
jni
hello.c
Android.mk
Application.mk
hello.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); //調用傳入的參數 "I'm parameter!" return 0; }
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog LOCAL_MODULE :=hello LOCAL_SRC_FILES:= hello.c include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := x86 armeabi-v7a
使用ndk-build編譯生成x86和arm平台下的so文件:
然后就可以執行,連接root過的Android或者Android虛擬機,將inject和so文件考入設備,設置執行權限,執行。
我們現在可以查看進程內存,另起一個cmd窗口 ,因為我們在文件中的Log標志為INJECT,所以我們先打印log
使用 adb logcat -s INJECT命令
可以看到我們注入的進程id為36,我們查看這個進程的內存中加載的模塊
使用命令 cat /proc/36/maps
...
注入成功!
3)通過so文件實現got表Hook
1.編碼思路
首先了解一下動態加載機制,
a、模塊甲在編譯期間,將要引用的模塊乙的名字與函數名寫入自身的符號表。
b、運行期模塊甲調用時,調用流程是從調用代碼到PLT表到GOT表再跳入模塊乙。
也就是got表中保存着函數地址。
更多ELF文件了解可以參考:ELF文件格式解析
①首先保存系統中的函數地址,這里直接是調用函數的名稱。
②獲取函數所在模塊基地址,通過遍歷/proc/<pid>/maps文件
③遍歷模塊的got表,地址與保存的地址一致則hook,如果和fake函數一致則已經Hook過了。
修改的hello.c文件
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <android/log.h> #include <elf.h> #include <EGL/egl.h> #include <GLES/gl.h> #include <elf.h> #include <fcntl.h> #include <sys/mman.h> #define LOG_TAG "DEBUG" #define LOGD(fmt,args...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,fmt,##args) EGLBoolean (*old_eglSwapBuffers)(EGLDisplay dpy,EGLSurface surf) = -1; EGLBoolean new_eglSwapBuffers(EGLDisplay dpy,EGLSurface surface) { LOGD("New eglSwapBuffers"); if(old_eglSwapBuffers==-1) LOGD("error\n"); return old_eglSwapBuffers(dpy,surface); } 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){ 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; } #define LIBSF_PATH "/system/lib/libsurfaceflinger.so" int hook_eglSwapBuffers() { old_eglSwapBuffers = eglSwapBuffers; //保存系統中原來eglSwapBuffers函數地址,在Android.mk中加入庫 LOGD("Orig eglSwapBuffers = %p\n",old_eglSwapBuffers); void* base_addr = get_module_base(getpid(),LIBSF_PATH); //動態庫地址 LOGD("libsurfaceflinger.so address = %p\n",base_addr); int fd; fd = open(LIBSF_PATH,O_RDONLY); if(fd==-1){ LOGD("error\n"); return -1; } Elf32_Ehdr ehdr; //ELF header read(fd,&ehdr,sizeof(Elf32_Ehdr)); //讀取ELF文件格式的文件頭信息 unsigned long shdr_addr = ehdr.e_shoff; //section header table文件中的偏移 int shnum = ehdr.e_shnum; //section header table中有多少個條目 int shent_size = ehdr.e_shentsize; //section header table每一個條目的大小 unsigned long stridx = ehdr.e_shstrndx; //包含節名稱的字符串是第幾個節(從0開始) Elf32_Shdr shdr; //節頭結構定義 lseek(fd,shdr_addr+stridx*shent_size,SEEK_SET); //偏移到文件尾 read(fd,&shdr,shent_size); //讀取字符串表的信息 char* string_table = (char*)malloc(shdr.sh_size);//分配內存 lseek(fd,shdr.sh_offset,SEEK_SET);//偏移到字符串表 read(fd,string_table,shdr.sh_size); //讀取字符串表的內容 lseek(fd,shdr_addr,SEEK_SET);//還原指針到section header table處 int i; uint32_t out_addr = 0; uint32_t out_size = 0; uint32_t got_item = 0; int32_t got_found = 0; for(i = 0; i < shnum; i++){//每個節頭信息,找到got表 read(fd,&shdr,shent_size); if(shdr.sh_type == SHT_PROGBITS){ int name_idx = shdr.sh_name;//名稱索引 if(strcmp(&(string_table[name_idx]),".got.plt")==0 || strcmp(&(string_table[name_idx]),".got")==0){ out_addr = base_addr + shdr.sh_addr;//獲得got表 out_size = shdr.sh_size; LOGD("out_addr = %lx,out_size = %lx\n",out_addr,out_size); for(i=0;i<out_size;i+=4){ got_item = *(uint32_t*)(out_addr+i); if(got_item == old_eglSwapBuffers){ LOGD("Found eglSwapBuffers in got\n"); got_found = 1; //hook uint32_t page_size = getpagesize(); uint32_t entry_page_start = (out_addr + i)&(~(page_size-1)); mprotect((uint32_t*)entry_page_start,page_size,PROT_READ|PROT_WRITE); *(uint32_t*)(out_addr + i) = new_eglSwapBuffers; break; }else if(got_item == new_eglSwapBuffers){ LOGD("Already hooked\n"); break; } } if(got_found) break; } } } free(string_table); close(fd); } int hook_entry(char* a ){ LOGD("Hook success,pid=%d\n",getpid()); LOGD("Hook information: %s\n",a); LOGD("Start hooking\n"); hook_eglSwapBuffers(); return 0; }
Android.ml改為
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lEGL LOCAL_MODULE :=hello LOCAL_SRC_FILES:= hello.c include $(BUILD_SHARED_LIBRARY)
Application.mk改為
APP_ABI := x86 armeabi-v7a APP_PLATFORM := android-14
運行ndk-build編譯,和上面一樣執行,我們可以看到log信息已經hook成功了
三、總結
這里我們在/proc/<pid>/cmdline文件中比較進程名稱,在/proc/<pid>/maps文件中查找進程模塊,使用ptrace系列函數進行進程、寄存器操作,使用mmap函數在其他進程分配內存空間,使用dlopen獲取so庫地址,使用dlsym獲取so庫中函數地址,使用dlclose卸載so庫,通過got表獲取調用函數地址,通過mprotect更改保護屬性。
一次不錯的學習體驗!
參考: