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.\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;
}
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\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;
}
#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]\n", 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\n", 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\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, &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\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);// 第四步,獲取目標進程動態庫的幾個函數,並將要注入的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\n", 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\n"); // 第七步,在目標進程內調用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\n");
                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\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__)
#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\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;
}
#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]\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;
    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\n", target_pid);

    if (ptrace_attach(target_pid) == -1){
        DEBUG_PRINT("[+] ptrace_attach fail: %d\n", target_pid);
        goto exit;
    }
    if (ptrace_getregs(target_pid, &regs) == -1){
        DEBUG_PRINT("[+] ptrace_getregs fail: %d\n", 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\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, &regs) == -1){
          DEBUG_PRINT("[+] ptrace_call_wrapper fail: %d\n", 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\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, &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\n", 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\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, &regs) == -1)
        goto exit;

   // printf("Press enter to dlclose and detach\n");
     printf("Press enter to detach\n");
    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)]\n"

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM