android hook 框架 libinject2 如何实现so注入

Android so注入-libinject2 简介、编译、运行

Android so注入-libinject2  如何实现so注入

Android so注入-Libinject 如何实现so注入

Android so注入挂钩-Adbi 框架简介、编译、运行

Android so注入挂钩-Adbi 框架如何实现so注入

Android so注入挂钩-Adbi 框架如何实现so函数挂钩

Android so注入挂钩-Adbi 框架如何实现dalvik函数挂钩

Android dalvik挂钩-Xposed框架如何实现注入

Android dalvik挂钩-Xposed框架如何实现挂钩

上一篇 android hook 框架 libinject 简介、编译、运行 实际运行了so的注入并调用了注入so里的一个函数,这篇开始分析其实现。 

与之前分析的 abdi 项目一样,libinject2 也是依赖于linux系统的 ptrace 系统调用。

android hook 框架 ADBI 简介、编译、运行

android hook 框架 ADBI 如何实现so注入

android hook 框架 ADBI 如何实现函数挂钩

这个库首先对ptrace的调用封装了几个helper函数

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; // 当以满4字节读取内容时,直接使用 long 变量
                char chars[sizeof(long)]; // 最后不满4字节的内容,使用 char 变量
        } 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;
}
ptrace_readdata : src 对应 tracee进程指定地址,buf 对应trace进程地址, size 长度。 这个函数从目标进程src地址开始读size长度内容到本进程的buf内存里。使用 ptrace 函数,第一个参数 PTRACE_PEEKTEXT 实现从 tracee 进程读取数据。
由于 ptrace 的 peektext 时是以 32位为单位,而 size 是1字节为单位,所以先把 size 转成 4 字节为单位,依次用 ptrace 读取,最后剩下的不够 4字节的余数,依然调用一次 ptrace peektext,然后拷贝实际要的字节数到目标地址。
ptrace_readdata 在 libinject2 里并没有使用。

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;
}
ptrace_writedata 实现与 ptrace_readdata 相反的功能,将长度为 Size 字节的本进程 data 地址开始的数据,写入目标进程 Dest 开始的内存。
实际调用的是 ptrace, 第一个参数为 PTRACE_POKETEXT
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;
}

ptrace_attach 简单封装 ptrace+PTRACE_ATTACH + waitpid 的调用,ptrace_detach 简单封装 ptrace+PTRACE_DETACH的调用

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.
", 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 
",
                        func_name, ptrace_retval(regs), ptrace_ip(regs));
        return 0;
}
ptrace_call_wrapper 封装了trace进程调用tracee进程内函数的方法,tracee进程内要调用的函数地址用参数 func_addr 存放,func_addr的参数和参数个数由 parameters 和 param_num 指定。
调用完后tracee进程的寄存器内存获取并存放在 regs 变量里。

下面先了解下 struct pt_regs 结构,

这个结构封装了需要在内核入口中保存的最少的状态信息,比如说每一次的系统调用、中断、陷阱、故障时,pt_regs结构中保存了最少的状态信息,是一个数组,为了方便使用,定义了一系列寄存器宏指向数组的某一项, 使用 ptrace+PTRACE_GETREGS 可以获取目标进程的寄存器值,以 struct pt_regs 变量返回。
pt_regs结构:

/*
 * This struct defines the way the registers are stored on the
 * stack during a system call.  Note that sizeof(struct pt_regs)
 * has to be a multiple of 8.
 */
#ifndef __KERNEL__
struct pt_regs {
        long uregs[18];
};
#else /* __KERNEL__ */
struct pt_regs {
        unsigned long uregs[18];
};
#endif /* __KERNEL__ */


#define ARM_cpsr        uregs[16]
#define ARM_pc          uregs[15]
#define ARM_lr          uregs[14]
#define ARM_sp          uregs[13]
#define ARM_ip          uregs[12]
#define ARM_fp          uregs[11]
#define ARM_r10         uregs[10]
#define ARM_r9          uregs[9]
#define ARM_r8          uregs[8]
#define ARM_r7          uregs[7]
#define ARM_r6          uregs[6]
#define ARM_r5          uregs[5]
#define ARM_r4          uregs[4]
#define ARM_r3          uregs[3]
#define ARM_r2          uregs[2]
#define ARM_r1          uregs[1]
#define ARM_r0          uregs[0]
#define ARM_ORIG_r0     uregs[17]

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

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

ptrace_call 的实现如下:

#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 ++) { // 前面4个参数存放到寄存器里,pt_regs数组的 0,1,2,4 四个位置 regs->uregs[i] = params[i]; } // if (i < num_params) { //多于4个的参数,存放在目标进程的栈里

                  regs->ARM_sp -= (num_params - i) * sizeof(long) ; // 栈顶指针 ARM_sp 往低地址移动剩余参数的地址数
                  ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long));// 使用ptrace_writedata向目标进程的栈                                                                               //写入剩余参数的值

        }

        regs->ARM_pc = addr;  // 要在目标进程调用的函数地址写入目标进程PC寄存器
        if (regs->ARM_pc & 1) { // 16位的 thumb 格式
                /* thumb */
                regs->ARM_pc &= (~1u);
                regs->ARM_cpsr |= CPSR_T_MASK; // #define CPSR_T_MASK     ( 1u << 5 )
        } else { // arm 格式
                /* arm */
                regs->ARM_cpsr &= ~CPSR_T_MASK;
        }

        regs->ARM_lr = 0;

        if (ptrace_setregs(pid, regs) == -1   // 将构造好的寄存器内容写入目标进程
                        || ptrace_continue(pid) == -1) { // 恢复目标进程的运行,目标进程将从上述pc寄存器即addr函数开始运行
                printf("error
");
                return -1;
        }

        int stat = 0;
        waitpid(pid, &stat, WUNTRACED);
        while (stat != 0xb7f) {  // 这几句没看懂
                if (ptrace_continue(pid) == -1) {
                        printf("error
");
                        return -1;
                }
                waitpid(pid, &stat, WUNTRACED);
        }

        return 0;
}
#endif

下面这个函数实现了获取目标进程加载的动态库内部函数的地址,与 adbi 的原理一致,都是利用函数与动态库加载进内存的起始地址的offset一致,来计算的,个人觉得 libinject 在实现同样的功能时代码给 adbi 写得更舒服,这也是研究各种源码的好处,有对比才有高低。
void* get_module_base(pid_t pid, const char* module_name)  // 这个函数获取动态库 module_name 加载在进程 pid 后的起始地址
{
        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);  // 同样是通过解析 maps 文件得到的
        } 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]
", local_handle, remote_handle);

        void * ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);// 算法一致, local_addr - local_handle 得到                                                                                              // offset, 然后再加上 remote_handle, 即得到目标进程的函数地址

        return ret_addr;
}

以上函数基本上是helper函数,主要是封装了ptrace的调用实现一系列读写目标进程内存、寄存器的函数,并且封装了通过解析maps文件读取目标进程动态库里函数的地址的函数。

 

下面这个是 libinject2 的核心函数,实现了注入功能:

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; // 存放目标进程mmap获取的内存块的地址
        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;
        struct pt_regs original_regs;
       
        uint32_t code_length;
        long parameters[10];

        DEBUG_PRINT("[+] Injecting process: %d
", target_pid);

        if (ptrace_attach(target_pid) == -1) // 第一步: attach 到目标进程
                goto exit;

        if (ptrace_getregs(target_pid, &regs) == -1)
                goto exit;

        /* save original registers */
        memcpy(&original_regs, &regs, sizeof(regs)); // 第二步:保存目标进程被注入前的寄存器内容,方便注入完成后恢复

        mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
        DEBUG_PRINT("[+] Remote mmap address: %x
", 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, &regs) == -1)
                goto exit;

        map_base = ptrace_retval(&regs);  // 第三步,获取目标进程mmap调用的地址,并执行mmap调用,在目标进程分配一块地址,用于存放后面要注入的库路径和相关函数地址等

        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
",
                        dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);

        printf("library path = %s
", library_path);
        ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);// 第四步,获取目标进程动态库的几个函数,并将要注入的so的路径写入刚刚申请的内存的初始地址

        parameters[0] = map_base;
        parameters[1] = RTLD_NOW| RTLD_GLOBAL;

        if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)
                goto exit;

        void * sohandle = ptrace_retval(&regs); // 第五步,在目标进程内调用 dlopen函数加载要注入的 so ,这一步成功后,so已经被注入目标进程的地址空间内了

#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, &regs) == -1)
                goto exit;

        void * hook_entry_addr = ptrace_retval(&regs);
        DEBUG_PRINT("hook_entry_addr = %p
", hook_entry_addr); // 第六步,在目标进程内调用 dlsym函数获取刚刚注入的so里的hook函数

#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, &regs) == -1)
                goto exit;

        printf("Press enter to dlclose and detach
"); // 第七步,在目标进程内调用hook函数
        getchar();
        parameters[0] = sohandle;

        if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, &regs) == -1)
                goto exit;

        /* restore */
        ptrace_setregs(target_pid, &original_regs);
        ptrace_detach(target_pid); // 第八步,恢复目标进程的寄存器,detach 退出对目标进程的 ptrace
        ret = 0;

exit:
        return ret;
}

最后是main函数,libinject2 只是注入了一个So到目标进程,并执行了so里的一个函数,还没有真正劫持目标进程的函数

int main(int argc, char** argv) {
        pid_t target_pid;
        //target_pid = find_pid_of("/system/bin/surfaceflinger");
        target_pid = atoi(argv[1]);
        if (-1 == target_pid) {
                printf("Can't find the process
");
                return -1;
        }
        //target_pid = find_pid_of("/data/test");
        inject_remote_process(target_pid, "/data/local/tmp/libhello.so", "hook_entry",  "I'm parameter!", strlen("I'm parameter!"));
        return 0;
}

完整的libinject.c:

#include <stdio.h>
#include <stdlib.h>
//#include <asm/user.h>
#include <asm/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>
#include <android/log.h>

#define __arm__ 1

#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 *)&params[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
");
        return -1;
    }

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f) {
        if (ptrace_continue(pid) == -1) {
            printf("error
");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }

    return 0;
}

#elif defined(__i386__)
#if 0
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
");
        return -1;
    }

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f) {
        if (ptrace_continue(pid) == -1) {
            printf("error
");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }

    return 0;
}
#endif
#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]
", 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.
", 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 
",
            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;
    struct pt_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
", target_pid);

    if (ptrace_attach(target_pid) == -1){
        DEBUG_PRINT("[+] ptrace_attach fail: %d
", target_pid);
        goto exit;
    }
    if (ptrace_getregs(target_pid, &regs) == -1){
        DEBUG_PRINT("[+] ptrace_getregs fail: %d
", target_pid);
        goto exit;
    }
    /* save original registers */
    memcpy(&original_regs, &regs, sizeof(regs));

    mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
    DEBUG_PRINT("[+] Remote mmap address: %x
", 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, &regs) == -1){
          DEBUG_PRINT("[+] ptrace_call_wrapper fail: %d
", target_pid);
          goto exit;
    }
    map_base = ptrace_retval(&regs);

    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
",
            dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);

    printf("library path = %s
", 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, &regs) == -1)
        goto exit;

    void * sohandle = ptrace_retval(&regs);
    if(NULL == sohandle) {
        if (ptrace_call_wrapper(target_pid, "dlerror", dlerror_addr, parameters, 0, &regs) == -1)
             goto exit;
        char * errstr = ptrace_retval(&regs);
        uint8_t buf[1024]={0};
        ptrace_readdata(target_pid, errstr,buf,256);
        DEBUG_PRINT("[+] dlopen return error: %s
", buf);
    }

#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, &regs) == -1)
        goto exit;

    void * hook_entry_addr = ptrace_retval(&regs);
    DEBUG_PRINT("hook_entry_addr = %p
", 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, &regs) == -1)
        goto exit;

   // printf("Press enter to dlclose and detach
");
     printf("Press enter to detach
");
    getchar();

    #if 0
    parameters[0] = sohandle;
    if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, &regs) == -1)
        goto exit;
    #endif
    /* restore */
    ptrace_setregs(target_pid, &original_regs);
    ptrace_detach(target_pid);
    ret = 0;

exit:
    return ret;
}

#define HELPSTR "error usage: %s -p PID [-P PROCNAME] -l LIBNAME -f FUNCTION [-d (debug on)]
"

int main(int argc, char** argv) {
    pid_t target_pid = -1;
    char *proc_name = NULL;
    char *sodir = NULL;
    char *func_name = NULL;
    char *args = "";
    int opt;

    while ((opt = getopt(argc, argv, "p:l:f:P:")) != -1) {
            switch (opt) {
                case 'p':
                    target_pid = strtol(optarg, NULL, 0);
                    break;
                case 'l':
                     sodir =  strdup(optarg);
                     break;
                case 'f':
                     func_name = strdup(optarg);
                     break;
                 case 'P':
                       proc_name = strdup(optarg);
                       break;
                default:
                      fprintf(stderr,HELPSTR,argv[0]);
                      exit(0);
            }
    }

    if(proc_name != NULL && target_pid < 0) target_pid = find_pid_of(proc_name);

    if(target_pid < 0 || NULL == sodir || NULL == func_name) {
        fprintf(stderr,HELPSTR,argv[0]);
        exit(0);
    }

    if(argc>4) args=argv[4];

    inject_remote_process(target_pid, sodir, func_name,  args, strlen(args));
    return 0;
}
原文地址:https://www.cnblogs.com/jiayy/p/4286864.html