现在的位置: 首页 > 综合 > 正文

ptrace 进程注入

2013年04月24日 ⁄ 综合 ⁄ 共 4488字 ⁄ 字号 评论关闭

1. 简介

    使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行进程(以下简称为:目标进程)空间中,从而.so中的函数在目标进程空间中有对应的地址,然后通过此地址便可在目标进程中进行调用。

     到底是如何注入的呢?

     本文实现方案为:在目标进程中,通过dlopen把需要注入的.so加载到目标进程的空间中

2. 如何让目标进程执行dlopen加载.so?

     显然,目标进程本来是没有实现通过dlopen来加载我们想注入的.so,为了实现此功能,我们需要目标进程执行一段我们实现的代码,此段代码的功能为通过dlopen来加载一个.so。

3. 【加载.so的实现代码】

    加载需要注入的.so的实现代码如下所示:     

  1. .global _dlopen_addr_s       @dlopen函数在目标进程中的地址     注:以下全局变化在C中可读写  
  2. .global _dlopen_param1_s     @dlopen参数1<.so>在目标进程中的地址   
  3. .global _dlopen_param2_s     @dlopen参数2在目标进程中的地址  
  4.   
  5. .global _dlsym_addr_s        @dlsym函数在目标进程中的地址  
  6. .global _dlsym_param2_s      @dlsym参数2在目标进程中的地址,其实为函数名  
  7.   
  8. .global _dlclose_addr_s      @dlcose在目标进程中的地址  
  9.   
  10. .global _inject_start_s      @汇编代码段的起始地址  
  11. .global _inject_end_s        @汇编代码段的结束地址  
  12.   
  13. .global _inject_function_param_s  @hook_init参数在目标进程中的地址  
  14.   
  15. .global _saved_cpsr_s        @保存CPSR,以便执行完hook_init之后恢复环境  
  16. .global _saved_r0_pc_s       @保存r0-r15,以便执行完hook_init之后恢复环境  
  17.   
  18.   
  19. .data  
  20.   
  21. _inject_start_s:  
  22.     @ debug loop  
  23. 3:  
  24.     @sub r1, r1, #0  
  25.     @B 3b  
  26.   
  27.     @ dlopen  
  28.     ldr r1, _dlopen_param2_s        @设置dlopen第二个参数, flag  
  29.     ldr r0, _dlopen_param1_s        @设置dlopen第一个参数 .so  
  30.     ldr r3, _dlopen_addr_s          @设置dlopen函数  
  31.     blx r3                          @执行dlopen函数,返回值位于r0中  
  32.     subs r4, r0, #0                 @把dlopen的返回值soinfo保存在r4中,以方便后面dlclose使用  
  33.     beq 2f  
  34.   
  35.     @dlsym  
  36.     ldr r1, _dlsym_param2_s        @设置dlsym第二个参数,第一个参数已经在r0中了  
  37.     ldr r3, _dlsym_addr_s          @设置dlsym函数  
  38.     blx r3                         @执行dlsym函数,返回值位于r0中  
  39.     subs r3, r0, #0                @把返回值<hook_init在目标进程中的地址>保存在r3中  
  40.     beq 1f  
  41.   
  42.     @call our function  
  43.     ldr r0, _inject_function_param_s  @设置hook_init第一个参数  
  44.         blx r3                            @执行hook_init  
  45.     subs r0, r0, #0  
  46.     beq 2f  
  47.   
  48. 1:  
  49.     @dlclose                          
  50.     mov r0, r4                        @把dlopen的返回值设为dlcose的第一个参数  
  51.     ldr r3, _dlclose_addr_s           @设置dlclose函数  
  52.     blx r3                            @执行dlclose函数  
  53.   
  54. 2:  
  55.     @restore context  
  56.     ldr r1, _saved_cpsr_s             @恢复CPSR  
  57.     msr cpsr_cf, r1  
  58.     ldr sp, _saved_r0_pc_s            @恢复寄存器r0-r15  
  59.     ldmfd sp, {r0-pc}  
  60.       
  61.   
  62.       
  63.   
  64. _dlopen_addr_s:                           @初始化_dlopen_addr_s  
  65. .word 0x11111111  
  66.   
  67. _dlopen_param1_s:  
  68. .word 0x11111111  
  69.   
  70. _dlopen_param2_s:  
  71. .word 0x2                                 @RTLD_GLOBAL  
  72.   
  73. _dlsym_addr_s:  
  74. .word 0x11111111  
  75.   
  76. _dlsym_param2_s:  
  77. .word 0x11111111  
  78.   
  79. _dlclose_addr_s:  
  80. .word 0x11111111  
  81.   
  82. _inject_function_param_s:  
  83. .word 0x11111111  
  84.   
  85. _saved_cpsr_s:  
  86. .word 0x11111111  
  87.   
  88. _saved_r0_pc_s:  
  89. .word 0x11111111  
  90.   
  91.   
  92. _inject_end_s:                     @代码结束地址  
  93.   
  94. .space 0x400, 0                    @代码段空间大小  
  95.   
  96. .end  



4. 如何把【加载.so的实现代码】写入目标进程并启动执行?

   为了把【加载.so的实现代码】写入目标进程,主要有以下两步操作:

   1) 在目标进程中找到存放【加载.so的实现代码】的空间(通过mmap实现)

   2) 把【加载.so的实现代码】写入目标进程指定的空间

   3) 启动执行

4.1 在目标进程中找到存放【加载.so的实现代码】的空间

    通过mmap来实现,其实现步骤如下:

   1) 获取目标进程中mmap地址
   2) 把mmap参数据放入r0-r3,另外两个写入目标进程sp 
   3) pc设置为mmap地址,lr设置为0
   4) 把准备好的寄存器写入目标进程(PTRACE_SETREGS),并启动目标进程运行(PTRACE_CONT)
   5) 分配的内存首地址位于r0 (PTRACE_GETREGS)

4.2 为【加载.so的实现代码】中的全局变量赋值

   1) 获取目标进程中dlopen地址并赋值给_dlopen_addr_s

   2) 获取目标进程中dlsym地址并赋值给_dlsym_addr_s

   3) 获取目标进程中dlclose地址并赋值给_dlclose_addr_s

   4) 把需要加载的.so的路径放入 汇编代码中,并获取此路径在目标进程中的地址然后赋值给_dlopen_param1_s

   5) 把需要加载的.so中的hook_init放入 汇编代码中,并获取此路径在目标进程中的地址然后赋值给_dlsym_param2_s

   6) 把目标进程中的cpsr保存在_saved_cpsr_s中

   7) 把目标进程中的r0-r15存入汇编代码中,并获取此变量在目标进程中的地址然后赋值给_saved_r0_pc_s

   8) 通过ptrace( PTRACE_POKETEXT,...)把汇编代码写入目标进程中,起始地址由前面的mmap所分配

   9) 把目标进程的pc设置为汇编代码的起始地址,然后调用ptrace(PTRACE_DETACH,...)以启动目标进程执行

5. 把汇编代码写入目标进程并执行的实现代码

5.1 主函数 writecode_to_targetproc

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <asm/ptrace.h>  
  4. #include <asm/user.h>  
  5. #include <asm/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 <android/log.h>  
  13. #include <sys/types.h>  
  14. #include <sys/socket.h>  
  15. #include <netinet/in.h>  
  16. #include <sys/stat.h>  
  17.   
  18. #define MAX_PATH 0x100  
  19. #define REMOTE_ADDR( addr, local_base, remote_base ) ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) )  
  20.   
  21. /* write the assembler code into target proc, 
  22.  * and invoke it to execute 
  23.  */  
  24. int writecode_to_targetproc(   
  25.     pid_t target_pid, // target process pid  
  26.     const char *library_path, // the path of .so that will be   
  27.                               // upload to target process   
  28.     const char *function_name, // .so init fucntion e.g. hook_init

抱歉!评论已关闭.