├── GOT_HOOK ├── hook.c └── libhooktest.so ├── INLINE_HOOK ├── libthook.so └── thumb_inline_hook.c ├── INSTRUCTION.md ├── README.md ├── hello.c ├── libhello.so ├── so_inject └── so_inject.c /GOT_HOOK/hook.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define LOG_TAG "INJECT" 12 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 13 | 14 | // extern jstring Java_xh_helloworld_MainActivity_stringFromJNI(JNIEnv *env, jobject clazz); 15 | #define HOOKED_FUNC "Java_xh_helloworld_MainActivity_stringFromJNI" 16 | 17 | jstring (*old_native_fuc)(JNIEnv *env, jobject clazz) = -1; 18 | 19 | JNIEXPORT jstring new_native_fuc 20 | (JNIEnv *env, jobject clazz) 21 | { 22 | return (*env)->NewStringUTF(env, "This is test!!!"); 23 | } 24 | 25 | void* get_module_base(pid_t pid, const char* module_name) 26 | { 27 | FILE *fp; 28 | long addr = 0; 29 | char *pch; 30 | char filename[32]; 31 | char line[1024]; 32 | 33 | if (pid < 0) { 34 | // self process 35 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid); 36 | } else { 37 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 38 | } 39 | 40 | fp = fopen(filename, "r"); 41 | 42 | if (fp != NULL) { 43 | while (fgets(line, sizeof(line), fp)) { 44 | if (strstr(line, module_name)) { 45 | pch = strtok( line, "-" ); 46 | addr = strtoul( pch, NULL, 16 ); 47 | if (addr == 0x8000) 48 | addr = 0; 49 | break; 50 | } 51 | } 52 | fclose(fp) ; 53 | } 54 | return (void *)addr; 55 | } 56 | 57 | #define LIBSF_PATH "/data/app/xh.helloworld-1/lib/arm/libnative-lib.so" 58 | int hook_native() 59 | { 60 | void * base_addr = get_module_base(getpid(), LIBSF_PATH); 61 | 62 | LOGD("target so address = %p\n", base_addr); 63 | 64 | int fd; 65 | fd = open(LIBSF_PATH, O_RDONLY); 66 | if (-1 == fd) { 67 | LOGD("error\n"); 68 | return -1; 69 | } 70 | 71 | void * handler = dlopen(LIBSF_PATH, RTLD_NOLOAD); 72 | if(!handler) 73 | { 74 | printf("%s", dlerror()); 75 | return -1; 76 | } 77 | 78 | old_native_fuc = dlsym(handler, HOOKED_FUNC); 79 | if(!old_native_fuc) 80 | { 81 | printf("%s", dlerror()); 82 | return -1; 83 | } 84 | 85 | old_native_fuc = Java_xh_helloworld_MainActivity_stringFromJNI; 86 | 87 | LOGD("orig fuc = %p\n", old_native_fuc); 88 | Elf32_Ehdr ehdr; 89 | read(fd, &ehdr, sizeof(Elf32_Ehdr)); 90 | 91 | unsigned long shdr_addr = ehdr.e_shoff; 92 | int shnum = ehdr.e_shnum; 93 | int shent_size = ehdr.e_shentsize; 94 | unsigned long stridx = ehdr.e_shstrndx; 95 | 96 | Elf32_Shdr shdr; 97 | lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET); 98 | read(fd, &shdr, shent_size); 99 | 100 | char * string_table = (char *)malloc(shdr.sh_size); 101 | lseek(fd, shdr.sh_offset, SEEK_SET); 102 | read(fd, string_table, shdr.sh_size); 103 | lseek(fd, shdr_addr, SEEK_SET); 104 | 105 | int i; 106 | uint32_t out_addr = 0; 107 | uint32_t out_size = 0; 108 | uint32_t got_item = 0; 109 | int32_t got_found = 0; 110 | 111 | for (i = 0; i < shnum; i++) 112 | { 113 | read(fd, &shdr, shent_size); 114 | if (shdr.sh_type == SHT_PROGBITS) 115 | { 116 | int name_idx = shdr.sh_name; 117 | if (strcmp(&(string_table[name_idx]), ".got.plt") == 0 || strcmp(&(string_table[name_idx]), ".got") == 0) 118 | { 119 | out_addr = base_addr + shdr.sh_addr; 120 | out_size = shdr.sh_size; 121 | LOGD("out_addr = %lx, out_size = %lx\n", out_addr, out_size); 122 | 123 | for (i = 0; i < out_size; i += 4) 124 | { 125 | got_item = *(uint32_t *)(out_addr + i); 126 | LOGD("GOT[%d] = %lx", i, got_item); 127 | if (got_item == old_native_fuc) 128 | { 129 | LOGD("Found target fuc in got!\n"); 130 | got_found = 1; 131 | 132 | uint32_t page_size = getpagesize(); 133 | uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1)); 134 | mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE); 135 | *(uint32_t *)(out_addr + i) = new_native_fuc; 136 | 137 | break; 138 | } else if (got_item == new_native_fuc) 139 | { 140 | LOGD("Already hooked\n"); 141 | break; 142 | } 143 | } 144 | if (got_found) 145 | break; 146 | } 147 | } 148 | } 149 | 150 | free(string_table); 151 | close(fd); 152 | } 153 | 154 | int hook_entry(char * a){ 155 | LOGD("Hook success\n"); 156 | LOGD("Start hooking\n"); 157 | hook_native(); 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /GOT_HOOK/libhooktest.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlvsjp/android_native_hook/79f0beb7dd09fc24c91eed2797b5e6a3b51e048c/GOT_HOOK/libhooktest.so -------------------------------------------------------------------------------- /INLINE_HOOK/libthook.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlvsjp/android_native_hook/79f0beb7dd09fc24c91eed2797b5e6a3b51e048c/INLINE_HOOK/libthook.so -------------------------------------------------------------------------------- /INLINE_HOOK/thumb_inline_hook.c: -------------------------------------------------------------------------------- 1 | /** 2 | 适用于Thumb指令集的Native函数INLINE HOOK 3 | Hook 函数入口点: hook_entry 4 | 参数:0 - 直接返回Hook函数 5 | 1 - 调用Hook函数后,返回原函数 6 | 宏定义: 7 | LIBSF_PATH : 待HOOK的so绝对路径 8 | HOOKED_FUNC : 待HOOK的函数 9 | **/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define LOG_TAG "INJECT" 22 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 23 | 24 | #define LIBSF_PATH "/data/app/xh.helloworld-1/lib/arm/libnative-lib.so" 25 | #define HOOKED_FUNC "Java_xh_helloworld_MainActivity_stringFromJNI" 26 | 27 | 28 | jstring (*old_native_func)(JNIEnv *env, jobject clazz) = -1; 29 | 30 | typedef struct tagHookInfo 31 | { 32 | uint32_t m_nNewCode[3]; //新的汇编代码 jmp xxx 33 | uint16_t m_nOldCode[6]; //原先的汇编代码 34 | uint32_t m_nNewPfnAddr; //新函数地址 35 | uint32_t m_nOldPfnAddr; //原先的函数地址 36 | } HOOK_INFO; 37 | 38 | typedef unsigned int (*OLDFUC)(JNIEnv *, jobject); 39 | OLDFUC g_pfnOldAddr = NULL; 40 | HOOK_INFO g_HookInfo; 41 | int FLAG = 0; 42 | 43 | JNIEXPORT jstring new_native_func 44 | (JNIEnv *env, jobject clazz) 45 | { 46 | LOGD("This is new_native_func"); 47 | return (*env)->NewStringUTF(env, "Hello CFCA!"); 48 | } 49 | 50 | 51 | //刷新指令缓存 52 | void CacheFlush(unsigned int begin, unsigned int end) 53 | { 54 | const int syscall = 0xf0002; 55 | 56 | __asm __volatile ( 57 | "mov r0, %0\n" 58 | "mov r1, %1\n" 59 | "mov r7, %2\n" 60 | "mov r2, #0x0\n" 61 | "svc 0x00000000\n" 62 | : 63 | : "r" (begin), "r" (end), "r" (syscall) 64 | : "r0", "r1", "r7" 65 | ); 66 | } 67 | 68 | //写入新指令 69 | void WriteNewCode() 70 | { 71 | memcpy((void *)g_HookInfo.m_nOldPfnAddr, g_HookInfo.m_nNewCode, sizeof(uint32_t) * 3); 72 | CacheFlush(g_HookInfo.m_nOldPfnAddr, g_HookInfo.m_nOldPfnAddr + sizeof(uint32_t) * 3); 73 | } 74 | 75 | 76 | //还原旧指令 77 | void WriteOldCode() 78 | { 79 | memcpy((void *)g_HookInfo.m_nOldPfnAddr, g_HookInfo.m_nOldCode, sizeof(uint16_t) * 6); 80 | CacheFlush(g_HookInfo.m_nOldPfnAddr, g_HookInfo.m_nOldPfnAddr + sizeof(uint16_t) * 6); 81 | } 82 | 83 | 84 | //新函数 85 | jstring NewFuc(JNIEnv * jenv, jobject clazz) 86 | { 87 | LOGD("Hello New Fuc"); 88 | 89 | // 调用hook函数 90 | jstring ret = new_native_func(jenv, clazz); 91 | 92 | if(FLAG){ 93 | //还原 94 | WriteOldCode(); 95 | 96 | //调用原先的函数 97 | g_pfnOldAddr = (OLDFUC)((char *)(g_HookInfo.m_nOldPfnAddr) + 1); 98 | ret = (*g_pfnOldAddr)(jenv, clazz); 99 | 100 | //继续Hook 101 | WriteNewCode(); 102 | } 103 | 104 | return ret; 105 | } 106 | 107 | 108 | void* get_module_base(pid_t pid, const char* module_name) 109 | { 110 | FILE *fp; 111 | long addr = 0; 112 | char *pch; 113 | char filename[32]; 114 | char line[1024]; 115 | 116 | if (pid < 0) { 117 | // self process 118 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid); 119 | } else { 120 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 121 | } 122 | 123 | fp = fopen(filename, "r"); 124 | 125 | if (fp != NULL) { 126 | while (fgets(line, sizeof(line), fp)) { 127 | if (strstr(line, module_name)) { 128 | pch = strtok( line, "-" ); 129 | addr = strtoul( pch, NULL, 16 ); 130 | if (addr == 0x8000) 131 | addr = 0; 132 | break; 133 | } 134 | } 135 | fclose(fp) ; 136 | } 137 | return (void *)addr; 138 | } 139 | 140 | int hook_native() 141 | { 142 | int i = 0; 143 | void * base_addr = get_module_base(getpid(), LIBSF_PATH); 144 | LOGD("target so address = %p\n", base_addr); 145 | 146 | // 获得需要hook的函数地址 147 | void * handler = dlopen(LIBSF_PATH, RTLD_NOLOAD); 148 | if(!handler) 149 | { 150 | printf("%s", dlerror()); 151 | return -1; 152 | } 153 | 154 | old_native_func = (uint32_t)dlsym(handler, HOOKED_FUNC) - 1; 155 | if(!old_native_func) 156 | { 157 | printf("%s", dlerror()); 158 | return -1; 159 | } 160 | 161 | LOGD("orig fuc = %p\n", old_native_func); 162 | 163 | // 初始化hook结构体 164 | g_HookInfo.m_nNewPfnAddr = (uint32_t)NewFuc; 165 | g_HookInfo.m_nOldPfnAddr = (uint32_t)old_native_func; 166 | 167 | // BX PC; nop 168 | g_HookInfo.m_nNewCode[0] = 0x46C04778; 169 | 170 | // LDR PC [PC, #-0x4] 171 | g_HookInfo.m_nNewCode[1] = 0xE51FF004; 172 | 173 | // 新函数地址 174 | g_HookInfo.m_nNewCode[2] = g_HookInfo.m_nNewPfnAddr; 175 | 176 | // 保存原指令 177 | for (; i < 6; i++) 178 | { 179 | g_HookInfo.m_nOldCode[i] = ((uint16_t *)g_HookInfo.m_nOldPfnAddr)[i]; 180 | } 181 | 182 | // 获得该函数所在的内存页面 183 | uint32_t page_size = getpagesize(); 184 | uint32_t entry_page_start = (uint32_t)old_native_func & (~(page_size - 1)); 185 | 186 | // 修改该内存属性为可读可写可执行 187 | if (mprotect((uint32_t *)entry_page_start, page_size, PROT_WRITE | PROT_READ | PROT_EXEC) != 0 ) 188 | { 189 | LOGD("修改内存保护属性错误!"); 190 | return -1; 191 | } 192 | 193 | // 写入新的汇编代码 194 | WriteNewCode(); 195 | } 196 | 197 | 198 | /* 入口点 */ 199 | int hook_entry(char * a){ 200 | LOGD("Hook success\n"); 201 | LOGD("Start hooking\n"); 202 | FLAG = atoi(a); 203 | hook_native(); 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /INSTRUCTION.md: -------------------------------------------------------------------------------- 1 | ##COMPLIE COMMAND: 2 | 3 | #for so file: 4 | -gcc --sysroot=/sysroot -llog -ldl -fPIC -shared -o libthook.so hook.c 5 | 6 | #for so_inject executable file: 7 | -gcc --sysroot=/sysroot -llog -ldl -fPIE -pie -llog -o so_inject so_inject.c 8 | 9 | #default hook entry: 10 | `hook_entry` 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android_native_hook 2 | Android hook by so inject 3 | -------------------------------------------------------------------------------- /hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOG_TAG "INJECT" 9 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 10 | 11 | int hook_entry(char * a){ 12 | LOGD("Hook success, pid = %d\n", getpid()); 13 | LOGD("Hello %s\n", a); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /libhello.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlvsjp/android_native_hook/79f0beb7dd09fc24c91eed2797b5e6a3b51e048c/libhello.so -------------------------------------------------------------------------------- /so_inject: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlvsjp/android_native_hook/79f0beb7dd09fc24c91eed2797b5e6a3b51e048c/so_inject -------------------------------------------------------------------------------- /so_inject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #if defined(__i386__) 17 | #define pt_regs user_regs_struct 18 | #elif defined(__aarch64__) 19 | #define pt_regs user_pt_regs 20 | #define uregs regs 21 | #define ARM_pc pc 22 | #define ARM_sp sp 23 | #define ARM_cpsr pstate 24 | #define ARM_lr regs[30] 25 | #define ARM_r0 regs[0] 26 | #define PTRACE_GETREGS PTRACE_GETREGSET 27 | #define PTRACE_SETREGS PTRACE_SETREGSET 28 | #endif 29 | 30 | #define ENABLE_DEBUG 1 31 | 32 | #if ENABLE_DEBUG 33 | #define LOG_TAG "INJECT" 34 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, fmt, ##args) 35 | #define DEBUG_PRINT(format,args...) \ 36 | LOGD(format, ##args) 37 | #else 38 | #define DEBUG_PRINT(format,args...) 39 | #endif 40 | 41 | #define CPSR_T_MASK ( 1u << 5 ) 42 | 43 | #if defined(__aarch64__) 44 | const char *libc_path = "/system/lib64/libc.so"; 45 | const char *linker_path = "/system/bin/linker64"; 46 | #else 47 | const char *libc_path = "/system/lib/libc.so"; 48 | const char *linker_path = "/system/bin/linker"; 49 | #endif 50 | 51 | int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size) 52 | { 53 | long i, j, remain; 54 | uint8_t *laddr; 55 | size_t bytes_width = sizeof(long); 56 | 57 | union u { 58 | long val; 59 | char chars[bytes_width]; 60 | } d; 61 | 62 | j = size / bytes_width; 63 | remain = size % bytes_width; 64 | 65 | laddr = buf; 66 | 67 | for (i = 0; i < j; i ++) { 68 | d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0); 69 | memcpy(laddr, d.chars, bytes_width); 70 | src += bytes_width; 71 | laddr += bytes_width; 72 | } 73 | 74 | if (remain > 0) { 75 | d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0); 76 | memcpy(laddr, d.chars, remain); 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | /* 83 | Func : 将size字节的data数据写入到pid进程的dest地址处 84 | @param dest: 目的进程的栈地址 85 | @param data: 需要写入的数据的起始地址 86 | @param size: 需要写入的数据的大小,以字节为单位 87 | */ 88 | int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size) 89 | { 90 | long i, j, remain; 91 | uint8_t *laddr; 92 | size_t bytes_width = sizeof(long); 93 | 94 | //很巧妙的联合体,这样就可以方便的以字节为单位写入4字节数据,再以long为单位ptrace_poketext到栈中  95 | union u { 96 | long val; 97 | char chars[bytes_width]; 98 | } d; 99 | 100 | j = size / bytes_width; 101 | remain = size % bytes_width; 102 | 103 | laddr = data; 104 | 105 | //先以4字节为单位进行数据写入 106 | 107 | for (i = 0; i < j; i ++) { 108 | memcpy(d.chars, laddr, bytes_width); 109 | ptrace(PTRACE_POKETEXT, pid, dest, d.val); 110 | 111 | dest += bytes_width; 112 | laddr += bytes_width; 113 | } 114 | 115 | if (remain > 0) { 116 | //为了最大程度的保持原栈的数据,先读取dest的long数据,然后只更改其中的前remain字节,再写回 117 | d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0); 118 | for (i = 0; i < remain; i ++) { 119 | d.chars[i] = *laddr ++; 120 | } 121 | 122 | ptrace(PTRACE_POKETEXT, pid, dest, d.val); 123 | } 124 | 125 | return 0; 126 | } 127 | 128 | /* 129 | 功能总结: 130 | 1,将要执行的指令写入寄存器中,指令长度大于4个long的话,需要将剩余的指令通过ptrace_writedata函数写入栈中; 131 | 2,使用ptrace_continue函数运行目的进程,直到目的进程返回状态值0xb7f(对该值的分析见后面红字); 132 | 3,函数执行完之后,目标进程挂起,使用ptrace_getregs函数获取当前的所有寄存器值,方便后面使用ptrace_retval函数获取函数的返回值。 133 | */ 134 | #if defined(__arm__) || defined(__aarch64__) 135 | int ptrace_call(pid_t pid, uintptr_t addr, long *params, int num_params, struct pt_regs* regs) 136 | { 137 | int i; 138 | #if defined(__arm__) 139 | int num_param_registers = 4; 140 | #elif defined(__aarch64__) 141 | int num_param_registers = 8; 142 | #endif 143 | 144 | for (i = 0; i < num_params && i < num_param_registers; i ++) { 145 | regs->uregs[i] = params[i]; 146 | } 147 | 148 | // 149 | // push remained params onto stack 150 | // 151 | if (i < num_params) { 152 | regs->ARM_sp -= (num_params - i) * sizeof(long) ; 153 | ptrace_writedata(pid, (void *)regs->ARM_sp,(uint8_t *)& params[i], (num_params - i) * sizeof(long)); 154 | } 155 | //将PC寄存器值设为目标函数的地址 156 | regs->ARM_pc = addr; 157 | //进行指令集判断  158 | if (regs->ARM_pc & 1) { 159 | /* thumb */ 160 | regs->ARM_pc &= (~1u); 161 | // #define CPSR_T_MASK  ( 1u << 5 )  CPSR为程序状态寄存器 162 | regs->ARM_cpsr |= CPSR_T_MASK; 163 | } else { 164 | /* arm */ 165 | regs->ARM_cpsr &= ~CPSR_T_MASK; 166 | } 167 | 168 | //设置子程序的返回地址为空,以便函数执行完后,返回到null地址,产生SIGSEGV错误,详细作用见后面的红字分析 169 | regs->ARM_lr = 0; 170 | 171 | /* 172 |     *Ptrace_setregs就是将修改后的regs写入寄存器中,然后调用ptrace_continue来执行我们指定的代码 173 |     */ 174 | if (ptrace_setregs(pid, regs) == -1 175 | || ptrace_continue(pid) == -1) { 176 | printf("error\n"); 177 | return -1; 178 | } 179 | 180 | int stat = 0; 181 | waitpid(pid, &stat, WUNTRACED); 182 | /* WUNTRACED告诉waitpid,如果子进程进入暂停状态,那么就立即返回。如果是被ptrace的子进程,那么即使不提供WUNTRACED参数,也会在子进程进入暂停状态的时候立即返回。 183 | 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。这里的0xb7f就表示子进程进入了暂停状态,且发送的错误信号为11(SIGSEGV),它表示试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。那么什么时候会发生这种错误呢?显然,当子进程执行完注入的函数后,由于我们在前面设置了regs->ARM_lr = 0,它就会返回到0地址处继续执行,这样就会产生SIGSEGV了!*/ 184 | 185 | //这个循环是否必须我还不确定。因为目前每次ptrace_call调用必定会返回0xb7f,不过在这也算是增加容错性吧~ 186 | 187 | //通过看ndk的源码sys/wait.h以及man waitpid可以知道这个0xb7f的具体作用。首先说一下stat的值:高2字节用于表示导致子进程的退出或暂停状态信号值,低2字节表示子进程是退出(0x0)还是暂停(0x7f)状态。0xb7f就表示子进程为暂停状态,导致它暂停的信号量为11即sigsegv错误。 188 | while (stat != 0xb7f) { 189 | if (ptrace_continue(pid) == -1) { 190 | printf("error\n"); 191 | return -1; 192 | } 193 | waitpid(pid, &stat, WUNTRACED); 194 | } 195 | 196 | return 0; 197 | } 198 | 199 | #elif defined(__i386__) 200 | long ptrace_call(pid_t pid, uintptr_t addr, long *params, int num_params, struct user_regs_struct * regs) 201 | { 202 | regs->esp -= (num_params) * sizeof(long) ; 203 | ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long)); 204 | 205 | long tmp_addr = 0x00; 206 | regs->esp -= sizeof(long); 207 | ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr)); 208 | 209 | regs->eip = addr; 210 | 211 | if (ptrace_setregs(pid, regs) == -1 212 | || ptrace_continue( pid) == -1) { 213 | printf("error\n"); 214 | return -1; 215 | } 216 | 217 | int stat = 0; 218 | waitpid(pid, &stat, WUNTRACED); 219 | while (stat != 0xb7f) { 220 | if (ptrace_continue(pid) == -1) { 221 | printf("error\n"); 222 | return -1; 223 | } 224 | waitpid(pid, &stat, WUNTRACED); 225 | } 226 | 227 | return 0; 228 | } 229 | #else 230 | #error "Not supported" 231 | #endif 232 | 233 | int ptrace_getregs(pid_t pid, struct pt_regs * regs) 234 | { 235 | #if defined (__aarch64__) 236 | int regset = NT_PRSTATUS; 237 | struct iovec ioVec; 238 | 239 | ioVec.iov_base = regs; 240 | ioVec.iov_len = sizeof(*regs); 241 | if (ptrace(PTRACE_GETREGSET, pid, (void*)regset, &ioVec) < 0) { 242 | perror("ptrace_getregs: Can not get register values"); 243 | printf(" io %llx, %d", ioVec.iov_base, ioVec.iov_len); 244 | return -1; 245 | } 246 | 247 | return 0; 248 | #else 249 | if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) { 250 | perror("ptrace_getregs: Can not get register values"); 251 | return -1; 252 | } 253 | 254 | return 0; 255 | #endif 256 | } 257 | 258 | int ptrace_setregs(pid_t pid, struct pt_regs * regs) 259 | { 260 | #if defined (__aarch64__) 261 | int regset = NT_PRSTATUS; 262 | struct iovec ioVec; 263 | 264 | ioVec.iov_base = regs; 265 | ioVec.iov_len = sizeof(*regs); 266 | if (ptrace(PTRACE_SETREGSET, pid, (void*)regset, &ioVec) < 0) { 267 | perror("ptrace_setregs: Can not get register values"); 268 | return -1; 269 | } 270 | 271 | return 0; 272 | #else 273 | if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) { 274 | perror("ptrace_setregs: Can not set register values"); 275 | return -1; 276 | } 277 | 278 | return 0; 279 | #endif 280 | } 281 | 282 | int ptrace_continue(pid_t pid) 283 | { 284 | if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) { 285 | perror("ptrace_cont"); 286 | return -1; 287 | } 288 | 289 | return 0; 290 | } 291 | 292 | int ptrace_attach(pid_t pid) 293 | { 294 | if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) { 295 | perror("ptrace_attach"); 296 | return -1; 297 | } 298 | 299 | int status = 0; 300 | waitpid(pid, &status , WUNTRACED); 301 | 302 | return 0; 303 | } 304 | 305 | int ptrace_detach(pid_t pid) 306 | { 307 | if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) { 308 | perror("ptrace_detach"); 309 | return -1; 310 | } 311 | 312 | return 0; 313 | } 314 | 315 | 316 | //显然,这里面核心的就是get_module_base函数: 317 | /* 318 | 此函数的功能就是通过遍历/proc/pid/maps文件,来找到目的module_name的内存映射起始地址。 319 | 由于内存地址的表达方式是startAddrxxxxxxx-endAddrxxxxxxx的,所以会在后面使用strtok(line,"-")来分割字符串 320 | 如果pid = -1,表示获取本地进程的某个模块的地址, 321 | 否则就是pid进程的某个模块的地址。 322 | */ 323 | 324 | void* get_module_base(pid_t pid, const char* module_name) 325 | { 326 | FILE *fp; 327 | long addr = 0; 328 | char *pch; 329 | char filename[32]; 330 | char line[1024]; 331 | 332 | if (pid < 0) { 333 | /* self process */ 334 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid); 335 | } else { 336 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 337 | } 338 | 339 | fp = fopen(filename, "r"); 340 | 341 | if (fp != NULL) { 342 | while (fgets(line, sizeof(line), fp)) { 343 | if (strstr(line, module_name)) { 344 | //分解字符串为一组字符串。line为要分解的字符串,"-"为分隔符字符串。 345 | pch = strtok( line, "-" ); 346 | //将参数pch字符串根据参数base(表示进制)来转换成无符号的长整型数  347 | addr = strtoull( pch, NULL, 16 ); 348 | 349 | if (addr == 0x8000) 350 | addr = 0; 351 | 352 | break; 353 | } 354 | } 355 | 356 | fclose(fp) ; 357 | } 358 | 359 | return (void *)addr; 360 | } 361 | 362 | /* 363 | 该函数为一个封装函数,通过调用get_module_base函数来获取目的进程的某个模块的起始地址,然后通过公式计算出指定函数在目的进程的起始地址。 364 | */ 365 | void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr) 366 | { 367 | void* local_handle, *remote_handle; 368 | 369 | //获取本地某个模块的起始地址 370 | local_handle = get_module_base(-1, module_name); 371 | //获取远程pid的某个模块的起始地址 372 | remote_handle = get_module_base(target_pid, module_name); 373 | 374 | DEBUG_PRINT("[+] get_remote_addr: local[%llx], remote[%llx]\n", local_handle, remote_handle); 375 | /*这需要我们好好理解:local_addr - local_handle的值为指定函数(如mmap)在该模块中的偏移量,然后再加上rempte_handle,结果就为指定函数在目的进程的虚拟地址*/ 376 | void * ret_addr = (void *)((uintptr_t)local_addr + (uintptr_t)remote_handle - (uintptr_t)local_handle); 377 | 378 | #if defined(__i386__) 379 | if (!strcmp(module_name, libc_path)) { 380 | ret_addr += 2; 381 | } 382 | #endif 383 | return ret_addr; 384 | } 385 | 386 | //根据name找到pid 387 | int find_pid_of(const char *process_name) 388 | { 389 | int id; 390 | pid_t pid = -1; 391 | DIR* dir; 392 | FILE *fp; 393 | char filename[32]; 394 | char cmdline[256]; 395 | 396 | struct dirent * entry; 397 | 398 | if (process_name == NULL) 399 | return -1; 400 | 401 | dir = opendir("/proc"); 402 | if (dir == NULL) 403 | return -1; 404 | 405 | while((entry = readdir(dir)) != NULL) { 406 | id = atoi(entry->d_name); 407 | if (id != 0) { 408 | sprintf(filename, "/proc/%d/cmdline", id); 409 | fp = fopen(filename, "r"); 410 | if (fp) { 411 | fgets(cmdline, sizeof(cmdline), fp); 412 | fclose(fp); 413 | 414 | if (strcmp(process_name, cmdline) == 0) { 415 | /* process found */ 416 | pid = id; 417 | break; 418 | } 419 | } 420 | } 421 | } 422 | 423 | closedir(dir); 424 | return pid; 425 | } 426 | 427 | uint64_t ptrace_retval(struct pt_regs * regs) 428 | { 429 | #if defined(__arm__) || defined(__aarch64__) 430 | return regs->ARM_r0; 431 | #elif defined(__i386__) 432 | return regs->eax; 433 | #else 434 | #error "Not supported" 435 | #endif 436 | } 437 | 438 | uint64_t ptrace_ip(struct pt_regs * regs) 439 | { 440 | #if defined(__arm__) || defined(__aarch64__) 441 | return regs->ARM_pc; 442 | #elif defined(__i386__) 443 | return regs->eip; 444 | #else 445 | #error "Not supported" 446 | #endif 447 | } 448 | 449 | //总结一下ptrace_call_wrapper,它的完成两个功能: 450 | //一是调用ptrace_call函数来执行指定函数,执行完后将子进程挂起; 451 | //二是调用ptrace_getregs函数获取所有寄存器的值,主要是为了获取r0即函数的返回值。 452 | int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs) 453 | { 454 | DEBUG_PRINT("[+] Calling %s in target process.\n", func_name); 455 | if (ptrace_call(target_pid, (uintptr_t)func_addr, parameters, param_num, regs) == -1) 456 | return -1; 457 | 458 | if (ptrace_getregs(target_pid, regs) == -1) 459 | return -1; 460 | DEBUG_PRINT("[+] Target process returned from %s, return value=%llx, pc=%llx \n", 461 | func_name, ptrace_retval(regs), ptrace_ip(regs)); 462 | return 0; 463 | } 464 | 465 | //远程注入 466 | int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size) 467 | { 468 | int ret = -1; 469 | void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; 470 | void *local_handle, *remote_handle, *dlhandle; 471 | uint8_t *map_base = 0; 472 | uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; 473 | 474 | struct pt_regs regs, original_regs; 475 | long parameters[10]; 476 | 477 | DEBUG_PRINT("[+] Injecting process: %d\n", target_pid); 478 | 479 | //①ATTATCH,指定目标进程,开始调试 480 | if (ptrace_attach(target_pid) == -1) 481 | goto exit; 482 | 483 | //②GETREGS,获取目标进程的寄存器,保存现场 484 | if (ptrace_getregs(target_pid, ®s) == -1) 485 | goto exit; 486 | 487 | /* save original registers */ 488 | memcpy(&original_regs, ®s, sizeof(regs)); 489 | 490 | //③通过get_remote_addr函数获取目的进程的mmap函数的地址,以便为libxxx.so分配内存 491 | 492 | /* 493 | 需要对(void*)mmap进行说明:这是取得inject本身进程的mmap函数的地址,由于mmap函数在libc.so  494 | 库中,为了将libxxx.so加载到目的进程中,就需要使用目的进程的mmap函数,所以需要查找到libc.so库在目的进程的起始地址。 495 | */ 496 | mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap); 497 | DEBUG_PRINT("[+] Remote mmap address: %llx\n", mmap_addr); 498 | 499 | /* call mmap (null, 0x4000, PROT_READ | PROT_WRITE | PROT_EXEC, 500 |                          MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 501 | 匿名申请一块0x4000大小的内存 502 | */ 503 | parameters[0] = 0; // addr 504 | parameters[1] = 0x4000; // size 505 | parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot 506 | parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags 507 | parameters[4] = 0; //fd 508 | parameters[5] = 0; //offset 509 | 510 | if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1) 511 | goto exit; 512 | 513 | //⑤从寄存器中获取mmap函数的返回值,即申请的内存首地址: 514 | map_base = ptrace_retval(®s); 515 | 516 | //⑥依次获取linker中dlopen、dlsym、dlclose、dlerror函数的地址: 517 | dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen ); 518 | dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym ); 519 | dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose ); 520 | dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror ); 521 | 522 | DEBUG_PRINT("[+] Get imports: dlopen: %llx, dlsym: %llx, dlclose: %llx, dlerror: %llx\n", 523 | dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); 524 | 525 | printf("library path = %s\n", library_path); 526 | //⑦调用dlopen函数: 527 | /* 528 | ①将要注入的so名写入前面mmap出来的内存 529 | ②写入dlopen代码 530 | ③执行dlopen("libxxx.so", RTLD_NOW ! RTLD_GLOBAL) 531 | RTLD_NOW之类的参数作用可参考: 532 | http://baike.baidu.com/view/2907309.htm?fr=aladdin 533 | ④取得dlopen的返回值,存放在sohandle变量中 534 | */ 535 | ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1); 536 | 537 | parameters[0] = map_base; 538 | parameters[1] = RTLD_NOW| RTLD_GLOBAL; 539 | 540 | if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1) 541 | goto exit_with_error; 542 | 543 | void * sohandle = ptrace_retval(®s); 544 | if(!sohandle) { 545 | if (ptrace_call_wrapper(target_pid, "dlerror", dlerror_addr, 0, 0, ®s) != -1) 546 | { 547 | uint8_t *errret = ptrace_retval(®s); 548 | uint8_t errbuf[250]; 549 | ptrace_readdata(target_pid, errret, errbuf, 250); 550 | printf("%s\n", errbuf); 551 | } 552 | goto exit_with_error; 553 | } 554 | 555 | //⑧调用dlsym函数 556 | /* 557 | 等同于hook_entry_addr = (void *)dlsym(sohandle, "hook_entry"); 558 | */ 559 | #define FUNCTION_NAME_ADDR_OFFSET 0x100 560 | ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1); 561 | parameters[0] = sohandle; 562 | parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET; 563 | 564 | if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1) 565 | goto exit_with_error; 566 | 567 | void * hook_entry_addr = ptrace_retval(®s); 568 | DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr); 569 | 570 | //⑨调用hook_entry函数: 571 | #define FUNCTION_PARAM_ADDR_OFFSET 0x200 572 | ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1); 573 | parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET; 574 | 575 | if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1) 576 | goto exit_with_error; 577 | 578 | /* 579 | printf("Press enter to dlclose and detach\n"); 580 | getchar(); 581 | */ 582 | 583 | parameters[0] = sohandle; 584 | 585 | //⑩调用dlclose关闭lib: 586 | if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1) 587 | goto exit_with_error; 588 | 589 | exit_with_error: 590 | /* restore */ 591 | //⑪恢复现场并退出ptrace: 592 | ptrace_setregs(target_pid, &original_regs); 593 | ptrace_detach(target_pid); 594 | ret = 0; 595 | 596 | exit: 597 | return ret; 598 | } 599 | 600 | void print_help(char * self) 601 | { 602 | printf("SO INJECT for Android API 23\n"); 603 | printf("Usage:\n\t%s [hook_entry] [parameter]", self); 604 | printf("\nOptimized by H.K.T\n"); 605 | } 606 | 607 | int main(int argc, char** argv) { 608 | pid_t target_pid; 609 | char szBuf[256]; 610 | char pathBuf[256]; 611 | char paraBuf[256]; // = "I'm parameter!"; 612 | char entryBuf[256]; // = "hook_entry"; 613 | char* lpFlag = NULL; 614 | 615 | if (argc < 3) 616 | { 617 | print_help(argv[0]); 618 | return 0; 619 | } 620 | else if(argc == 3) 621 | { 622 | strcpy(entryBuf, "hook_entry"); 623 | strcpy(paraBuf, "I'm parameter!"); 624 | } 625 | else if (argc == 4) 626 | { 627 | strcpy(entryBuf, argv[3]); 628 | } 629 | else 630 | { 631 | strcpy(entryBuf, argv[3]); 632 | strcpy(paraBuf, argv[4]); 633 | } 634 | 635 | strcpy(szBuf, argv[1]); 636 | strcpy(pathBuf, argv[2]); 637 | lpFlag = strstr(szBuf, "#"); 638 | 639 | if (lpFlag != NULL) 640 | { 641 | *lpFlag = '\0'; 642 | } 643 | 644 | target_pid = find_pid_of(szBuf); 645 | 646 | if (-1 == target_pid) { 647 | printf("Can't find the process\n"); 648 | return -1; 649 | } 650 | 651 | printf("pid : %d\npath : %s\nentry: %s\npara : %s\n", target_pid, pathBuf, entryBuf, paraBuf); 652 | inject_remote_process(target_pid, pathBuf, entryBuf, paraBuf, strlen(paraBuf)); 653 | return 0; 654 | } 655 | --------------------------------------------------------------------------------