android hook 框架 libinject2 如何实现so注入
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 系统调用。
这个库首先对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 *)¶ms[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, ®s) == -)
goto exit; /* save original registers */
memcpy(&original_regs, ®s, 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, , ®s) == -)
goto exit; map_base = ptrace_retval(®s); // 第三步,获取目标进程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, , ®s) == -)
goto exit; void * sohandle = ptrace_retval(®s); // 第五步,在目标进程内调用 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, , ®s) == -)
goto exit; void * hook_entry_addr = ptrace_retval(®s);
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, , ®s) == -)
goto exit; printf("Press enter to dlclose and detach\n"); // 第七步,在目标进程内调用hook函数
getchar();
parameters[] = sohandle; if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, , ®s) == -)
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 *)¶ms[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, ®s) == -){
DEBUG_PRINT("[+] ptrace_getregs fail: %d\n", target_pid);
goto exit;
}
/* save original registers */
memcpy(&original_regs, ®s, 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, , ®s) == -){
DEBUG_PRINT("[+] ptrace_call_wrapper fail: %d\n", target_pid);
goto exit;
}
map_base = ptrace_retval(®s); 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, , ®s) == -)
goto exit; void * sohandle = ptrace_retval(®s);
if(NULL == sohandle) {
if (ptrace_call_wrapper(target_pid, "dlerror", dlerror_addr, parameters, , ®s) == -)
goto exit;
char * errstr = ptrace_retval(®s);
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, , ®s) == -)
goto exit; void * hook_entry_addr = ptrace_retval(®s);
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, , ®s) == -)
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, , ®s) == -)
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注入的更多相关文章
- android hook 框架 libinject 如何实现so注入
前面两篇 android hook 框架 libinject2 简介.编译.运行 android hook 框架 libinject2 如何实现so注入 实际运行并分析了 Android中的so注入( ...
- Android Hook框架adbi的分析(1)---注入工具hijack
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74055505 一.Android Hook框架adbi的基本介绍 adbi是And ...
- android hook 框架 libinject2 简介、编译、运行
Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2 如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...
- android hook 框架 ADBI 如何实现so注入
Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2 如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...
- android hook 框架 ADBI 如何实现dalvik函数挂钩
Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2 如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...
- Android Hook框架adbi源码浅析(一)
adbi(The Android Dynamic Binary Instrumentation Toolkit)是一个Android平台通用hook框架,基于动态库注入与inline hook技术实现 ...
- Android Hook框架adbi的分析(2)--- inline Hook的实现
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/74452308 一. Android Hook框架adbi源码中inline Hoo ...
- Android Hook框架adbi的分析(3)---编译和inline Hook实践
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/75200800 一.序言 在前面的博客中,已经分析过了Android Hook框架a ...
- android hook 框架 xposed 如何实现注入
Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2 如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...
随机推荐
- K-均值聚类——电影类型
K-均值聚类 K-均值算法试图将一系列样本分割成K个不同的类簇(其中K是模型的输入参数),其形式化的目标函数称为类簇内的方差和(within cluster sum of squared errors ...
- P1182 数列分段Section II
P1182 数列分段Section II 题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小. 关于最大值最小: 例如一数列4 2 ...
- Entity FrameWork和Dapper的使用
EF是微软系列下的更正苗红的重量级的ORM框架,功能强大,操作数据库的时候几乎不用写sql,可以像写C#代码一样操作数据库,尤其支持多表关联操作的时候极为方便,但是生成的sql语句性能很差,实在不敢恭 ...
- Qsys配置生成nios系统模块
1. 本次使用的是别人写好的例程,主要研究学习,使用quartus 11打开工程 2. bdf文件是块编辑器的,相当于原理图,以前只在用NIOS的时候会用到这种方式.接下来新建一个工程,添加原理图元件 ...
- Eclipse 窗口说明---Eclipse教程第03课
Eclipse 工作台(Workbench) 首先,让我们来看一下Eclipse 作台用户界面,和它里面的各种组件. 工作台是多个窗口的集合.每个窗口包含菜单栏,工具栏,快捷方式栏,以及一个或者多个透 ...
- laravel5.5容器
目录 1. 比较典型的例子就是 cache 缓存 2. 容器顾名思义,其实就是完成存取过程 2.1 绑定过程 简单绑定 绑定单例 绑定实例 绑定初始数据 2.2 解析过程 容器主要是为了实现控制反转, ...
- 这是我见过最厉害的--智能代码生成器、html+js+底层+sql全都有、瓦特平台
1:直接上图.图片有点多.我就没全部上传了. (demo.使用方法.数据库bak)下载:http://pan.baidu.com/s/1ntE5bDn 起源: 之前有好多人问我代码生成器的源码.我发了 ...
- Kotlin的数据类:节省很多行代码(KAD 10)
作者:Antonio Leiva 时间:Jan 25, 2017 原文链接:https://antonioleiva.com/data-classes-kotlin/ 在前面的文章中,我们已经见到了类 ...
- Python 高级 I/O 多路复用
Table of Contents 前言 select selectors 结语 参考链接 前言 第一次接触和 I/O 多路复用相关的概念是在书 CSAPP1 的并发编程章节,当时在了解了这个概念后只 ...
- 1.0 python-client以及ui自动化介绍
appium的client-----捕获元素和对元素进行操作都是在client里面去写脚本实现的,client会将你写的python脚本发送到appium server上,然后appium serv ...