Android so注入-libinject2 简介、编译、运行

Android so注入-libinject2  如何实现so注入

Android so注入-Libinject 如何实现so注入

Android so注入挂钩-Adbi 框架简介、编译、运行

Android so注入挂钩-Adbi 框架如何实现so注入

Android so注入挂钩-Adbi 框架如何实现so函数挂钩

Android so注入挂钩-Adbi 框架如何实现dalvik函数挂钩

Android dalvik挂钩-Xposed框架如何实现注入

Android dalvik挂钩-Xposed框架如何实现挂钩

上一篇 android hook 框架 libinject 简介、编译、运行 实际运行了so的注入并调用了注入so里的一个函数,这篇开始分析其实现。

与之前分析的 abdi 项目一样,libinject2 也是依赖于linux系统的 ptrace 系统调用。

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

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

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

这个库首先对ptrace的调用封装了几个helper函数

int ptrace_readdata(pid_t pid,  uint8_t *src, uint8_t *buf, size_t size)
{
uint32_t i, j, remain;
uint8_t *laddr; union u {
long val; // 当以满4字节读取内容时,直接使用 long 变量
char chars[sizeof(long)]; // 最后不满4字节的内容,使用 char 变量
} d; j = size / ;
remain = size % ; laddr = buf; for (i = ; i < j; i ++) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, src, );
memcpy(laddr, d.chars, );
src += ;
laddr += ;
} if (remain > ) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, src, );
memcpy(laddr, d.chars, remain);
} return ;
}
ptrace_readdata : src 对应 tracee进程指定地址,buf 对应trace进程地址, size 长度。 这个函数从目标进程src地址开始读size长度内容到本进程的buf内存里。使用 ptrace 函数,第一个参数 PTRACE_PEEKTEXT 实现从 tracee 进程读取数据。
由于 ptrace 的 peektext 时是以 32位为单位,而 size 是1字节为单位,所以先把 size 转成 4 字节为单位,依次用 ptrace 读取,最后剩下的不够 4字节的余数,依然调用一次 ptrace peektext,然后拷贝实际要的字节数到目标地址。
ptrace_readdata 在 libinject2 里并没有使用。

int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size)
{
uint32_t i, j, remain;
uint8_t *laddr; union u {
long val;
char chars[sizeof(long)];
} d; j = size / ;
remain = size % ; laddr = data; for (i = ; i < j; i ++) {
memcpy(d.chars, laddr, );
ptrace(PTRACE_POKETEXT, pid, dest, d.val); dest += ;
laddr += ;
} if (remain > ) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, );
for (i = ; i < remain; i ++) {
d.chars[i] = *laddr ++;
} ptrace(PTRACE_POKETEXT, pid, dest, d.val);
} return ;
}
ptrace_writedata 实现与 ptrace_readdata 相反的功能,将长度为 Size 字节的本进程 data 地址开始的数据,写入目标进程 Dest 开始的内存。
实际调用的是 ptrace, 第一个参数为 PTRACE_POKETEXT
int ptrace_attach(pid_t pid)
{
if (ptrace(PTRACE_ATTACH, pid, NULL, ) < ) {
perror("ptrace_attach");
return -;
} int status = ;
waitpid(pid, &status , WUNTRACED); return ;
} int ptrace_detach(pid_t pid)
{
if (ptrace(PTRACE_DETACH, pid, NULL, ) < ) {
perror("ptrace_detach");
return -;
} return ;
}

ptrace_attach 简单封装 ptrace+PTRACE_ATTACH + waitpid 的调用,ptrace_detach 简单封装 ptrace+PTRACE_DETACH的调用

long ptrace_retval(struct pt_regs * regs) // 获取函数调用的返回值
{
#if defined(__arm__)
return regs->ARM_r0;
#elif defined(__i386__)
return regs->eax;
#else
#error "Not supported"
#endif
} long ptrace_ip(struct pt_regs * regs) //获取程序计数器
{
#if defined(__arm__)
return regs->ARM_pc;
#elif defined(__i386__)
return regs->eip;
#else
#error "Not supported"
#endif
} int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs)
{
DEBUG_PRINT("[+] Calling %s in target process.\n", func_name);
if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) == -)
return -; if (ptrace_getregs(target_pid, regs) == -)
return -;
DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x \n",
func_name, ptrace_retval(regs), ptrace_ip(regs));
return ;
}
ptrace_call_wrapper 封装了trace进程调用tracee进程内函数的方法,tracee进程内要调用的函数地址用参数 func_addr 存放,func_addr的参数和参数个数由 parameters 和 param_num 指定。
调用完后tracee进程的寄存器内存获取并存放在 regs 变量里。

下面先了解下 struct pt_regs 结构,

这个结构封装了需要在内核入口中保存的最少的状态信息,比如说每一次的系统调用、中断、陷阱、故障时,pt_regs结构中保存了最少的状态信息,是一个数组,为了方便使用,定义了一系列寄存器宏指向数组的某一项, 使用 ptrace+PTRACE_GETREGS 可以获取目标进程的寄存器值,以 struct pt_regs 变量返回。
pt_regs结构:

/*
* This struct defines the way the registers are stored on the
* stack during a system call. Note that sizeof(struct pt_regs)
* has to be a multiple of 8.
*/
#ifndef __KERNEL__
struct pt_regs {
long uregs[18];
};
#else /* __KERNEL__ */
struct pt_regs {
unsigned long uregs[18];
};
#endif /* __KERNEL__ */ #define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]

ptrace_getregs 的实现如下,

int ptrace_getregs(pid_t pid, struct pt_regs * regs)
{
if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < ) {
perror("ptrace_getregs: Can not get register values");
return -;
} return ;
} int ptrace_setregs(pid_t pid, struct pt_regs * regs)
{
if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < ) {
perror("ptrace_setregs: Can not set register values");
return -;
} return ;
} int ptrace_continue(pid_t pid)
{
if (ptrace(PTRACE_CONT, pid, NULL, ) < ) {
perror("ptrace_cont");
return -;
} return ;
}

ptrace_call 的实现如下:

#if defined(__arm__)
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)
{
uint32_t i;
for (i = ; i < num_params && i < ; i ++) { // 前面4个参数存放到寄存器里,pt_regs数组的 0,1,2,4 四个位置
regs->uregs[i] = params[i];
} //
if (i < num_params) { //多于4个的参数,存放在目标进程的栈里

regs->ARM_sp -= (num_params - i) * sizeof(long) ; // 栈顶指针 ARM_sp 往低地址移动剩余参数的地址数
                  ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long));// 使用ptrace_writedata向目标进程的栈                                                                               //写入剩余参数的值

        }

        regs->ARM_pc = addr;  // 要在目标进程调用的函数地址写入目标进程PC寄存器
if (regs->ARM_pc & ) { // 16位的 thumb 格式
/* thumb */
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK; // #define CPSR_T_MASK     ( 1u << 5 )
} else { // arm 格式
/* arm */
regs->ARM_cpsr &= ~CPSR_T_MASK;
} regs->ARM_lr = ; if (ptrace_setregs(pid, regs) == -1 // 将构造好的寄存器内容写入目标进程
|| ptrace_continue(pid) == -) { // 恢复目标进程的运行,目标进程将从上述pc寄存器即addr函数开始运行
printf("error\n");
return -;
} int stat = ;
waitpid(pid, &stat, WUNTRACED);
while (stat != 0xb7f) { // 这几句没看懂
if (ptrace_continue(pid) == -) {
printf("error\n");
return -;
}
waitpid(pid, &stat, WUNTRACED);
}
return ;
}
#endif
下面这个函数实现了获取目标进程加载的动态库内部函数的地址,与 adbi 的原理一致,都是利用函数与动态库加载进内存的起始地址的offset一致,来计算的,个人觉得 libinject 在实现同样的功能时代码给 adbi 写得更舒服,这也是研究各种源码的好处,有对比才有高低。
void* get_module_base(pid_t pid, const char* module_name)  // 这个函数获取动态库 module_name 加载在进程 pid 后的起始地址
{
FILE *fp;
long addr = ;
char *pch;
char filename[];
char line[]; if (pid < ) {
/* self process */
snprintf(filename, sizeof(filename), "/proc/self/maps", pid); // 同样是通过解析 maps 文件得到的
} else {
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
} fp = fopen(filename, "r"); if (fp != NULL) {
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name)) {
pch = strtok( line, "-" );
addr = strtoul( pch, NULL, ); if (addr == 0x8000)
addr = ; break;
}
} fclose(fp) ;
} return (void *)addr;
} void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr) // 这个函数获取目标进程内某个动态库函数的地址
{
void* local_handle, *remote_handle; local_handle = get_module_base(-, module_name);
remote_handle = get_module_base(target_pid, module_name); DEBUG_PRINT("[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle); void * ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);// 算法一致, local_addr - local_handle 得到 // offset, 然后再加上 remote_handle, 即得到目标进程的函数地址 return ret_addr;
}

以上函数基本上是helper函数,主要是封装了ptrace的调用实现一系列读写目标进程内存、寄存器的函数,并且封装了通过解析maps文件读取目标进程动态库里函数的地址的函数。

 

下面这个是 libinject2 的核心函数,实现了注入功能:

int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size)
{
int ret = -;
void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; // 存放目标进程相应函数的地址
void *local_handle, *remote_handle, *dlhandle;
uint8_t *map_base = ; // 存放目标进程mmap获取的内存块的地址
uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs;
struct pt_regs original_regs; uint32_t code_length;
long parameters[]; DEBUG_PRINT("[+] Injecting process: %d\n", target_pid); if (ptrace_attach(target_pid) == -) // 第一步: attach 到目标进程
goto exit; if (ptrace_getregs(target_pid, &regs) == -)
goto exit; /* save original registers */
memcpy(&original_regs, &regs, sizeof(regs)); // 第二步:保存目标进程被注入前的寄存器内容,方便注入完成后恢复 mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr); /* 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 if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, , &regs) == -)
goto exit; map_base = ptrace_retval(&regs); // 第三步,获取目标进程mmap调用的地址,并执行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 );
dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror ); DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); printf("library path = %s\n", library_path);
ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + );// 第四步,获取目标进程动态库的几个函数,并将要注入的so的路径写入刚刚申请的内存的初始地址 parameters[] = map_base;
parameters[] = RTLD_NOW| RTLD_GLOBAL; if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, , &regs) == -)
goto exit; void * sohandle = ptrace_retval(&regs); // 第五步,在目标进程内调用 dlopen函数加载要注入的 so ,这一步成功后,so已经被注入目标进程的地址空间内了 #define FUNCTION_NAME_ADDR_OFFSET 0x100
ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + );
parameters[] = sohandle;
parameters[] = map_base + FUNCTION_NAME_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, , &regs) == -)
goto exit; void * hook_entry_addr = ptrace_retval(&regs);
DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr); // 第六步,在目标进程内调用 dlsym函数获取刚刚注入的so里的hook函数 #define FUNCTION_PARAM_ADDR_OFFSET 0x200
ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + );
parameters[] = map_base + FUNCTION_PARAM_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, , &regs) == -)
goto exit; printf("Press enter to dlclose and detach\n"); // 第七步,在目标进程内调用hook函数
getchar();
parameters[] = sohandle; if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, , &regs) == -)
goto exit; /* restore */
ptrace_setregs(target_pid, &original_regs);
ptrace_detach(target_pid); // 第八步,恢复目标进程的寄存器,detach 退出对目标进程的 ptrace
ret = ; exit:
return ret;
}

最后是main函数,libinject2 只是注入了一个So到目标进程,并执行了so里的一个函数,还没有真正劫持目标进程的函数

int main(int argc, char** argv) {
pid_t target_pid;
//target_pid = find_pid_of("/system/bin/surfaceflinger");
target_pid = atoi(argv[]);
if (- == target_pid) {
printf("Can't find the process\n");
return -;
}
//target_pid = find_pid_of("/data/test");
inject_remote_process(target_pid, "/data/local/tmp/libhello.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!"));
return ;
}

完整的libinject.c:

#include <stdio.h>
#include <stdlib.h>
//#include <asm/user.h>
#include <asm/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>
#include <android/log.h> #define __arm__ 1 #if defined(__i386__)
//#define pt_regs user_regs_struct
#endif #define ENABLE_DEBUG 1 #if ENABLE_DEBUG
#define LOG_TAG "INJECT"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, fmt, ##args)
#define DEBUG_PRINT(format,args...) \
LOGD(format, ##args)
#else
#define DEBUG_PRINT(format,args...)
#endif #define CPSR_T_MASK ( 1u << 5 ) const char *libc_path = "/system/lib/libc.so";
const char *linker_path = "/system/bin/linker"; int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size)
{
uint32_t i, j, remain;
uint8_t *laddr; union u {
long val;
char chars[sizeof(long)];
} d; j = size / ;
remain = size % ; laddr = buf; for (i = ; i < j; i ++) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, src, );
memcpy(laddr, d.chars, );
src += ;
laddr += ;
} if (remain > ) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, src, );
memcpy(laddr, d.chars, remain);
} return ;
} int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size)
{
uint32_t i, j, remain;
uint8_t *laddr; union u {
long val;
char chars[sizeof(long)];
} d; j = size / ;
remain = size % ; laddr = data; for (i = ; i < j; i ++) {
memcpy(d.chars, laddr, );
ptrace(PTRACE_POKETEXT, pid, dest, d.val); dest += ;
laddr += ;
} if (remain > ) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, );
for (i = ; i < remain; i ++) {
d.chars[i] = *laddr ++;
} ptrace(PTRACE_POKETEXT, pid, dest, d.val);
} return ;
} #if defined(__arm__)
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)
{
uint32_t i;
for (i = ; i < num_params && i < ; i ++) {
regs->uregs[i] = params[i];
} //
// push remained params onto stack
//
if (i < num_params) {
regs->ARM_sp -= (num_params - i) * sizeof(long) ;
ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long));
} regs->ARM_pc = addr;
if (regs->ARM_pc & ) {
/* thumb */
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK;
} else {
/* arm */
regs->ARM_cpsr &= ~CPSR_T_MASK;
} regs->ARM_lr = ; if (ptrace_setregs(pid, regs) == -
|| ptrace_continue(pid) == -) {
printf("error\n");
return -;
} int stat = ;
waitpid(pid, &stat, WUNTRACED);
while (stat != 0xb7f) {
if (ptrace_continue(pid) == -) {
printf("error\n");
return -;
}
waitpid(pid, &stat, WUNTRACED);
} return ;
} #elif defined(__i386__)
#if 0
long ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct user_regs_struct * regs)
{
regs->esp -= (num_params) * sizeof(long) ;
ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long)); long tmp_addr = 0x00;
regs->esp -= sizeof(long);
ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr)); regs->eip = addr; if (ptrace_setregs(pid, regs) == -
|| ptrace_continue( pid) == -) {
printf("error\n");
return -;
} int stat = ;
waitpid(pid, &stat, WUNTRACED);
while (stat != 0xb7f) {
if (ptrace_continue(pid) == -) {
printf("error\n");
return -;
}
waitpid(pid, &stat, WUNTRACED);
} return ;
}
#endif
#else
#error "Not supported"
#endif int ptrace_getregs(pid_t pid, struct pt_regs * regs)
{
if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < ) {
perror("ptrace_getregs: Can not get register values");
return -;
} return ;
} int ptrace_setregs(pid_t pid, struct pt_regs * regs)
{
if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < ) {
perror("ptrace_setregs: Can not set register values");
return -;
} return ;
} int ptrace_continue(pid_t pid)
{
if (ptrace(PTRACE_CONT, pid, NULL, ) < ) {
perror("ptrace_cont");
return -;
} return ;
} int ptrace_attach(pid_t pid)
{
if (ptrace(PTRACE_ATTACH, pid, NULL, ) < ) {
perror("ptrace_attach");
return -;
} int status = ;
waitpid(pid, &status , WUNTRACED); return ;
} int ptrace_detach(pid_t pid)
{
if (ptrace(PTRACE_DETACH, pid, NULL, ) < ) {
perror("ptrace_detach");
return -;
} return ;
} void* get_module_base(pid_t pid, const char* module_name)
{
FILE *fp;
long addr = ;
char *pch;
char filename[];
char line[]; if (pid < ) {
/* self process */
snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
} else {
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
} fp = fopen(filename, "r"); if (fp != NULL) {
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name)) {
pch = strtok( line, "-" );
addr = strtoul( pch, NULL, ); if (addr == 0x8000)
addr = ; break;
}
} fclose(fp) ;
} return (void *)addr;
} void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
{
void* local_handle, *remote_handle; local_handle = get_module_base(-, module_name);
remote_handle = get_module_base(target_pid, module_name); DEBUG_PRINT("[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle); void * ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle); #if defined(__i386__)
if (!strcmp(module_name, libc_path)) {
ret_addr += ;
}
#endif
return ret_addr;
} int find_pid_of(const char *process_name)
{
int id;
pid_t pid = -;
DIR* dir;
FILE *fp;
char filename[];
char cmdline[]; struct dirent * entry; if (process_name == NULL)
return -; dir = opendir("/proc");
if (dir == NULL)
return -; while((entry = readdir(dir)) != NULL) {
id = atoi(entry->d_name);
if (id != ) {
sprintf(filename, "/proc/%d/cmdline", id);
fp = fopen(filename, "r");
if (fp) {
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp); if (strcmp(process_name, cmdline) == ) {
/* process found */
pid = id;
break;
}
}
}
} closedir(dir);
return pid;
} long ptrace_retval(struct pt_regs * regs)
{
#if defined(__arm__)
return regs->ARM_r0;
#elif defined(__i386__)
//return regs->eax;
#else
#error "Not supported"
#endif
} long ptrace_ip(struct pt_regs * regs)
{
#if defined(__arm__)
return regs->ARM_pc;
#elif defined(__i386__)
//return regs->eip;
#else
#error "Not supported"
#endif
} int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs)
{
DEBUG_PRINT("[+] Calling %s in target process.\n", func_name);
if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) == -)
return -; if (ptrace_getregs(target_pid, regs) == -)
return -;
DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x \n",
func_name, ptrace_retval(regs), ptrace_ip(regs));
return ;
} int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size)
{
int ret = -;
void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;
void *local_handle, *remote_handle, *dlhandle;
uint8_t *map_base = ;
uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs;
struct pt_regs original_regs;
extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
_dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
_saved_cpsr_s, _saved_r0_pc_s; uint32_t code_length;
long parameters[]; DEBUG_PRINT("[+] Injecting process: %d\n", target_pid); if (ptrace_attach(target_pid) == -){
DEBUG_PRINT("[+] ptrace_attach fail: %d\n", target_pid);
goto exit;
}
if (ptrace_getregs(target_pid, &regs) == -){
DEBUG_PRINT("[+] ptrace_getregs fail: %d\n", target_pid);
goto exit;
}
/* save original registers */
memcpy(&original_regs, &regs, sizeof(regs)); mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr); /* 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 if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, , &regs) == -){
DEBUG_PRINT("[+] ptrace_call_wrapper fail: %d\n", target_pid);
goto exit;
}
map_base = ptrace_retval(&regs); 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 );
dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror ); DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); printf("library path = %s\n", library_path);
ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + ); parameters[] = map_base;
parameters[] = RTLD_NOW| RTLD_GLOBAL; if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, , &regs) == -)
goto exit; void * sohandle = ptrace_retval(&regs);
if(NULL == sohandle) {
if (ptrace_call_wrapper(target_pid, "dlerror", dlerror_addr, parameters, , &regs) == -)
goto exit;
char * errstr = ptrace_retval(&regs);
uint8_t buf[]={};
ptrace_readdata(target_pid, errstr,buf,);
DEBUG_PRINT("[+] dlopen return error: %s\n", buf);
} #define FUNCTION_NAME_ADDR_OFFSET 0x100
ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + );
parameters[] = sohandle;
parameters[] = map_base + FUNCTION_NAME_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, , &regs) == -)
goto exit; void * hook_entry_addr = ptrace_retval(&regs);
DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr); #define FUNCTION_PARAM_ADDR_OFFSET 0x200
ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + );
parameters[] = map_base + FUNCTION_PARAM_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, , &regs) == -)
goto exit; // printf("Press enter to dlclose and detach\n");
printf("Press enter to detach\n");
getchar(); #if 0
parameters[] = sohandle;
if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, , &regs) == -)
goto exit;
#endif
/* restore */
ptrace_setregs(target_pid, &original_regs);
ptrace_detach(target_pid);
ret = ; exit:
return ret;
} #define HELPSTR "error usage: %s -p PID [-P PROCNAME] -l LIBNAME -f FUNCTION [-d (debug on)]\n" int main(int argc, char** argv) {
pid_t target_pid = -;
char *proc_name = NULL;
char *sodir = NULL;
char *func_name = NULL;
char *args = "";
int opt; while ((opt = getopt(argc, argv, "p:l:f:P:")) != -) {
switch (opt) {
case 'p':
target_pid = strtol(optarg, NULL, );
break;
case 'l':
sodir = strdup(optarg);
break;
case 'f':
func_name = strdup(optarg);
break;
case 'P':
proc_name = strdup(optarg);
break;
default:
fprintf(stderr,HELPSTR,argv[]);
exit();
}
} if(proc_name != NULL && target_pid < ) target_pid = find_pid_of(proc_name); if(target_pid < || NULL == sodir || NULL == func_name) {
fprintf(stderr,HELPSTR,argv[]);
exit();
} if(argc>) args=argv[]; inject_remote_process(target_pid, sodir, func_name, args, strlen(args));
return ;
}

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

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

    前面两篇 android hook 框架 libinject2 简介.编译.运行 android hook 框架 libinject2 如何实现so注入 实际运行并分析了 Android中的so注入( ...

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

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

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

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

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

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

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

    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. TI C6000 数据存储处理与性能优化

    存储器之于CPU好比仓库之于车间.车间加工过程中的原材料.半成品.成品等均需入出仓库,生产效率再快,如果仓库周转不善,也必然造成生产阻塞.如同仓库需要合理地规划管理一般,数据存储也需要恰当的处理技巧来 ...

  2. 14 Django的用户认证组件

    用户认证 auth模块 from django.contrib import auth django.contrib.auth中提供了许多方法,这里主要介绍其中的三个: 1.1 .authentica ...

  3. html+css调用服务器端字体

    在浏览网页时,由于客户端没有安装某些特殊字体,导致网页文字无法按设计正常显示,这时我们可以使用服务器字体来避免这种现象的发送 语法 @font-face { /* 自定义字体名称 */ font-fa ...

  4. java 1.7 新io 实践 NIO2

    Files 类使用 package com.xinyu.test; import java.io.IOException; import java.nio.ByteBuffer; import jav ...

  5. 云计算之路-阿里云上:愚人节被阿里云OCS愚

    今天是愚人节,而我们却被阿里云OCS愚,很多地方的缓存一直不过期,造成很多页面中的数据一直不更新.这篇博文将向您分享我们这两天遇到的OCS问题. 阿里云OCS(Open Cache Service)是 ...

  6. copyEvens

    public int[] copyEvens(int[] nums, int count) { int newIndex=0; int i=0; int newArray[] = new int[co ...

  7. android的5个进程等级

    一.Foreground Process(前台进程,优先级别最高) 1.正处于 Activity Resume() 状态 2.正处于与 bound 服务交互的状态 3.正处于服务在前台运行的状态(st ...

  8. 【Training versus Testing】林轩田机器学习基石

    接着上一讲留下的关子,机器学习是否可行与假设集合H的数量M的关系. 机器学习是否可行的两个关键点: 1. Ein(g)是否足够小(在训练集上的表现是否出色) 2. Eout(g)是否与Ein(g)足够 ...

  9. 孤荷凌寒自学python第五十八天成功使用python来连接上远端MongoDb数据库

    孤荷凌寒自学python第五十八天成功使用python来连接上远端MongoDb数据库 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第四天.今天的感觉是,mongoDB数据 ...

  10. 孤荷凌寒自学python第四十八天通用同一数据库中复制数据表函数最终完成

    孤荷凌寒自学python第四十八天通用同一数据库中复制数据表函数最终完成 (完整学习过程屏幕记录视频地址在文末) 今天继续建构自感觉用起来顺手些的自定义模块和类的代码. 今天经过反复折腾,最终基本上算 ...