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

前面两篇

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

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

实际运行并分析了 Android中的so注入(inject)和挂钩(hook) - For both x86 and arm 这个博客给出了 libinject 改进版的代码。

今天分析一下古河大神原始的 libinject 的源码,libinject2 与 原始的 libinject 大部分代码是一致的,各种 ptrace 的封装函数基本照抄,但有一点很不一样,古河的 libinject 在执行so注入及执行注入的so内的hook函数时,是将一连串的函数构造成一块统一的 shellcode ,然后整块shellcode 写入目标进程 mmap 出来的一块地址,设置目标进程寄存器,一次性调用。 而 libinject2 是分开多次设置目标进程的内存和寄存器,并多次调用实现同样的效果。从代码可读性看,libinject2 更清晰易懂。这一篇文章只分析两者不同的部分。

首先,shellcode 代码写在一个单独的汇编文件里, shellcode.s  , 将需要在目标进程执行的 dlopen,dlsym,dlclose, 及hook函数都先用汇编写好,各个函数的真实地址和参数的真实值由 inject.c 里的函数动态指定,这份汇编会与 inject.c 编译后的汇编链接在一起

.global _dlopen_addr_s
.global _dlopen_param1_s
.global _dlopen_param2_s

.global _dlsym_addr_s
.global _dlsym_param2_s

.global _dlclose_addr_s

.global _inject_start_s
.global _inject_end_s

.global _inject_function_param_s

.global _saved_cpsr_s
.global _saved_r0_pc_s

.data

_inject_start_s:
        @ debug loop
3:
        @sub r1, r1, #0
        @B 3b

        @ dlopen
        ldr r1, _dlopen_param2_s
        ldr r0, _dlopen_param1_s
        ldr r3, _dlopen_addr_s
        blx r3
        subs r4, r0, #0
        beq     2f

        @dlsym
        ldr r1, _dlsym_param2_s
        ldr r3, _dlsym_addr_s
        blx r3
        subs r3, r0, #0
        beq 1f

        @call our function
        ldr r0, _inject_function_param_s
        blx r3
        subs r0, r0, #0
        beq 2f

1:
        @dlclose
        mov r0, r4
        ldr r3, _dlclose_addr_s
        blx r3

2:
        @restore context
        ldr r1, _saved_cpsr_s
        msr cpsr_cf, r1
        ldr sp, _saved_r0_pc_s
        ldmfd sp, {r0-pc}

_dlopen_addr_s:
.word 0x11111111

_dlopen_param1_s:
.word 0x11111111

_dlopen_param2_s:
.word 0x2

_dlsym_addr_s:
.word 0x11111111

_dlsym_param2_s:
.word 0x11111111

_dlclose_addr_s:
.word 0x11111111

_inject_function_param_s:
.word 0x11111111

_saved_cpsr_s:
.word 0x11111111

_saved_r0_pc_s:
.word 0x11111111

_inject_end_s:

.space 0x400, 0

.end

这里插入一下,怎么在android里使用这份代码呢,仍然是新建一个module,然后 Android.mk 如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := inject
LOCAL_SRC_FILES := ../inject.c ../shellcode.s
LOCAL_ARM_MODE := arm
LOCAL_CFLAGS := -g
include $(BUILD_EXECUTABLE)

在Android.mk文件目录下执行 ndk-build 即可

libinject 的注入过程如下:

int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size ){
        int ret = -1;
        void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr;
        void *local_handle, *remote_handle, *dlhandle;
        uint8_t *map_base;

        struct pt_regs regs, original_regs;
        extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s,                         _saved_cpsr_s, _saved_r0_pc_s; // shellcode.s 汇编代码定义的变量

        long parameters[10];

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

if ( ptrace_attach( target_pid ) == -1 )
              return EXIT_SUCCESS;

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

        /* save original registers */
        memcpy( &original_regs, &regs, sizeof(regs) ); // 第一步,attach目标进程,并保留注入前的寄存器状态

        mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap );

        /* 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

        DEBUG_PRINT( "[+] Calling mmap in target process.\n" );

if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, &regs ) == -1 )
            goto exit;

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

        map_base = (uint8_t *)regs.ARM_r0; // 第二步,在目标进程执行 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 );

        remote_code_ptr = map_base + 0x3C00;
        local_code_ptr = (uint8_t *)&_inject_start_s; // 本进程 shellcode 的起始地址

        _dlopen_addr_s = (uint32_t)dlopen_addr;
        _dlsym_addr_s = (uint32_t)dlsym_addr;
        _dlclose_addr_s = (uint32_t)dlclose_addr; //第三步,获取目标进程 dlopen,dlsym,dlclose函数地址并赋值给汇编文件定义的变量

        code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s;
        dlopen_param1_ptr = local_code_ptr + code_length + 0x20;
        dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH;
        saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH;
        inject_param_ptr = saved_r0_pc_ptr + MAX_PATH; // 设置dlopen,dlsym等函数的参数相对于汇编代码起始地址的地址

strcpy( dlopen_param1_ptr, library_path );
        _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr );

/* dlopen parameter 1: library name */
        DEBUG_PRINT( "[+] _dlopen_param1_s: %x\n", _dlopen_param1_s );

        /* dlsym parameter 2: function name */
        strcpy( dlsym_param2_ptr, function_name );
        _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr );
        DEBUG_PRINT( "[+] _dlsym_param2_s: %x\n", _dlsym_param2_s );

        /* saved cpsr */
        _saved_cpsr_s = original_regs.ARM_cpsr;

        /* saved r0-pc */
        memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), 16 * 4 ); // r0 ~ r15
        _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr );
        DEBUG_PRINT( "[+] _saved_r0_pc_s: %x\n", _saved_r0_pc_s );

        /* Inject function parameter */
        memcpy( inject_param_ptr, param, param_size );
        _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr );
        DEBUG_PRINT( "[+] _inject_function_param_s: %x\n", _inject_function_param_s );//第四步,计算要执行的 dlopen,dlsym函数及参数在目标进程内相对于前面mmap出来的地址的地址

        DEBUG_PRINT( "[+] Remote shellcode address: %x\n", remote_code_ptr );
        ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 );//第五步: 将整块shellcode代码写入目标进程的内存

        memcpy( &regs, &original_regs, sizeof(regs) );
        regs.ARM_sp = (long)remote_code_ptr;
        regs.ARM_pc = (long)remote_code_ptr;
        ptrace_setregs( target_pid, &regs ); // 第六步,修改目标进程的栈顶指针 sp 执行shellcode 的初始位置,且设置pc寄存器也指向这个位置
        ptrace_detach( target_pid ); // 第七步,detach目标进程,目标进程接下去会开始执行shellcode

        // inject succeeded
        ret = 0;

exit:
        return ret;
}

我们看到,libinject 没有将 original_regs 恢复到目标进程的ptrace 操作,其实shellcode里执行完hook函数后,最后会执行  @restore context   的代码,这几句就是恢复寄存器状态的

时间: 2024-08-24 04:32:09

android hook 框架 libinject 如何实现so注入的相关文章

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

上一篇 android hook 框架 libinject 简介.编译.运行 实际运行了so的注入并调用了注入so里的一个函数,这篇开始分析其实现. 与之前分析的 abdi 项目一样,libinject2 也是依赖于linux系统的 ptrace 系统调用. android hook 框架 ADBI 简介.编译.运行 android hook 框架 ADBI 如何实现so注入 android hook 框架 ADBI 如何实现函数挂钩 这个库首先对ptrace的调用封装了几个helper函数 i

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

前面几篇分析已经能做到注入一个so到目标进程并用so里的函数挂钩目标进程的函数,如果对这个实现不了解,请返回去阅读  android hook 框架 ADBI 简介.编译.运行  . android hook 框架 ADBI 如何实现so注入 .android hook 框架 ADBI 如何实现so函数挂钩, so函数的挂钩只能影响native世界,没有影响到java虚拟机内部,而android绝大部分逻辑都是跑在虚拟机内部的.所以这篇接着分析 adbi 剩下的部分代码,看它如何实现挂钩dalv

Android Hook框架adbi源码浅析(一)

adbi(The Android Dynamic Binary Instrumentation Toolkit)是一个Android平台通用hook框架,基于动态库注入与inline hook技术实现.该框架由两个主要模块构成,1.hijack负责将动态库注入到目标进程:2.libbase提供动态库本身,它实现了通用的hook功能. 而example则是一个使用adbi进行epoll_wait hook的demo. [email protected]PC:~/Android/adbi-maste

Android Hook框架adbi源码浅析(二)

二.libbase 其实上面加载完SO库后,hook的功能我们完全可以自己在动态库中实现.而adbi作者为了方便我们使用,编写了一个通用的hook框架工具即libbase库.libbase依然在解决两个问题:1.获取要hook的目标函数地址:2.给函数打二进制补丁即inline hook. 关于获取hook函数地址的方法这里不再赘述.直接看inline hook部分,这部分功能在base\hook.c的hook()函数中实现,先看hook_t结构体: struct hook_t { unsign

android hook 框架 xposed 如何实现注入

前面分析的adbi框架和libinject都是使用so注入的方式,实现将指定代码装入目标进程,这种方式有几个特点: 1. 是动态的,需要目标进程已经启动 2. 无法影响全局,比如注入A进程挂钩里边libc.so的open函数,此时,B进程使用的libc.so的open函数还是老函数,linux系统通过COW机制,在你注入A进程并执行对open的挂钩的时候,拷贝了新的页面,放入新的函数.如果要影响全局,应该注入到类似 Zygote 这样的进程,且应该在zygote进程启动之后马上注入,这样后续zy

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

简介: libinject 最开始是2011年看雪android安全版版主之一‘古河’大神发布的一份android平台的注入库:  发个Android平台上的注入代码  ,网上很多随后发布的注入代码都是其变种,不过我这几天尝试运行那份代码,发现有些问题,本博运行和分析的代码是另外一位大神的改进版本,在我的环境里运行注入和挂钩都成功了 : Android中的so注入(inject)和挂钩(hook) - For both x86 and arm ,为了表示区别,我把这个项目称为  libinjec

Android.Hook框架Cydia篇

Cydia Substrate是一个代码修改平台.它可以修改任何主进程的代码,不管是用Java还是C/C++(native代码)编写的.而Xposed只支持HOOK app_process中的java函数,因此Cydia Substrate是一款强大而实用的HOOK工具. 官网地址:http://www.cydiasubstrate.com/ 官方教程:http://www.cydiasubstrate.com/id/38be592b-bda7-4dd2-b049-cec44ef7a73b SD

android hook 框架 xposed 如何实现挂钩

前面知道,安装xposed框架后,系统启动,执行init, init 再调用 app_process 程序,由于这时候 app_process 已经被换了,所以app_process 启动后先进入的是 xposedbridge.class 的 main 函数, 这个函数最后才进入标准的 zygoteInit.class 的 main 函数,在进入 zygote 之前,它调用了几个函数,初始化了xposed框架,下面逐个分析. 一. initNative Xposed.cpp (xposed):

教你写Android ImageLoader框架之图片加载与加载策略

在教你写Android ImageLoader框架之初始配置与请求调度中,我们已经讲述了ImageLoader的请求配置与调度相关的设计与实现.今天我们就来深入了解图片的具体加载过程以及加载的策略(包括按顺序加载和逆序加载) ,在这其中我会分享我的一些设计决策,也欢迎大家给我提建议. 图片的加载 Loader与LoaderManager的实现 在上一篇文章教你写Android ImageLoader框架之初始配置与请求调度中,我们聊到了Loader与LoaderManager. ImageLoa