前面两篇

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
:
@sub r1, r1, #
@B 3b @ dlopen
ldr r1, _dlopen_param2_s
ldr r0, _dlopen_param1_s
ldr r3, _dlopen_addr_s
blx r3
subs r4, r0, #
beq 2f @dlsym
ldr r1, _dlsym_param2_s
ldr r3, _dlsym_addr_s
blx r3
subs r3, r0, #
beq 1f @call our function
ldr r0, _inject_function_param_s
blx r3
subs r0, r0, #
beq 2f :
@dlclose
mov r0, r4
ldr r3, _dlclose_addr_s
blx r3 :
@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, .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 = -;
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[]; DEBUG_PRINT( "[+] Injecting process: %d\n", target_pid );

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

if ( ptrace_getregs( target_pid, &regs ) == - )
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[] = ; // addr
parameters[] = 0x4000; // size
parameters[] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
parameters[] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
parameters[] = ; //fd
parameters[] = ; //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 ) == - )
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), * ); // 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 = ; exit:
return ret;
}

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

到目前为止,一共分析了3份so注入代码,前两份为:

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

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

对比一下,

首先,3份注入代码原理是一样的,都是利用ptrace系统调用,获取目标进程寄存器,在目标进程调用dlopen/dlsym等动态库函数实现在目标进程加载指定的so, 加载之后执行挂钩函数时,hijack 利用  __attribute__ ((constructor)) 函数属性实现自动执行,libinject/libinject2 还是通过寄存器ptrace操作执行函数。

其次,古河的libinject和adbi的hijack都是事先构造一片‘shellcode’,再块拷贝到目标进程地址空间(先操作目标进程寄存器调用mmap分配一块),然后再执行这块预先构造好的‘shellcode’,不同是libinject 是用汇编代码构造,hijack是写在一个数组里。libinject2在实现注入时,将上述shellcode分成多次的 ptrace 调用,代码可读性更高,逻辑更清晰

最后,libinject/libinject2只是提高了注入的实现,没有挂钩的实现。 adbi 项目还提供了挂钩框架的实现

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

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

    Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2  如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...

  2. Android Hook框架adbi的分析(1)---注入工具hijack

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74055505 一.Android Hook框架adbi的基本介绍 adbi是And ...

  3. android hook 框架 ADBI 如何实现so注入

    Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2  如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...

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

    Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2  如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...

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

    Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2  如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...

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

    adbi(The Android Dynamic Binary Instrumentation Toolkit)是一个Android平台通用hook框架,基于动态库注入与inline hook技术实现 ...

  7. Android Hook框架adbi的分析(2)--- inline Hook的实现

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74452308 一. Android Hook框架adbi源码中inline Hoo ...

  8. Android Hook框架adbi的分析(3)---编译和inline Hook实践

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/75200800 一.序言 在前面的博客中,已经分析过了Android Hook框架a ...

  9. android hook 框架 xposed 如何实现注入

    Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2  如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...

随机推荐

  1. MySQL触发器和更新操作

    一.触发器概念 触发器(trigger):监视某种情况,并触发某种操作,它是提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动 ...

  2. python文件,字符串,二进制的读写

    读文件: f = open('/Users/michael/test.txt', 'r') #一次读取文件的全部内容 f.read() #文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且 ...

  3. Altium Designer 快捷键使用整理

    Altium Designer 快捷键 一.原理图部分 1.原理图元件自动编号 原理图中快捷键 T+A 2.原理图与PCB交互设计查找 原理图中选中一个元件跳转到PCB中相应的位置T+S 3.原理图中 ...

  4. python语法root=Tkinter.Tk()

    1. Tkinter 是一个python模块,是一个调用Tcl/Tk的接口,它是一个跨平台的脚本图形界面接口.Tkinter不是唯一的python图形编程接口,但是是 其中比较流行的一个.最大的特点是 ...

  5. Jmeter-jtl性能测试报告转换-2种导出方法

    方法一*********************** 环境搭建 1.Java JDK   (版本最好在1.6或者1.6以上) 2.ANT 安装 下载地址:http://ant.apache.org/b ...

  6. python 多版本的兼容

    1.针对linux版本 linux版本的话,首先调用whereis python 来获取到多版本的路径. root@Ulord-14:~# whereis pythonpython: /usr/bin ...

  7. UVa 11374 - Airport Express ( dijkstra预处理 )

    起点和终点各做一次单源最短路, d1[i], d2[i]分别代表起点到i点的最短路和终点到i点的最短路,枚举商业线车票cost(a, b);  ans = min( d1[a] + cost(a, b ...

  8. ESLint 代码检查规范

    目录 Airbnb Javascript Style Guide 引用 对象 数组 函数 箭头函数 类和构造器 模块 Iterators and Generators 变量 比较运算符 注释 空格 A ...

  9. ALPHA(10)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  10. web自动化测试:watir+minitest(四)

    脚本连跑: rake是ruby中的一个构建工具,和make很像.允许用ruby来写rakefile. 我们使用rake以任务的方式来运行我们的脚本集. 新建Rakefile文件,写入如下内容: req ...