zoukankan      html  css  js  c++  java
  • 使用ptrace向已运行进程中注入.so并执行相关函数(转)

    1. 简介

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

         到底是如何注入的呢?

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

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

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

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

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

    .global _dlopen_addr_s       @dlopen函数在目标进程中的地址     注:以下全局变化在C中可读写
    .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      @dlcose在目标进程中的地址
    
    .global _inject_start_s      @汇编代码段的起始地址
    .global _inject_end_s        @汇编代码段的结束地址
    
    .global _inject_function_param_s  @hook_init参数在目标进程中的地址
    
    .global _saved_cpsr_s        @保存CPSR,以便执行完hook_init之后恢复环境
    .global _saved_r0_pc_s       @保存r0-r15,以便执行完hook_init之后恢复环境
    
    
    .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函数,返回值位于r0中
        subs r4, r0, #0                 @把dlopen的返回值soinfo保存在r4中,以方便后面dlclose使用
        beq    2f
    
        @dlsym
        ldr r1, _dlsym_param2_s        @设置dlsym第二个参数,第一个参数已经在r0中了
        ldr r3, _dlsym_addr_s          @设置dlsym函数
        blx r3                         @执行dlsym函数,返回值位于r0中
        subs r3, r0, #0                @把返回值<hook_init在目标进程中的地址>保存在r3中
        beq 1f
    
        @call our function
        ldr r0, _inject_function_param_s  @设置hook_init第一个参数
            blx r3                            @执行hook_init
        subs r0, r0, #0
        beq 2f
    
    1:
        @dlclose                        
        mov r0, r4                        @把dlopen的返回值设为dlcose的第一个参数
        ldr r3, _dlclose_addr_s           @设置dlclose函数
        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:                           @初始化_dlopen_addr_s
    .word 0x11111111
    
    _dlopen_param1_s:
    .word 0x11111111
    
    _dlopen_param2_s:
    .word 0x2                                 @RTLD_GLOBAL
    
    _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                    @代码段空间大小
    
    .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

    #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 <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/stat.h>
    
    #define MAX_PATH 0x100
    #define REMOTE_ADDR( addr, local_base, remote_base ) ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) )
    
    /* write the assembler code into target proc,
     * and invoke it to execute
     */
    int writecode_to_targetproc( 
        pid_t target_pid, // target process pid
        const char *library_path, // the path of .so that will be 
                                  // upload to target process 
        const char *function_name, // .so init fucntion e.g. hook_init
        void *param, // the parameters of init function
        size_t param_size ) // number of parameters 
    {
        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;
    
        // extern global variable in the assembler code 
        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];
    
        // make target_pid as its child process and stop
        if ( ptrace_attach( target_pid ) == -1 )
            return -1;
    
        // get the values of 18 registers from target_pid
        if ( ptrace_getregs( target_pid, ®s ) == -1 )
            goto exit;
    
        // save original registers 
        memcpy( &original_regs, ®s, sizeof(regs) );
    
        // get mmap address from target_pid
        // the mmap is the address of mmap in the cur process
        mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap );
    
        // set mmap parameters
        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
    
        // execute the mmap in target_pid
        if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, ®s ) == -1 )
            goto exit;
    
        // get the return values of mmap <in r0>
        if ( ptrace_getregs( target_pid, ®s ) == -1 )
            goto exit;
    
        // get the start address for assembler code
        map_base = (uint8_t *)regs.ARM_r0;
    
        // get the address of dlopen, dlsym and dlclose in target process
        dlopen_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlopen );
        dlsym_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlsym );
        dlclose_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlclose );
    
        // set the start address for assembler code in target process
        remote_code_ptr = map_base + 0x3C00;
    
        // set the start address for assembler code in cur process
        local_code_ptr = (uint8_t *)&_inject_start_s;
    
        // set global variable of assembler code
        // and these address is in the target process
        _dlopen_addr_s = (uint32_t)dlopen_addr;
        _dlsym_addr_s = (uint32_t)dlsym_addr;
        _dlclose_addr_s = (uint32_t)dlclose_addr;
    
        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;
    
    
        // save library path to assembler code global variable
        strcpy( dlopen_param1_ptr, library_path );
        _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr );
        
    
        // save function name to assembler code global variable
        strcpy( dlsym_param2_ptr, function_name );
        _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr );
    
        // save cpsr to assembler code global variable
        _saved_cpsr_s = original_regs.ARM_cpsr;
    
        // save r0-r15 to assembler code global variable
        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 );
    
        // save function parameters to assembler code global variable
        memcpy( inject_param_ptr, param, param_size );
        _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr );
    
        // write the assembler code into target process
        // now the values of global variable is in the target process space
        ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 );
    
        memcpy( ®s, &original_regs, sizeof(regs) );
    
        // set sp and pc to the start address of assembler code
        regs.ARM_sp = (long)remote_code_ptr;
        regs.ARM_pc = (long)remote_code_ptr;
    
        // set registers for target process
        ptrace_setregs( target_pid, ®s );
    
        // make the target_pid is not a child process of cur process
        // and make target_pid continue to running
        ptrace_detach( target_pid );
    
        // now finish it successfully
        ret = 0;
    
    exit:
        return ret;
    }

    5.2 attach目标进程ptrace_attach

    int ptrace_attach( pid_t pid )
    {
        // after PTRACE_ATTACH, the proc<pid> will stop
        if ( ptrace( PTRACE_ATTACH, pid, NULL, 0  ) < 0 )
        {
            perror( "ptrace_attach" );
            return -1;
        }
    
        // wait proc<pid> stop
        waitpid( pid, NULL, WUNTRACED );
    
        // after PTRACE_SYSCALL, the proc<pid> will continue,
        // but when exectue sys call function, proc<pid> will stop
        if ( ptrace( PTRACE_SYSCALL, pid, NULL, 0  ) < 0 )
        {
            perror( "ptrace_syscall" );
            return -1;
        }
    
        // wait proc<pid> stop
        waitpid( pid, NULL, WUNTRACED );
    
        return 0;
    }

    5.3 获取目标进程寄存器值ptrace_getregs

    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;
    }

    5.4 获取目标进程中指定模块中指定函数的地址get_remote_addr

     
    /* find the start address of module whose name is module_name 
     * in the designated process
     */
    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 );
    
        return (void *)( (uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle );
    }

    5.5 在目标进程中执行指定函数ptrace_call

    int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs )
    {
        uint32_t i;
    
        // put the first 4 parameters into r0-r3
        for ( i = 0; i < num_params && i < 4; i ++ )
        {
            regs->uregs[i] = params[i];
        }
    
        // push remained params into 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) );
        }
        // set the pc to func <e.g: mmap> that will be executed
        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;
        }
    
        // when finish this func, pid will stop
        regs->ARM_lr = 0;    
    
        // set the regsister and start to execute
        if ( ptrace_setregs( pid, regs ) == -1 
            || ptrace_continue( pid ) == -1 )
        {
            return -1;
        }
    
        // wait pid finish work and stop
        waitpid( pid, NULL, WUNTRACED );
    
        return 0;
    }

    5.6 把代码写入目标进程指定地址ptrace_writedata

    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;
    }

    5.7 设置目标进程寄存器ptrace_setregs

    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;
    }

    5.8 detach目标进程ptrace_detach

    int ptrace_detach( pid_t pid )
    {
        if ( ptrace( PTRACE_DETACH, pid, NULL, 0 ) < 0 )
        {
            perror( "ptrace_detach" );
            return -1;
        }
    
        return 0;
    }

    6.  需要被加载的.so

        需要被加载的.so例子程序如下,其目的是替换目标进程libapp.so中的strlen函数。其主要实现见hook_init。

    int g_isInit = 0;    
    pthread_t g_hThread;     
    
    __attribute__((visibility("default"))) void hook_init( char *args )
    {
       if( g_isInit == 1 )
       {
          printf("i am already in!");
          return;
       }
    
       void* soHandle = NULL;
       
       // the libapp.so is a .so of target process, and it call strcmp
       soHandle  = dlopen( "libapp.so", RTLD_GLOBAL );
       if( soHandle != NULL )
       {
          g_realstrcmp = NULL;
          replaceFunc( soHandle, "strcmp", my_strcmp, (void**)&g_realstrcmp );
          
          int ret = pthread_create( &g_hThread, NULL, my_thread, NULL );
          if( ret != 0 )
          {
             printf("create thread error:%d", ret );
          }
          
          g_isInit = 1;
       }
       
    }

    6.1 替换函数replaceFunc

    // replace function of libapp.so
    // e.g: replace strcmp of libapp.so with my_strcmp
    void replaceFunc(void *handle,const char *name, void* pNewFun, void** pOldFun )
    {
    
       if(!handle)
          return;
          
       soinfo *si = (soinfo*)handle;   
       Elf32_Sym *symtab = si->symtab;  
       const char *strtab = si->strtab;  
       Elf32_Rel *rel = si->plt_rel;
       unsigned count = si->plt_rel_count; 
       unsigned idx; 
    
       // these external functions that are called by libapp.so 
       // is in the plt_rel
       for(idx=0; idx<count; idx++) 
       {  
          unsigned type = ELF32_R_TYPE(rel->r_info);  
          unsigned sym = ELF32_R_SYM(rel->r_info);  
          unsigned reloc = (unsigned)(rel->r_offset + si->base);  
          char *sym_name = (char *)(strtab + symtab[sym].st_name); 
          
          if(strcmp(sym_name, name)==0) 
          { 
             *pOldFun = (void *)*((unsigned*)reloc); 
                 *((unsigned*)reloc)= pNewFun;
             break;
          } 
          rel++;  
       } 
    }

    6.2 新函数及其它函数

    // global function variable, save the address of strcmp of libapp.so
    int (*g_realstrcmp)(const char *s1, const char *s2);
    
    // my strcmp function
    int my_strcmp(const char *s1, const char *s2)
    {
        if( g_realstrcmp != NULL )
        {
            int nRet = g_realstrcmp( s1, s2 );
            printf("***%s: s1=%s, s2=%s
    ",__FUNCTION__, s1, s2 );
            return nRet;
        }
    
        return -1;
    }
    
    
    // create a thread
    void* my_thread( void* pVoid )
    {
        int sock;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if( sock < -1 )
        {
          printf("create socket failed!
    ");
          return 0;
        }
    
        struct sockaddr_in addr_serv;  
        int len;  
        memset(&addr_serv, 0, sizeof(struct sockaddr_in));  
        addr_serv.sin_family = AF_INET;  
        addr_serv.sin_port = htons(9999);   
        addr_serv.sin_addr.s_addr = inet_addr("127.0.0.1");  
        len = sizeof(addr_serv);  
    
        int flags = fcntl( sock, F_GETFL, 0); 
        fcntl( sock, F_SETFL, flags | O_NONBLOCK);
        int nPreState = -1;
        unsigned char data=0;
        while( 1 )
        {
            data++;
            sendto( sock, &data,  sizeof( data ), 0, (struct sockaddr *)&addr_serv, sizeof( addr_serv ) );
            usleep( 30000 );
        }
    }
  • 相关阅读:
    关于生成并发唯一性流水号的解决方案
    父页面得到<iframe>
    struts2 convention配置中常见配置选项及说明
    Struts2下关于Calendar,date的一些处理
    怎样将用户名和密码保存到Cookie中?【转】
    如何调用用户控件(UserControl)的方法 .
    Struts遍历标签<s:iterator>总结 .
    在事业的开展上保持归零的心态
    这种日子最轻松,这样的人生最快乐
    诚实是人世间最珍贵的宝物,是每个人都应当坚守的伟大情操
  • 原文地址:https://www.cnblogs.com/qq78292959/p/4014243.html
Copyright © 2011-2022 走看看