天天看点

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跳转,随便整!