天天看點

Android平台arm64 ptrace hook bridge_code debug

參考文檔:

http://blog.chinaunix.net/uid-20361370-id-1962528.html

共享庫注入–injectso執行個體

http://www.aiuxian.com/article/p-1295924.html

Android平台的 Ptrace, 注入, Hook 全攻略

最近學習ptrace,也想嘗試寫個hook的程式玩玩。最後階段的bridge_code代碼總是出錯。ptrace之後則不能通過gdbserver64 調試被控程序(應該可以hook完修改代碼之後退出,用gdbserver64 迅速以attach的方式連接配接調試,當然原程式裡面加入sleep延遲友善操作,這是後話,當時沒想到 )。

也不能在原程式裡面自行調試,因為源程式沒有資格修改自己的代碼段,後來想到可以用gdb修改完代碼,再繼續運作,調試程式具體錯在哪。

參考:Android平台的 Ptrace, 注入, Hook 全攻略

直接上代碼

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <utils/Log.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/statfs.h>
#include <sys/inotify.h>
#include <poll.h>

#include <cutils/log.h>

#include <sys/reg.h>
#include <sys/uio.h>
#include <elf.h>
#include <asm/ptrace.h>
#include <sys/ptrace.h>
#include <linux/ptrace.h>

#include <sys/mman.h>


int flag = ;
int count = ;


int test()
{
    printf("Now in %s. Target is running: %d\n", __func__, count);
    count++;
    return ;   
}

int hookfun()
{
    printf("Now in %s. Target is running: %d\n", __func__, count);
    count++;
    return ;   
}

int bridge_code()
{

    asm("stp    x6, x7, [sp,#-16]! \n\t"  
    "stp    x4, x5, [sp,#-16]! \n\t"  
    "stp    x2, x3, [sp,#-16]! \n\t"  
    "stp    x0, x1, [sp,#-16]! \n\t"  
    "ldr    x6, loc_tar \n\t"
    "mov    x7, x30 \n\t"
    "stp    x6, x7, [sp,#-16]! \n\t"  
    "blr    x6 \n\t"
    "ldp    x6, x7, [sp],#16 \n\t"
    "mov    x30,x7 \n\t"
    "ldp    x0, x1, [sp],#16 \n\t"  
    "ldp    x2, x3, [sp],#16 \n\t"  
    "ldp    x4, x5, [sp],#16 \n\t"  
    "ldp    x6, x7, [sp],#16 \n\t"  
    "mov    x0, x0 \n\t"            //exec code
    "mov    x0, x0 \n\t"            //b back
    "loc_tar: \t\n"
    ".word 0x0 \t\n"            //hook_func addr
    ".word 0x0 \t\n"
    );  
    return ;
}

unsigned int build_jmp_code(long jump_from, long jump_to)
{
    0 0 0 1 0 1 imm26
    unsigned int b_inst_1 = ;
    unsigned int b_inst_2 = ;

    unsigned int ret;
    int jump_dis;

//  printf("jump_from   = %lx\n", jump_from);
//  printf("jump_to   = %lx\n", jump_to);

    jump_dis = jump_to - jump_from;

    printf("jump_dis = %d\n", jump_dis);

    jump_dis /= ;

//  printf("jump_dis = %d\n", jump_dis);

    if (jump_dis >  ) {
        ret = b_inst_1 | jump_dis;
    } else {
        ret = b_inst_2 | (jump_dis & );
    }

    return ret;
}

int print_gdb_change_info(long src_addr, long hookfun_addr, long bridge_addr)
{

    unsigned int src_code;
    unsigned int src_fill;
    unsigned int back_fill;

    int fill_opcount  = ;
    int jback_opcount = ;
    int  hook_opcount = ;

    src_fill  = build_jmp_code(src_addr, bridge_addr);
    back_fill = build_jmp_code(bridge_addr + jback_opcount *  , src_addr + );

    src_code = *(unsigned int *)src_addr;


    printf("/************************************/\n");
    printf("gdb change code as following:\n");

    printf("set *(int *)0x%lx = 0x%x\n", src_addr, src_fill);   
    printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + fill_opcount * , src_code);
    printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + jback_opcount * , back_fill);
    printf("set *(long *)0x%lx = 0x%lx\n", bridge_addr + hook_opcount * , hookfun_addr);
    printf("/************************************/\n\n");   
    return ;

}

int main()
{
    pid_t pid;
    pid = getpid();

    long ret;

    printf("Target pid = %d\n", pid);

    printf("test = %p\n", &test);
    printf("hookfun = %p\n", &hookfun);
    printf("bridge_code = %p\n", &bridge_code);

    print_gdb_change_info((long)&test, (long)&hookfun, (long)&bridge_code);

    while(flag) {
        test();
        sleep();
    }
    return ;
}
           

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bridge_code_test
LOCAL_SRC_FILES := bridge_code_test.c
LOCAL_SHARED_LIBRARIES:= libutils libcutils

#LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
LOCAL_CFLAGS+= -DAARCH64 -O0

include $(BUILD_EXECUTABLE)
           

bridge_code 不言而喻,參考文檔中是直接給出機器指令,我不想這麼麻煩,可以直接寫在hook_s的源程式裡,然後複制到一段malloc的位址中,修改其中需要修改的地方,ptrace傳遞到目标程式。當然,在這裡就是為了調試這段代碼成功與否。無需複制,複制也沒用,test源程式的四位元組指令不靠gdb,咱也改不了。

hook思路很簡單,test程式運作,就先執行hookfun,然後再執行test。

編譯,不想折騰,編譯非PIE的。直接gdb中運作。

Android平台arm64 ptrace hook bridge_code debug

gdb中如何修改已經列印出來了,友善自己,也友善需要的兄弟們調試!

Android平台arm64 ptrace hook bridge_code debug

如圖,一目了然,test第一條指令跳轉到正确的位址,bridge_code也修改正确。gdb,還是你最牛!

直接continue繼續運作,結果正确。

Android平台arm64 ptrace hook bridge_code debug

太假了,一次就成功了?哪有,錯誤的道路不想走了,懶。直接給出錯誤:

"stp    x6, x7, [sp,#-16]! \n\t"  
"blr    x6 \n\t"
"ldp    x6, x7, [sp],#16 \n\t"
           

之前的代碼“blr x6”前後的指令都沒有。沒有,為什麼錯了呢?

v7之前的abi(或是eabi) 隻要求r0-r3無需儲存,想當然v8也一樣,害死人。

如下圖,不說了,讓我靜靜地多哭會。

Android平台arm64 ptrace hook bridge_code debug

感覺bridge_code怪怪的,再剽竊下編譯器反彙編出來的代碼,整個像正常函數的:

int bridge_code()
{
    asm("stp    x29, x30, [sp,#-80]! \n\t"
        "mov    x29, sp \n\t"
        "stp    x0, x1, [sp,#16] \n\t"
        "stp    x2, x3, [sp,#32] \n\t"
        "stp    x4, x5, [sp,#48] \n\t"
        "stp    x6, x7, [sp,#64] \n\t"
        "ldr    x5, loc_tar \n\t"
        "blr    x5 \n\t"  
        "ldp    x0, x1, [sp,#16] \n\t"
        "ldp    x2, x3, [sp,#32] \n\t"
        "ldp    x4, x5, [sp,#48] \n\t"
        "ldp    x6, x7, [sp,#64] \n\t"
        "ldp    x29, x30, [sp],#80 \n\t"
        "ret \n\t"              //exec code
        "ret \n\t"          //b back
        "loc_tar: \t\n"
        ".xword hookfun \t\n"           //hook_func addr    
    );  
    return ;
}
int print_gdb_change_info(long src_addr, long hookfun_addr, long bridge_addr)
{

    unsigned int src_code;
    unsigned int src_fill;
    unsigned int back_fill;

    int fill_opcount  = ;
    int jback_opcount = ;
    int  hook_opcount = ;

    src_fill  = build_jmp_code(src_addr, bridge_addr);
    back_fill = build_jmp_code(bridge_addr + jback_opcount *  , src_addr + );

    src_code = *(unsigned int *)src_addr;


    printf("/************************************/\n");
    printf("gdb change code as following:\n");

    printf("set *(int *)0x%lx = 0x%x\n", src_addr, src_fill);   
    printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + fill_opcount * , src_code);
    printf("set *(int *)0x%lx = 0x%x\n", bridge_addr + jback_opcount * , back_fill);
    printf("set *(long *)0x%lx = 0x%lx\n", bridge_addr + hook_opcount * , hookfun_addr);
    printf("/************************************/\n\n");   
    return ;

}

int main()
{
    pid_t pid;
    pid = getpid();

    long ret;

    printf("Target pid = %d\n", pid);

    printf("test = %p\n", &test);
    printf("hookfun = %p\n", &hookfun);
    printf("bridge_code = %p\n", &bridge_code);

    bridge_code();

    print_gdb_change_info((long)&test, (long)&hookfun, (long)&bridge_code);

    while(flag) {
        test();
        sleep();
    }
    return ;
}
           

改個bridge_code,還貼其他代碼做什麼。

一來彙編改了,修改偏移的數字13 14 15也要改改,main函數直接調用了bridge_code,沒騙大家,不改之前,bridge_code也是一個正常的函數。

由于“.xword hookfun”的存在,隻能編譯為非PIE的了。

如下圖,hookfun執行了一次。

Android平台arm64 ptrace hook bridge_code debug
Android平台arm64 ptrace hook bridge_code debug

既然需要用到指令計算,順便看看:

unsigned int build_jmp_code(long jump_from, long jump_to)
{
    0 0 0 1 0 1 imm26
    unsigned int b_inst_1 = ;
    unsigned int b_inst_2 = ;

    unsigned int ret;
    int jump_dis;

    jump_dis = jump_to - jump_from;

    printf("jump_dis = %d\n", jump_dis);

    jump_dis /= ;

    if (jump_dis >  ) {
        ret = b_inst_1 | jump_dis;
    } else {
        ret = b_inst_2 | (jump_dis & );
    }

    return ret;
}
           
Android平台arm64 ptrace hook bridge_code debug

左移右移還是不靠譜啊,還是除法簡單。

注意正負128M的限制,ptrace mmap bridge_code空間的時候可不能随便申請,得靠原函數近點,目标函數blr跳轉,随便整!