首先给大家分享一个巨牛巨牛的人工智能教程,是我无意中发现的。教程不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈~我正在学习中,觉得太牛了,所以分享给大家!点这里可以跳转到教程
1. 简介
使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行进程(以下简称为:目标进程)空间中,从而.so中的函数在目标进程空间中有对应的地址,然后通过此地址便可在目标进程中进行调用。
到底是如何注入的呢?
本文实现方案为:在目标进程中,通过dlopen把需要注入的.so加载到目标进程的空间中。
2. 如何让目标进程执行dlopen加载.so?
显然,目标进程本来是没有实现通过dlopen来加载我们想注入的.so,为了实现此功能,我们需要目标进程执行一段我们实现的代码,此段代码的功能为通过dlopen来加载一个.so。
3. 【加载.so的实现代码】
加载需要注入的.so的实现代码如下所示:
.global _dlopen_addr_s @dlopen函数在目标进程中的地址 注:以下全局变化在C中可读写.global _dlopen_param1_s @dlopen参数1<.so>在目标进程中的地址 .global _dlopen_param2_s @dlopen参数2在目标进程中的地址.global _dlsym_addr_s @dlsym函数在目标进程中的地址.global _dlsym_param2_s @dlsym参数2在目标进程中的地址,其实为函数名.global _dlclose_addr_s @dlcose在目标进程中的地址.global _inject_start_s @汇编代码段的起始地址.global _inject_end_s @汇编代码段的结束地址.global _inject_function_param_s @hook_init参数在目标进程中的地址.global _saved_cpsr_s @保存CPSR,以便执行完hook_init之后恢复环境.global _saved_r0_pc_s @保存r0-r15,以便执行完hook_init之后恢复环境.data_inject_start_s: @ debug loop3: @sub r1, r1, #0 @B 3b @ dlopen ldr r1, _dlopen_param2_s @设置dlopen第二个参数, flag ldr r0, _dlopen_param1_s @设置dlopen第一个参数 .so ldr r3, _dlopen_addr_s @设置dlopen函数 blx r3 @执行dlopen函数,返回值位于r0中 subs r4, r0, #0 @把dlopen的返回值soinfo保存在r4中,以方便后面dlclose使用 beq 2f @dlsym ldr r1, _dlsym_param2_s @设置dlsym第二个参数,第一个参数已经在r0中了 ldr r3, _dlsym_addr_s @设置dlsym函数 blx r3 @执行dlsym函数,返回值位于r0中 subs r3, r0, #0 @把返回值<hook_init在目标进程中的地址>保存在r3中 beq 1f @call our function ldr r0, _inject_function_param_s @设置hook_init第一个参数 blx r3 @执行hook_init subs r0, r0, #0 beq 2f1: @dlclose mov r0, r4 @把dlopen的返回值设为dlcose的第一个参数 ldr r3, _dlclose_addr_s @设置dlclose函数 blx r3 @执行dlclose函数2: @restore context ldr r1, _saved_cpsr_s @恢复CPSR msr cpsr_cf, r1 ldr sp, _saved_r0_pc_s @恢复寄存器r0-r15 ldmfd sp, {r0-pc} _dlopen_addr_s: @初始化_dlopen_addr_s.word 0x11111111_dlopen_param1_s:.word 0x11111111_dlopen_param2_s:.word 0x2 @RTLD_GLOBAL_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
4. 如何把【加载.so的实现代码】写入目标进程并启动执行?
为了把【加载.so的实现代码】写入目标进程,主要有以下两步操作:
1) 在目标进程中找到存放【加载.so的实现代码】的空间(通过mmap实现)
2) 把【加载.so的实现代码】写入目标进程指定的空间
3) 启动执行
4.1 在目标进程中找到存放【加载.so的实现代码】的空间
通过mmap来实现,其实现步骤如下:
1) 获取目标进程中mmap地址
2) 把mmap参数据放入r0-r3,另外两个写入目标进程sp
3) pc设置为mmap地址,lr设置为0
4) 把准备好的寄存器写入目标进程(PTRACE_SETREGS),并启动目标进程运行(PTRACE_CONT)
5) 分配的内存首地址位于r0 (PTRACE_GETREGS)
4.2 为【加载.so的实现代码】中的全局变量赋值
1) 获取目标进程中dlopen地址并赋值给_dlopen_addr_s
2) 获取目标进程中dlsym地址并赋值给_dlsym_addr_s
3) 获取目标进程中dlclose地址并赋值给_dlclose_addr_s
4) 把需要加载的.so的路径放入 汇编代码中,并获取此路径在目标进程中的地址然后赋值给_dlopen_param1_s
5) 把需要加载的.so中的hook_init放入 汇编代码中,并获取此路径在目标进程中的地址然后赋值给_dlsym_param2_s
6) 把目标进程中的cpsr保存在_saved_cpsr_s中
7) 把目标进程中的r0-r15存入汇编代码中,并获取此变量在目标进程中的地址然后赋值给_saved_r0_pc_s
8) 通过ptrace( PTRACE_POKETEXT,...)把汇编代码写入目标进程中,起始地址由前面的mmap所分配
9) 把目标进程的pc设置为汇编代码的起始地址,然后调用ptrace(PTRACE_DETACH,...)以启动目标进程执行
5. 把汇编代码写入目标进程并执行的实现代码
5.1 主函数 writecode_to_targetproc
#include <stdio.h>#include <stdlib.h>#include <asm/ptrace.h>#include <asm/user.h>#include <asm/ptrace.h>#include <sys/wait.h>#include <sys/mman.h>#include <dlfcn.h>#include <dirent.h>#include <unistd.h>#include <string.h>#include <android/log.h>#include <sys/types.h>#include <sys/socket.h>#inc