首页
社区
课程
招聘
[原创]一个烂尾的Android内联hook框架
发表于: 2021-5-23 14:47 11286

[原创]一个烂尾的Android内联hook框架

2021-5-23 14:47
11286

  之前因为有某些需求想找一个arm平台的inline hook框架,但是没能找到一个既精简、又支持修改多参数、修改返回值、支持用自定义函数替换掉原函数的框架。所以就决定花点时间自己实现一个,写了几天以后被告知不用做了,于是便有了这个烂尾项目。今天整理文件的时候看到了,与其留着发霉不如分享给坛友作为学习的例子。此代码为没用的草稿,不合规范的地方就不用对其提意见了。

  2.1 精简
    使用一个结构体和一段shellcode维护一个hook
  2.2 支持多线程hook
    每个hook维护一段只属于自己的shellcode
  2.3 支持修改参数
    参数数量小于4的修改r0-r3寄存器,大于4的再修改栈空间
  2.4 支持修改返回值
    覆盖r0寄存器的值
  2.5 支持函数替换
    hook函数执行完以后加载原函数下一条指令地址到lr寄存器
    总体设计为,每个hook维持一个结构体与一段自己的shellcode,初始化hook时直接修改shellcode中的变量值,备份并修改原函数的起始汇编代码,使其直接跳到shellcode执行,完成其余的工作。所有的hook保存在一个链表中进行统一管理。

    以下为维护hook的结构体:

    在初始化hook时,首先获取shellcode中变量的内存地址,分配一块内存空间给结构体中的shellcode_code字段,并记录长度,记录原始函数的地址与hook函数的地址,存储原函数的前几条指令作为备份代码,并记录备份代码的长度,param_list中存储有想要修改的参数值,ret_value中存储有想要修改成的返回值。

    初始化hook的代码如下:

    在备份原函数前几条指令时需要对指令类型进行判断,如果是thumb指令,则需要对地址减1再进行保存。
    判断指令类型的代码如下:

    备份指令的函数如下:

    指令地址为奇数是thumb指令
    在shellcode开始执行之前,需要先对其中的变量进行初始化,以下为需要初始化的值:

    shellcode在执行时,需要先保存原始上下文,由于r13,r14,r15寄存器比较特殊,在跳转到保存上下文代码块时,寄存器的值已经发生变化,因此需要单独对其进行保存

    再进行上下文环境保存,代码如下:

    与保存上下文相对应的,以下是恢复上下文的代码块:

    执行hook的代码:

    以上代码中,callback_modify_param被填充为以下函数:

    callback_modify_return的值被填充为以下函数的地址:

    指令修复部分还没完成。

    最后附上Makefile:

    总体来讲arm上的inline hook框架要比x86难实现一点,传参方式使用寄存器加栈顶空间,还需要对两种不同类型的指令进行判断和修复。目前的代码只能说是写了一个开头,要想做一个完整的框架,还是需要一点工作量的。

typedef struct inline_item{
    void* shell_code;           // shelcode的内存起始地址
    int32_t shell_code_len;     // shellcode的长度
    address_t old_fun;          // 原始函数的起始地址
    address_t new_fun;          // hook函数的起始地址
    void* back_code;            // 备份的代码
    int32_t back_code_len;      // 备份代码的长度
    void* reg;                  // 保存寄存器空间
    void* stack;                // 保存栈空间
    list_node_t* param_list;    // 参数链表
    void* ret_value;            // 返回值
} inline_item_t;
typedef struct inline_item{
    void* shell_code;           // shelcode的内存起始地址
    int32_t shell_code_len;     // shellcode的长度
    address_t old_fun;          // 原始函数的起始地址
    address_t new_fun;          // hook函数的起始地址
    void* back_code;            // 备份的代码
    int32_t back_code_len;      // 备份代码的长度
    void* reg;                  // 保存寄存器空间
    void* stack;                // 保存栈空间
    list_node_t* param_list;    // 参数链表
    void* ret_value;            // 返回值
} inline_item_t;
 
inline_item_t* init_item(address_t* target_fun, address_t* hook_fun, list_node_t* param_list,address_t ret_value)
{
    inline_item_t* p_item = 0;
    address_t temp = 0;
    p_item = (inline_item_t*)malloc( sizeof(inline_item_t) );
    p_item->shell_code_len = (int8_t*)&asm_shellcode_end - (int8_t*)&asm_shellcode_begin;
    p_item->shell_code = (int8_t*)malloc( p_item->shell_code_len);  // 分配shellcode空间
    p_item->old_fun = target_fun;                                   // 保存原始函数地址
    p_item->new_fun = hook_fun;                                     // 存储hook函数地址
    p_item->back_code_len = 12;                                     // 备份12字节指令
    p_item->back_code = malloc(p_item->back_code_len);
    p_item->reg = malloc(0x10 * sizeof(address_t));                 // 分配备份寄存器的空间
    p_item->stack = malloc(0x10 * sizeof(address_t));               // 分配备份栈的空间
    p_item->param_list = 0;
    p_item->ret_value = 0;
    if( param_list != 0){
        p_item->param_list = param_list;                            // 初始化要修改的参数列表
    }
    if( ret_value!=0 ){
        p_item->ret_value = (address_t*)malloc( sizeof(address_t) ); // 初始化要修改的返回值
        ret_value = 10;
        memcpy( p_item->ret_value, &ret_value, sizeof(address_t));
    }
    return p_item;
}
inline_item_t* init_item(address_t* target_fun, address_t* hook_fun, list_node_t* param_list,address_t ret_value)
{
    inline_item_t* p_item = 0;
    address_t temp = 0;
    p_item = (inline_item_t*)malloc( sizeof(inline_item_t) );
    p_item->shell_code_len = (int8_t*)&asm_shellcode_end - (int8_t*)&asm_shellcode_begin;
    p_item->shell_code = (int8_t*)malloc( p_item->shell_code_len);  // 分配shellcode空间
    p_item->old_fun = target_fun;                                   // 保存原始函数地址
    p_item->new_fun = hook_fun;                                     // 存储hook函数地址
    p_item->back_code_len = 12;                                     // 备份12字节指令
    p_item->back_code = malloc(p_item->back_code_len);
    p_item->reg = malloc(0x10 * sizeof(address_t));                 // 分配备份寄存器的空间
    p_item->stack = malloc(0x10 * sizeof(address_t));               // 分配备份栈的空间
    p_item->param_list = 0;
    p_item->ret_value = 0;
    if( param_list != 0){
        p_item->param_list = param_list;                            // 初始化要修改的参数列表
    }
    if( ret_value!=0 ){
        p_item->ret_value = (address_t*)malloc( sizeof(address_t) ); // 初始化要修改的返回值
        ret_value = 10;
        memcpy( p_item->ret_value, &ret_value, sizeof(address_t));
    }
    return p_item;
}
bool get_type(address_t addr)  
{
#if defined(__arm64__) || defined(__aarch64__)
    return ((int64_t)addr & 0x1);
#else
    return ((int32_t)addr & 0x1);  
#endif
}
bool get_type(address_t addr)  
{
#if defined(__arm64__) || defined(__aarch64__)
    return ((int64_t)addr & 0x1);
#else
    return ((int32_t)addr & 0x1);  
#endif
}
void back_target(inline_item_t* p_item){
    p_item->back_code_len = BACKLEN;
    //保存old_fun前几条指令
    p_item->back_code = (int8_t*)malloc(p_item->back_code_len);
    memcpy(p_item->back_code, (address_t*)p_item->old_fun, p_item->back_code_len);
    memcpy(p_item->shell_code + asm_pos.back_code_pos, p_item->back_code, p_item->back_code_len);
    //修改old_fun前几条指令
    if( !set_mem_permission(p_item->old_fun,RWX) ){ // 修改内存属性为可读可写可执行
        return ;
    }
    memcpy((address_t*)p_item->old_fun, p_item->shell_code + asm_pos.load_pc_pos,sizeof(address_t));
    memcpy((address_t*)p_item->old_fun + 1, &p_item->shell_code, sizeof(address_t));
    memcpy(p_item->shell_code+asm_pos.load_pc_pos, p_item->back_code, sizeof(address_t)*3);
}
void back_target(inline_item_t* p_item){
    p_item->back_code_len = BACKLEN;
    //保存old_fun前几条指令
    p_item->back_code = (int8_t*)malloc(p_item->back_code_len);
    memcpy(p_item->back_code, (address_t*)p_item->old_fun, p_item->back_code_len);
    memcpy(p_item->shell_code + asm_pos.back_code_pos, p_item->back_code, p_item->back_code_len);
    //修改old_fun前几条指令
    if( !set_mem_permission(p_item->old_fun,RWX) ){ // 修改内存属性为可读可写可执行
        return ;
    }
    memcpy((address_t*)p_item->old_fun, p_item->shell_code + asm_pos.load_pc_pos,sizeof(address_t));
    memcpy((address_t*)p_item->old_fun + 1, &p_item->shell_code, sizeof(address_t));
    memcpy(p_item->shell_code+asm_pos.load_pc_pos, p_item->back_code, sizeof(address_t)*3);
}
asm_old_fun_continue:   // 原函数的地址
    .word 0x12345678   
asm_new_fun:            // hook函数的地址           
    .word 0x12345678   
asm_param_num:          //参数的个数
    .word 0x00000010
asm_reg_sp:             // 栈顶值
    .word 0x12345678
asm_reg_lr:             // 返回地址
    .word 0x12345678
callback_modify_param:  // callback_modify_param函数的地址
    .word 0x00000000
callback_modify_return: // callback_modify_return函数的地址
    .word 0x00000000
asm_item_addr:          // 当前hook结构体地址
    .word 0x12345678
asm_reg_addr:         
    .word 0x12345678
asm_stack_addr:
    .word 0x12345678
 
asm_back_code:          // 备份的原始函数代码块
    .word 0x12345678
    .word 0x12345678
    .word 0x12345678
    ldr r15,asm_old_fun_continue    // 跳转到备份代码的下一条指令继续执行
asm_old_fun_continue:   // 原函数的地址
    .word 0x12345678   
asm_new_fun:            // hook函数的地址           
    .word 0x12345678   
asm_param_num:          //参数的个数
    .word 0x00000010
asm_reg_sp:             // 栈顶值
    .word 0x12345678
asm_reg_lr:             // 返回地址
    .word 0x12345678
callback_modify_param:  // callback_modify_param函数的地址
    .word 0x00000000
callback_modify_return: // callback_modify_return函数的地址
    .word 0x00000000
asm_item_addr:          // 当前hook结构体地址
    .word 0x12345678
asm_reg_addr:         
    .word 0x12345678
asm_stack_addr:
    .word 0x12345678
 
asm_back_code:          // 备份的原始函数代码块
    .word 0x12345678
    .word 0x12345678
    .word 0x12345678
    ldr r15,asm_old_fun_continue    // 跳转到备份代码的下一条指令继续执行
str r13,asm_reg_sp
str r14,asm_reg_lr
// stmfd sp!,{r0}       // 这个需要注意,保存的pc应当是old_fun的下一条指令地址,
// sub r0,r15,0x        // 所以执行到这里以后要保存当前pc减去已经执行的指令步数*4
// ldmfd sp!,{r0}
bl fun_save_context
str r13,asm_reg_sp
str r14,asm_reg_lr
// stmfd sp!,{r0}       // 这个需要注意,保存的pc应当是old_fun的下一条指令地址,
// sub r0,r15,0x        // 所以执行到这里以后要保存当前pc减去已经执行的指令步数*4
// ldmfd sp!,{r0}
bl fun_save_context
fun_save_context:
    stmfd sp!,{r12} // ------- 开始保存寄存器
    ldr r12,asm_reg_addr
    stmea r12!,{r0-r11}      //保存r0--r11
    mov r0,r12
    ldmfd sp!,{r12}          //取出r12
    stmea r0!,{r12}          //保存r12
    mrs r1,cpsr              //保存CPSR寄存器
    stmea r0!,{r1}
   // mov r1,sp                //不能直接操作sp
   // stmea r0!,{r1} // ------- 寄存器保存完成
    ldr r0,asm_stack_addr //-- 开始保存栈空间
    mov r2,0x10              //需要保存的栈空间大小
    mov r1,sp
 _loop_save_stack:
    cmp r2,0x00
    ble _loop_save_context_end
    ldr r3,[r1]
    str r3,[r0],0x04
    add r1,r1,0x04
    sub r2,r2,0x01
    b _loop_save_stack
 _loop_save_context_end:  //-- 栈空间保存完成
    bx r14
fun_save_context_end:
fun_save_context:
    stmfd sp!,{r12} // ------- 开始保存寄存器
    ldr r12,asm_reg_addr
    stmea r12!,{r0-r11}      //保存r0--r11
    mov r0,r12
    ldmfd sp!,{r12}          //取出r12
    stmea r0!,{r12}          //保存r12
    mrs r1,cpsr              //保存CPSR寄存器
    stmea r0!,{r1}
   // mov r1,sp                //不能直接操作sp
   // stmea r0!,{r1} // ------- 寄存器保存完成
    ldr r0,asm_stack_addr //-- 开始保存栈空间
    mov r2,0x10              //需要保存的栈空间大小
    mov r1,sp
 _loop_save_stack:
    cmp r2,0x00
    ble _loop_save_context_end
    ldr r3,[r1]

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-5-23 14:53 被某警官编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (3)
雪    币: 156
活跃值: (3801)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
作者有考虑dobby吗?
2021-5-23 22:21
0
雪    币: 29
活跃值: (5667)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
作者有考虑Substrate吗?
2021-5-24 01:43
0
雪    币: 1422
活跃值: (1578)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
不吃早饭 作者有考虑Substrate吗?
饭哥啥时候搓个文章让大伙长长见识
2021-5-25 15:05
0
游客
登录 | 注册 方可回帖
返回
//