前言
现目前的保护基本都是写在Native层了,那么我们想要Hack应用就必须Hack native。于是总结一下Android注入的几种方式和技巧。
正文
-
Ptrace往目标进程注入so模块,so模块的init_array段和JNI_OnLoad执行我们的代码,实现Hack目标进程。
整个项目的组成:- hack.c 注入源代码
- Android.mk
- Makefile
Android.mk
LOCAL_PATH := $(call my-dir)
TARGET_PIE := true
NDK_APP_PIE := true
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
hack.c
LOCAL_C_INCLUDE :=
LOCAL_MODULE := hack
LOCAL_MODULE_TAGS := optional
# Allow execution on android-16+
LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie
include $(BUILD_EXECUTABLE)
include $(call all-makefiles-under,$(LOCAL_PATH))
Makefile
LOCAL_ARM_MODE := armeabi x86
all: check build
check:
ifeq (, $(shell which ndk-build))
$(error "No 'ndk-build' in PATH, please install Android NDK and configure properly")
endif
build:
ndk-build APP_ABI="armeabi" NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
install:
adb push libs/armeabi/hack /data/local/tmp/
clean:
rm -rf *.c~
rm -rf *.h~
rm -rf obj/
具体实现:
- 注入进程,使用ptrace(PTRACE_ATTACH,pid,NULL,NULL);
int ptrace_attach(pid_t pid){
int status = ptrace(PTRACE_ATTACH,pid,NULL,NULL);
if(status < 0){
perror("[!] Attach failed");
return -1;
}
return 0;
}
2.读取寄存器,保留现场,detach之前恢复现场。
int ptrace_getrets(pid_t pid,struct pt_regs *regs){
int status = ptrace(PTRACE_GETREGS,pid,NULL,regs);
if(status < 0){
perror("[!] Getregs error");
return -1;
}
return 0;
}
3.获取重要函数地址,dlopen、mmap、dlsym等函数。(/system/lib/libdl.so) 函数地址等于函数在so库中的偏移加上加载的基地址
// get pid module base [libc.so]
void *get_module_base(pid_t pid, char* module_name){
char* base;
long addr = 0;
char filename[1024] = {0};
char line[1024];
char *filename_tem = "/proc/%d/maps";
sprintf(filename,filename_tem,pid);
FILE* fp = fopen(filename,"r");
if(fp != NULL){
while(fgets(line, sizeof(line), fp)){
if(strstr(line, module_name)){
base = strtok(line, "-");
addr = strtoul(base, NULL, 16);
// if(addr == 0x8000)
// addr = 0;
break;
}
}
fclose(fp);
}
return (void*)addr;
}
void get_offset(){
pid_t mypid = getpid();
void *base = get_module_base(mypid,"libc.so");
mmap_off = (void *)((void *)mmap - base);
void *libdl_base = get_module_base(mypid,"/system/lib/libdl.so");
dlopen_off = (void *)dlopen - libdl_base;
dlclose_off = (void *)dlclose - libdl_base;
dlsym_off = (void *)dlsym - libdl_base;
// DEBUG("[+] mmap_off == > 0x%x\n[+] dlopen_off == > 0x%x\n",mmap_off,dlopen_off);
// DEBUG("off == > %p %p %p %p\n",dlopen_off,dlclose_off,dlsym_off,mmap_off);
}
4.调用mmap函数分配内存,写入so的path。
5.调用dlopen函数实现inject。
完整注入代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/wait.h>
#define CPSR_T_MASK ( 1u << 5 )
#define DEBUG(format, ...) printf (format, ##__VA_ARGS__)
int mmap_off,dlopen_off,dlclose_off,dlsym_off;
#define RTLD_LOCAL 0
#define RTLD_LAZY 0x00001
#define RTLD_NOW 0x00002
#define RTLD_NOLOAD 0x00004
#define RTLD_GLOBAL 0x00100
#define RTLD_NODELETE 0x01000
// ptrace will stop the process
int ptrace_attach(pid_t pid){
int status = ptrace(PTRACE_ATTACH,pid,NULL,NULL);
waitpid(pid,&status,WUNTRACED);
if(status < 0){
perror("[!] Attach failed");
return -1;
}
return 0;
}
int ptrace_detach(pid_t pid){
int status = ptrace(PTRACE_DETACH,pid,NULL,NULL);
if(status < 0){
perror("[!] Detach failed");
return -1;
}
return 0;
}
int ptrace_continue(pid_t pid){
int status = ptrace(PTRACE_CONT,pid,NULL,NULL);
// DEBUG("ptrace continue status %d\n",status);
if(status < 0){
perror("[!] cont failed");
return -1;
}
return 0;
}
int ptrace_getrets(pid_t pid,struct pt_regs *regs){
int status = ptrace(PTRACE_GETREGS,pid,NULL,regs);
if(status < 0){
perror("[!] Getregs error");
return -1;
}
return 0;
}
int ptrace_setregs(pid_t pid, struct pt_regs *regs){
int status = ptrace(PTRACE_SETREGS,pid,NULL,regs);
if(status < 0){
perror("[!] set regs error");
return -1;
}
return 0;
}
pid_t find_pid_of_package(const char *packname){
char cmd_buf[1024] = {0};
const char *cmd = "ps -ef | grep %s | grep -v grep | head -n1 | awk '{print $2}'";
sprintf(cmd_buf,cmd,packname);
DEBUG("[!] Execute Command \n");
char result[10240] = {0};
char buf[1024] = {0};
FILE *fp = NULL;
if( (fp = popen(cmd_buf, "r")) == NULL ) {
printf("popen error!\n");
return -1;
}
while (fgets(buf, sizeof(buf), fp)) {
strcat(result, buf);
}
pclose(fp);
return atoi(result);
}
// get pid module base [libc.so]
void *get_module_base(pid_t pid, char* module_name){
char* base;
long addr = 0;
char filename[1024] = {0};
char line[1024];
char *filename_tem = "/proc/%d/maps";
sprintf(filename,filename_tem,pid);
FILE* fp = fopen(filename,"r");
if(fp != NULL){
while(fgets(line, sizeof(line), fp)){
if(strstr(line, module_name)){
base = strtok(line, "-");
addr = strtoul(base, NULL, 16);
// if(addr == 0x8000)
// addr = 0;
break;
}
}
fclose(fp);
}
return (void*)addr;
}
void get_offset(){
pid_t mypid = getpid();
void *base = get_module_base(mypid,"libc.so");
mmap_off = (void *)mmap - base;
void *libdl_base = get_module_base(mypid,"/system/lib/libdl.so");
dlopen_off = (void *)dlopen - libdl_base;
dlclose_off = (void *)dlclose - libdl_base;
dlsym_off = (void *)dlsym - libdl_base;
// DEBUG("[+] mmap_off == > 0x%x\n[+] dlopen_off == > 0x%x\n",mmap_off,dlopen_off);
// DEBUG("off == > %p %p %p %p\n",dlopen_off,dlclose_off,dlsym_off,mmap_off);
}
void log_regs(struct pt_regs *regs){
DEBUG("r0 : 0x%08x\n",regs->ARM_r0);
DEBUG("r1 : 0x%08x\n",regs->ARM_r1);
DEBUG("r2 : 0x%08x\n",regs->ARM_r2);
DEBUG("r3 : 0x%08x\n",regs->ARM_r3);
DEBUG("r4 : 0x%08x\n",regs->ARM_r4);
DEBUG("r5 : 0x%08x\n",regs->ARM_r5);
DEBUG("r6 : 0x%08x\n",regs->ARM_r6);
DEBUG("r7 : 0x%08x\n",regs->ARM_r7);
DEBUG("r8 : 0x%08x\n",regs->ARM_r8);
DEBUG("r9 : 0x%08x\n",regs->ARM_r9);
DEBUG("r10 : 0x%08x\n",regs->ARM_r10);
DEBUG("r11 : 0x%08x\n",regs->ARM_fp);
DEBUG("r12 : 0x%08x\n",regs->ARM_ip);
DEBUG("sp : 0x%08x\n",regs->ARM_sp);
DEBUG("lr : 0x%08x\n",regs->ARM_lr);
DEBUG("pc : 0x%08x\n",regs->ARM_pc);
}
u_int32_t *ptrace_getdata(pid_t pid , u_int8_t *addr , u_int8_t size){
u_int8_t times = size / 4;
uint32_t read_data[size];
int i = 0;
while(i < times){
// 每次读取word
uint32_t data = ptrace(PTRACE_PEEKDATA,pid,addr + i * 4,NULL);
read_data[i++] = data;
}
// for(int i = 0;)
return read_data;
}
unsigned int ptrace_writedata(pid_t pid,u_int8_t *addr , u_int32_t *bufaddr, u_int8_t size){
u_int8_t times = size / 4;
int i = 0;
while(i < times){
int status = ptrace(PTRACE_POKETEXT , pid , addr + i * 4 , *(bufaddr + i));
// printf("[-] status == > %d\n",status);
if(status < 0){
perror("error write");
return -1;
}
i++;
}
int remain = size % 4;
if(remain){
ptrace(PTRACE_POKETEXT,pid,addr + i * 4, *(bufaddr +i));
}
return 0;
}
int ptrace_call(pid_t pid, long *addr, long *params, int paramlen, struct pt_regs* regs){
uint32_t i;
// put the first 4 parameters into r0-r3
for ( i = 0; i < paramlen && i < 4; i ++ ){
regs->uregs[i] = params[i];
}
// push remained params into stack
if ( i < paramlen ){
regs->ARM_sp -= (paramlen - i) * sizeof(long) ; //倒序存放参数
ptrace_writedata(pid, regs->ARM_sp, ¶ms[i], (paramlen - i) * sizeof(long));
}
// set the pc to func <e.g: mmap> that will be executed
regs->ARM_pc = addr;
if ( regs->ARM_pc & 1 ){
/* thumb */
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK;
}
else{
/* arm */
regs->ARM_cpsr &= ~CPSR_T_MASK;
}
// when finish this func, pid will stop
regs->ARM_lr = 0;
// log_regs(regs);
// set the regsister and start to execute
if ( ptrace_setregs( pid, regs ) == -1
|| ptrace_continue( pid ) == -1 ){
return -1;
}
// wait pid finish work and stop
int status = 0;
// father 接管子进程的信号 lr为0时,为signal 11
waitpid(pid, &status, WUNTRACED );
DEBUG("[+] Invoke Function Finish\n");
return 0;
}
void inject(pid_t pid){
struct pt_regs ori_reg; // backup
struct pt_regs ori_reg_cont; // for continue
struct pt_regs regs;// new regs
ptrace_attach(pid);
ptrace_getrets(pid, &ori_reg);
ori_reg_cont = ori_reg;
get_offset();
void *dlopen_remote,*dlclose_remote,*dlsym_remote,*mmap_remote; // the application have been attached real
void *remote_libc_base = get_module_base(pid,"/system/lib/libc.so");
DEBUG("[+] remote libc base = = > %p\n",remote_libc_base);
void *remote_linker_base = get_module_base(pid,"/system/lib/libdl.so"); //
dlopen_remote = remote_linker_base + dlopen_off;
dlclose_remote = remote_linker_base + dlclose_off;
dlsym_remote = remote_linker_base + dlsym_off;
mmap_remote = remote_libc_base + mmap_off;
// log_regs(&ori_reg);
// DEBUG("[+] remote dlopen == > %p\n[+] remote dlclose == > %p\n[+] remote dlsym == > %p\n[+] remote mmap == > %p\n",dlopen_remote,dlclose_remote,dlsym_remote,mmap_remote);
long parameters[10] ;
parameters[0] = 0x12345678; // addr
parameters[1] = 0x4000; // size
parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
parameters[4] = 0; //fd
parameters[5] = 0; //offset
ptrace_call(pid,mmap_remote,parameters,6, &ori_reg); //call mmap
long *sopath = "/system/lib/libnative-lib.so"; // path需要注意可能需要系统 /data/local/tmp无效
ptrace_writedata(pid,0x12345678,sopath,strlen(sopath));
long dlopen_params[2] = {0x12345678,RTLD_LAZY};
ptrace_call(pid,dlopen_remote,dlopen_params,2,&ori_reg);
// 完成注入
ptrace_setregs(pid,&ori_reg_cont);
// getchar();
ptrace_detach(pid); // last step
}
int main(){
pid_t app_pid = find_pid_of_package("'com.umetrip.android.msky.app'");
DEBUG("[+] Detect pid %d\n",app_pid);
inject(app_pid);
}
-
zygote注入:
主要原理其实还是ptrace注入,因为安卓的应用进程是zygote fork来的。所以只要往zygote注入我们的模块,其fork的进程中自然而然就会存在我们注入的模块。
可以看到新建的进程中也存在了libnative-lib.so模块。