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. Echarts 解决饼图文字过长重叠的问题

    之前在网上查找了很多关于解决饼图文字描述过长导致重叠的问题,找了很多一直没有一个合适的解决方案,最后自己只能花时间研究echarts文档,功夫不负有心人,终于解决了文字重叠展示不全等问题. 废话不多说 ...

  2. Ubuntu 16.04上安装并配置Postfix作为只发送SMTP服务器

    如果大家已经在使用第三方邮件服务方案发送并收取邮件,则无需运行自己的邮件服务器.然而,如果大家管理一套云服务器,且其中安装的应用需要发送邮件通知,那么运行一套本地只发送SMTP服务器则更为理想. 如何 ...

  3. saltstack plug in

    目录 可插拔的子系统 灵活性 虚拟模块 salt的核心架构提供了一种高速的交流总线,在核心架构的上层,salt暴露出来的特征是:松散耦合,可插拔的子系统. 可插拔的子系统 salt包含20中插件系统, ...

  4. FCS校验 C语言简单实现

    static uint8 calcFCS(uint8 *pBuf, uint8 len){  uint8 rtrn = 0;  while (len--)  {    rtrn ^= *pBuf++; ...

  5. 12 KLT算法

    1 去除多余模块的 #-*- coding:utf-8 -*- ''' Lucas-Kanade tracker ==================== Lucas-Kanade sparse op ...

  6. RegisterWindowMessage

    RegisterWindowMessage function   Defines a new window message that is guaranteed to be unique throug ...

  7. 《Cracking the Coding Interview》——第5章:位操作——题目1

    2014-03-19 05:45 题目:给定两个数M和N,将N按照二进制位,覆盖到M的特定段位中去. 解法:位操作,请看代码. 代码: // 5.1 Insert one number into th ...

  8. 天性 & 如水一般,遇强则强 —— 梦想、行为、性格

    开篇声明,我博客中“小心情”这一系列,全都是日记啊随笔啊什么乱七八糟的.如果一不小心点进来了,不妨直接关掉.我自己曾经写过一段时间的日记,常常翻看,毫无疑问我的文笔是很差的,而且心情也是瞬息万变的.因 ...

  9. 《算法》C++代码 前言

    现在大二正在上<数据结构>课,课内的书上代码实现很喜欢无脑用类.变量名字很长,而且常常实现太繁琐,并且代码有些无法运行,这些对于老手无所谓,但初学者看起来却会很不舒服.因此写点自己的代码, ...

  10. WPF and Silverlight.ComboBox 如何通过 Binding IsDropDownOpen 实现下拉菜单展开

    In the WPF example the Popup and the ToggleButton (the arrow on the right) are bound with the proper ...