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. K-均值聚类——电影类型

    K-均值聚类 K-均值算法试图将一系列样本分割成K个不同的类簇(其中K是模型的输入参数),其形式化的目标函数称为类簇内的方差和(within cluster sum of squared errors ...

  2. P1182 数列分段Section II

    P1182 数列分段Section II 题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小. 关于最大值最小: 例如一数列4 2 ...

  3. Entity FrameWork和Dapper的使用

    EF是微软系列下的更正苗红的重量级的ORM框架,功能强大,操作数据库的时候几乎不用写sql,可以像写C#代码一样操作数据库,尤其支持多表关联操作的时候极为方便,但是生成的sql语句性能很差,实在不敢恭 ...

  4. Qsys配置生成nios系统模块

    1. 本次使用的是别人写好的例程,主要研究学习,使用quartus 11打开工程 2. bdf文件是块编辑器的,相当于原理图,以前只在用NIOS的时候会用到这种方式.接下来新建一个工程,添加原理图元件 ...

  5. Eclipse 窗口说明---Eclipse教程第03课

    Eclipse 工作台(Workbench) 首先,让我们来看一下Eclipse 作台用户界面,和它里面的各种组件. 工作台是多个窗口的集合.每个窗口包含菜单栏,工具栏,快捷方式栏,以及一个或者多个透 ...

  6. laravel5.5容器

    目录 1. 比较典型的例子就是 cache 缓存 2. 容器顾名思义,其实就是完成存取过程 2.1 绑定过程 简单绑定 绑定单例 绑定实例 绑定初始数据 2.2 解析过程 容器主要是为了实现控制反转, ...

  7. 这是我见过最厉害的--智能代码生成器、html+js+底层+sql全都有、瓦特平台

    1:直接上图.图片有点多.我就没全部上传了. (demo.使用方法.数据库bak)下载:http://pan.baidu.com/s/1ntE5bDn 起源: 之前有好多人问我代码生成器的源码.我发了 ...

  8. Kotlin的数据类:节省很多行代码(KAD 10)

    作者:Antonio Leiva 时间:Jan 25, 2017 原文链接:https://antonioleiva.com/data-classes-kotlin/ 在前面的文章中,我们已经见到了类 ...

  9. Python 高级 I/O 多路复用

    Table of Contents 前言 select selectors 结语 参考链接 前言 第一次接触和 I/O 多路复用相关的概念是在书 CSAPP1 的并发编程章节,当时在了解了这个概念后只 ...

  10. 1.0 python-client以及ui自动化介绍

     appium的client-----捕获元素和对元素进行操作都是在client里面去写脚本实现的,client会将你写的python脚本发送到appium server上,然后appium serv ...