对于Android for arm上的so注入(inject)和挂钩(hook),网上已有牛人给出了代码-libinject(http://bbs.pediy.com/showthread.php?t=141355)。由于实现中的ptrace函数是依赖于平台的,所以不经改动只能用于arm平台。本文将之扩展了一下,使它能够通用于Android的x86和arm平台。Arm平台部分基本重用了libinject中的代码,其中因为汇编不好移植且容易出错,所以把shellcode.s用ptrace_call替换掉了,另外保留了mmap,用来传字符串参数,当然也可以通过栈来传,但栈里和其它东西混一起,一弄不好就会隔儿了,所以还是保险点好。最后注意设备要root。

首先创建目录及文件:

jni
    inject.c
    Android.mk
    Application.mk

inject.c:

#include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include     #if defined(__i386__)  #define pt_regs         user_regs_struct  #endif    #define ENABLE_DEBUG 1    #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    #define CPSR_T_MASK     ( 1u << 5 )    const char *libc_path = "/system/lib/libc.so";  const char *linker_path = "/system/bin/linker";    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;  }    #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) {          regs->ARM_sp -= (num_params - i) * sizeof(long) ;          ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long));      }        regs->ARM_pc = addr;      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;            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;  }    #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;  }    int ptrace_attach(pid_t pid)  {      if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {          perror("ptrace_attach");          return -1;      }        int status = 0;      waitpid(pid, &status , 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);        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;  }    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)          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 *function_name, const char *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 = 0;      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);        if (ptrace_attach(target_pid) == -1)          goto exit;        if (ptrace_getregs(target_pid, ®s) == -1)          goto exit;        /* save original registers */      memcpy(&original_regs, ®s, sizeof(regs));        mmap_addr = get_remote_addr(target_pid, libc_path, (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        if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1)          goto exit;        map_base = ptrace_retval(®s);        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 );      dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)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)          goto exit;        void * sohandle = ptrace_retval(®s);    #define FUNCTION_NAME_ADDR_OFFSET       0x100      ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1);      parameters[0] = sohandle;         parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET;         if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1)          goto exit;        void * hook_entry_addr = ptrace_retval(®s);      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);      parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET;        if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1)          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)          goto exit;        /* restore */      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");      if (-1 == target_pid) {        printf("Can't find the process\n");        return -1;    }    //target_pid = find_pid_of("/data/test");      inject_remote_process(target_pid, "/data/libhello.so", "hook_entry",  "I'm parameter!", strlen("I'm parameter!"));      return 0;}  
注意上面的/system/bin/surfaceflinger进程我随手写的,你的设备上不一定有。没有的话挑其它的也行,前提是ps命令里能找到。

Android.mk:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := inject LOCAL_SRC_FILES := inject.c #shellcode.sLOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog#LOCAL_FORCE_STATIC_EXECUTABLE := trueinclude $(BUILD_EXECUTABLE)
Application.mk:
APP_ABI := x86 armeabi-v7a

运行nkd-build编译成生x86和arm平台下的可执行文件:

jzj@jzj-laptop:~/workspace/inject_hook/inject_jni$ ndk-buildInstall        : inject => libs/x86/injectInstall        : inject => libs/armeabi-v7a/inject

再来生成要注入的so,创建目录及文件:

jni
    hello.c
    Android.mk
    Application.mk

hello.c:

#include #include #include #include #include #include #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 := armLOCAL_MODULE    := helloLOCAL_SRC_FILES := hello.cinclude $(BUILD_SHARED_LIBRARY)

Application.mk:
APP_ABI := x86 armeabi-v7a
运行nkd-build编译成生x86和arm平台下的so:
jzj@jzj-laptop:~/workspace/inject_hook/hook_so_simple$ ndk-build Install        : libhello.so => libs/x86/libhello.soInstall        : libhello.so => libs/armeabi-v7a/libhello.so

然后就可以跑起来试试了,连接root过的Android设备或者打开模拟器。将inject和libhello.so拷入设备,设执行权限,执行:


先看看被注入进程(surfaceflinger)的mmap,可以看到我们的so已经被加载了,紧接着的那一块就是我们mmap出来的:

从logcat中也可以看到so注入成功,并且以被注入进程的身份执行了so中的代码:


简单的注入成功,现在我们再来做一个实验,就是应用这套机制来截获surfaceflinger中的eglSwapBuffers调用,然后用我们自己的函数来替换掉原来的eglSwapBuffers调用。关于截系统中的函数调用网上有例子http://bbs.pediy.com/showthread.php?t=157419,这里依葫芦画瓢。首先将hello.c改下:

#include #include #include #include #include #include #include #include #include #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\n");    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) {        /* 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;}#define LIBSF_PATH  "/system/lib/libsurfaceflinger.so"  int hook_eglSwapBuffers()  {      old_eglSwapBuffers = eglSwapBuffers;      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 (-1 == fd) {          LOGD("error\n");          return -1;      }      Elf32_Ehdr ehdr;      read(fd, &ehdr, sizeof(Elf32_Ehdr));      unsigned long shdr_addr = ehdr.e_shoff;        int shnum = ehdr.e_shnum;        int shent_size = ehdr.e_shentsize;        unsigned long stridx = ehdr.e_shstrndx;        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);        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++) {            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;                    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;                        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\n");    LOGD("Start hooking\n");    hook_eglSwapBuffers();      return 0;}

其实这种查找方法有点简单粗暴,要是正式应用的话可以在动态符号表中查找这个符号的got地址。

接着Android.mk改为:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog -lEGL#LOCAL_ARM_MODE := armLOCAL_MODULE    := helloLOCAL_SRC_FILES := hello.cinclude $(BUILD_SHARED_LIBRARY)

Application.mk :

APP_ABI := x86 armeabi-v7a  APP_PLATFORM := android-14

运行ndk-build编译:

jzj@jzj-laptop:~/workspace/inject_hook/hook_so$ ndk-build Install        : libhello.so => libs/x86/libhello.soInstall        : libhello.so => libs/armeabi-v7a/libhello.so

和上面一样运行,查看logcat,我们可以看到surfaceflinger中调用eglSwapBuffers的地址已被替换成我们的版本:

D/INJECT  ( 2231): [+] Injecting process: 1728D/INJECT  ( 2231): [+] get_remote_addr: local[b7e4f000], remote[b7e73000]D/INJECT  ( 2231): [+] Remote mmap address: b7e9fe32D/INJECT  ( 2231): [+] Calling mmap in target process.D/INJECT  ( 2231): [+] Target process returned from mmap, return value=b4f1c000, pc=0 D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]D/INJECT  ( 2231): [+] get_remote_addr: local[b7f00000], remote[b7f7e000]D/INJECT  ( 2231): [+] Get imports: dlopen: b7f84f50, dlsym: b7f84fd0, dlclose: b7f84de0, dlerror: b7f84dc0D/INJECT  ( 2231): [+] Calling dlopen in target process.D/INJECT  ( 2231): [+] Target process returned from dlopen, return value=b7f995ec, pc=0 D/INJECT  ( 2231): [+] Calling dlsym in target process.D/INJECT  ( 2231): [+] Target process returned from dlsym, return value=b4f17e10, pc=0 D/INJECT  ( 2231): hook_entry_addr = 0xb4f17e10D/DEBUG   ( 1728): Hook successD/DEBUG   ( 1728): Start hookingD/DEBUG   ( 1728): Orig eglSwapBuffers = 0xb7d4a9c0D/DEBUG   ( 1728): libsurfaceflinger.so address = 0xb7f22000D/DEBUG   ( 1728): out_addr = b7f7aff4, out_size = 624D/DEBUG   ( 1728): Found eglSwapBuffers in got...D/DEBUG   ( 1728): New eglSwapBuffersD/DEBUG   ( 1728): New eglSwapBuffersD/DEBUG   ( 1728): New eglSwapBuffers...



 

更多相关文章

  1. C语言函数的递归(上)
  2. 开发前奏曲之添加Android(安卓)SDK平台工具
  3. Android(安卓)Volley 简单分析
  4. 转:解开Android应用程序组件Activity的”singleTask”之谜
  5. ADB 自制android万用驱动方法,解决找不到驱动问题
  6. COCOS2D-X跨ANDROID&IOS平台开发入门教程
  7. Android深入浅出之Surface[1]
  8. 配置设备应用于开发
  9. Android中trim()函数的故事

随机推荐

  1. Android开发——控件基础(五)RadioButton组
  2. android中的六大布局
  3. Android腾讯微薄客户端开发教程汇总
  4. android 关于google刘海屏的解决方案
  5. layout_gravity与android:gravity有什么
  6. android各个文件分析
  7. android:versionCode和android:versionNa
  8. Android跨进程通信IPC之13——Binder总结
  9. Android(安卓)SDK中tools详解
  10. android中的六大布局