android inject (三) 跨进程注入so

前两篇主要说了一些基本内容,如pid的获取,都是查询后手动输入的,本片介绍注入第三方so到目标进程并执行so的方法。

前两篇的地址:android inject(一)ptrace基础   android inject (二) 跨进程注入

/*
 * 注入so到进程
 * 1.获取目标进程pid
 * 2.附加目标进程
 * 3.获取/保存目标进程寄存器的内容
 * 4.计算mmap方法的地址offset
 * 5.调用mmap方法,映射一块内存(更改本地寄存器的值,更改后赋值到目标寄存器,其目的是装注入so的路径,便于dlopen调用)
 * 6.计算 dlopen、dlsym、dlclose方法的地址
 * 7.分别调用上三个方法来加载so/获取so方法地址/释放so
 * 8.恢复上下文
 */
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/ptrace.h>
//#include <asm/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <android/log.h>

#define ENABLE_DEBUG 0

#define PTRACE_PEEKTEXT 1
#define PTRACE_POKETEXT 4
#define PTRACE_ATTACH	16
#define PTRACE_GETREGS 12
#define PTRACE_CONT 	7
#define PTRACE_DETACH   17
#define PTRACE_SYSCALL	24
#define CPSR_T_MASK		( 1u << 5 )

#if defined(__aarch64__)
const char *PATH_LIBC_SO = "/system/lib64/libc.so";
const char *PATH_LINKER = "/system/bin/linker64";
#else
const char *PATH_LIBC_SO = "/system/lib/libc.so";
const char *PATH_LINKER = "/system/bin/linker";
#endif

//const char *PATH_LINKER = "/system/bin/linker";
//const char *PATH_LIBC_SO = "/system/lib/libc.so";
const char* PATH_INJECT_SO = "/data/local/tmp/libso.so";

// 1.获取目标进程pid
int getRemotePid(const char* pack) {
	int remotePid = 0;
	int resultRemotePid = 0;
	struct dirent* entry = NULL;
	DIR* dir = opendir("/proc");
	char filename[1024];
	char cmdline[1024];
	FILE* fp = NULL;
	if (NULL == dir) {
		return -1;
	}

	while (NULL != (entry = readdir(dir))) {
		remotePid = atoi(entry->d_name);
		if (0 != remotePid) {
			snprintf(filename, 1024, "/proc/%d/cmdline", remotePid);
			fp = fopen(filename, "r");
			if (fp) {
				fgets(cmdline, sizeof(cmdline), fp);
				fclose(fp);
				if (0 == (strncmp(pack, cmdline, strlen(pack)))
						&& strlen(pack) == strlen(cmdline)) {
					printf("	cmdline:%s\n", cmdline);
					resultRemotePid = remotePid;
					break;
				}
			}
		}
	}
	closedir(dir);
	return resultRemotePid;
}

// 2.附加目标进程
int ptrace_attach(int pid) {
	int status;
	//附加 pid 进程
	if (0 > ptrace(PTRACE_ATTACH, pid, NULL, 0)) {
		return -1;
	}

	//等待附加进程停止
	waitpid((pid_t) pid, &status, WUNTRACED);

	return 0;
}

// 3.获取目标进程寄存器的值
int ptrace_getregs(int pid, struct pt_regs* regs) {
	if (0 > ptrace(PTRACE_GETREGS, pid, NULL, regs)) {
		printf("	***************ptrace_getregs error!");
		return -1;
	}
	return 0;
}

// 4.计算mmap地址offset
// 计算 pid 进程中 methodAdr 的偏移
void* getRemoteFuncAddr(int pid, const char* soName, void* selfMethodAddr) {
	int copyPid = -1;
	char mapsFile[1024] = { 0 };
	char mapsLineData[1024] = { 0 };
	unsigned long soAddr = 0;
	unsigned long selfSoAddr = 0;
	unsigned long remoteSoAddr = 0;
	FILE* fp = NULL;
	for (;;) {
		if (-1 == copyPid) {
			snprintf(mapsFile, sizeof(mapsFile), "/proc/self/maps");
		} else {
			snprintf(mapsFile, sizeof(mapsFile), "/proc/%d/maps", copyPid);
		}
		fp = fopen(mapsFile, "r");
		if (NULL != fp) {
			while (fgets(mapsLineData, sizeof(mapsLineData), fp)) {
				if (strstr(mapsLineData, soName)) {
					char* addr = strtok(mapsLineData, "-");
					soAddr = strtoul(addr, NULL, 16);
					printf(
							"	getRemoteFuncAddr_ mapsFile:%s ,mapsLineData:%s ,soName:%s ,soAddr:%ld\n",
							mapsFile, mapsLineData, soName, soAddr);
					if (0x8000 == soAddr) {
						soAddr = 0;
					}
					break;
				}
			}
		}
		fclose(fp);
		if (-1 == copyPid) {
			printf("	getRemoteFuncAddr_ pid:%d , soAddr:%p\n", copyPid,
					(void*) soAddr);
			copyPid = pid;
			selfSoAddr = soAddr;
		} else {
			printf("	getRemoteFuncAddr_ pid:%d , soAddr:%p\n", copyPid,
					(void*) soAddr);
			remoteSoAddr = soAddr;
			break;
		}
	}
	long resultAddr = ((long) selfMethodAddr - (long) selfSoAddr
			+ (long) remoteSoAddr);
	printf("	getRemoteFuncAddr_ resultAddr:%p\n\n", (void*) resultAddr);
	return (void*) resultAddr;
}

// 更改addr地址的值为data
int ptrace_poketext(int pid, void* addr, void* data, int paramLen) {
	printf("	ptrace_poketext paramLen:%d\n", paramLen);
	int writeNum, i, remain;
	int writeLen = sizeof(long);
	printf("	ptrace_poketext writeLen:%d\n", writeLen);
	writeNum = paramLen / sizeof(long);
	printf("	ptrace_poketext writeNum:%d\n", writeNum);
	remain = paramLen % writeLen;
	printf("	ptrace_poketext remain:%d\n", remain);
	if (0 != remain) {
		writeNum += 1;
	}
//	uint8_t* ldata = (uint8_t*) data;
//	uint8_t* ldest = (uint8_t*) addr;
	union u {
		long val;
		char chars[sizeof(long)];
	} d;
	uint8_t *laddr;
	for (i = 0; i < writeNum; i++) {
		if (remain && i == writeNum - 1) {
			d.val = ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
			for (i = 0; i < remain; i++) {
//				laddr = (uint8_t*) data;
//				d.chars[i] = *laddr++;
				d.chars[i] = ((*(uint8_t*) data))++;
//				d.chars[i] = *((uint8_t*) data) + 1;
			}
			ptrace(PTRACE_POKETEXT, pid, addr, d.val);
			printf("	ptrace_poketext write>4:%d\n", i);
			break;
			//此处可能有bug,如 memcpy拷贝一个字节,poketext拷贝4个字节,后三个字节是多少? 不变 还是 清零 还是d内存后面的三个字节?
//			memcpy(d.chars, data, remain);
//			ptrace(PTRACE_POKETEXT, pid, addr, d.val);
		} else {
			memcpy(d.chars, data, writeLen);
			//printf("	ptrace_poketext data:%s\n", &data);
			ptrace(PTRACE_POKETEXT, pid, addr, d.val);
//			dest += writeLen;
			addr = ((uint8_t*) addr) + writeLen;
			data = ((uint8_t*) data) + writeLen;
			printf("	ptrace_poketext addr:%p\n", addr);
			printf("	ptrace_poketext data:%p\n", data);
		}
	}
	return 0;
}

//修改pid进程寄存器
int ptrace_setregs(int pid, struct pt_regs* regs) {
	if (0 > ptrace(PTRACE_SETREGS, pid, NULL, regs)) {
		return -1;
	}
	return 0;
}

//激活目标进程
int ptrace_cont(int pid) {
	if (0 > ptrace(PTRACE_CONT, pid, NULL, 0)) {
		printf("	***************ptrace_cont error");
		return -1;
	}
	return 0;
}

// 5.调用pid进程的methodAddr方法,参数是params
int ptrace_call(int pid, void* methodAddr, long* params, long paramNum,
		struct pt_regs* regs) {
	int i;
	for (i = 0; i < paramNum; i++) {
		if (i < 4) {
			printf("	ptrace_call params[%d]:%p\n", i, (void*) params[i]);
			regs->uregs[i] = params[i];
		} else {
			//sp开辟空间
			regs->ARM_sp-= sizeof(long) * (paramNum - i);
			ptrace_poketext(pid, (void *) regs->ARM_sp, &params[i],
					sizeof(long) * (paramNum - i));
			break;
		}
	}
//设置pc
	regs->ARM_pc= (long) methodAddr;
//pc指令集判断32 and 16
	if (regs->ARM_pc& 1) {
		//thumb
		regs->ARM_pc &= (~1u);
		regs->ARM_cpsr |= CPSR_T_MASK;
	} else {
		//arm
		regs->ARM_cpsr &= ~CPSR_T_MASK;
	}

	regs->ARM_lr= 0;
//以上是修改regs的内容

	if (0 > ptrace_setregs(pid, regs) || 0 > ptrace_cont(pid)) {
		return -1;
	}
	int status = 0;

//ptrace_cont激活了附加进程, 这里等待附加进程停止
	waitpid(pid, &status, WUNTRACED);

	while (status != 0xb7f) {
		if (ptrace_cont(pid) == -1) {
			printf("	***************ptrace_call error\n");
			return -1;
		}
		waitpid(pid, &status, WUNTRACED);
	}

	return 0;
}

int ptrace_getResult(struct pt_regs * regs) {
	return regs->ARM_r0;
}

// 1.调用method方法并获取返回值
int ptrace_callMethod(int pid, void* methodAddr, long* params, long paramNum,
		struct pt_regs* regs) {

	// 调用methodAddr
	if (0 == ptrace_call(pid, methodAddr, params, paramNum, regs)) {
		printf("	ptrace_callMethod ptrace_call ok\n");
	} else {
		printf("	**********************ptrace_callMethod ptrace_call error\n");
		return -1;
	}
	// 获取调用methodAddr后的寄存器
	if (0 == ptrace_getregs(pid, regs)) {
		printf("	ptrace_callMethod ptrace_getregs ok\n");
	} else {
		printf(
				"	**********************ptrace_callMethod ptrace_getregs error\n");
		return -1;
	}

	return 0;
}

void* ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size) {
	long i, j, remain;
	uint8_t *laddr;
	size_t bytes_width = sizeof(long);

	union u {
		long val;
		char chars[sizeof(long)]; //[bytes_width];
	} d;

	j = size / bytes_width;
	remain = size % bytes_width;

	laddr = buf;

	for (i = 0; i < j; i++) {
		d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
		memcpy(laddr, d.chars, bytes_width);
		src += bytes_width;
		laddr += bytes_width;
	}

	if (remain > 0) {
		d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
		memcpy(laddr, d.chars, remain);
	}
	return laddr;
}

//去掉附加
int ptrace_detach(pid_t pid) {
	if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
		perror("ptrace_detach");
		return -1;
	}

	return 0;
}

void printfRemoteStr(int pid, void* addr, int len) {
	union u {
		long val;
		char chars[sizeof(long)];
	} d;
	char* chars = (char*) malloc(len);
	int i = 0;
	int j = len / sizeof(long);
	int x = len % sizeof(long);
	printf("****************printfRemoteStr_ addr:%p ; len:%d\n", addr, len);
	for (i = 0; i < j; i++) {
		d.val = ptrace(PTRACE_PEEKTEXT, pid, addr, d.chars);
		memcpy(chars, d.chars, 4);
		chars += sizeof(long);
		addr = ((uint8_t*) addr) + sizeof(long);
	}
	printf("****************printfRemoteStr_ addr:%p ; chars:%s\n", addr,
			chars - len + x);
}

//=============================================

int main() {
//	com.example.test_android_camera
//	com.tencent.mm
//	com.wuba.zhuanzhuan
//	com.taobao.taobao
	const char* pack = "com.wuba.zhuanzhuan";
	const char* injectSoMethodName = "InjectInterface";
	const char* injectSoMethodParams = "InjectInterface";
	struct pt_regs regs, OriginalRegs;
	// 1.get remote pid
	int pid = getRemotePid(pack);
	printf("1 getRemotePid pack:%s , pid:%d ok\n", pack, pid);

	// 2.attach pc
	if (0 == ptrace_attach(pid)) {
		printf("2 ptrace_attach %d ok\n", pid);
	}

	// 3.get remote regs
	if (0 == ptrace_getregs(pid, &regs)) {
		printf("3 ptrace_getregs %d ok\n", pid);
	}
	// 3.1.save regs
	memcpy(&OriginalRegs, &regs, sizeof(regs));

	// 4.get remote mmap method addr
	void* remoteMmapAddr = getRemoteFuncAddr(pid, PATH_LIBC_SO, (void *) mmap);
	printf("4 getRemoteFuncAddr mmap: %p ok\n", remoteMmapAddr);

	//set mmap method params
	long params[6];
	params[0] = 0;  // addr
	params[1] = 0x4000; // size
	params[2] = PROT_READ | PROT_WRITE | PROT_EXEC;  // prot
	params[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
	params[4] = 0; //fd
	params[5] = 0; //offset

	// 5.call mmap method, new memory
	if (0 == ptrace_callMethod(pid, remoteMmapAddr, params, 6, &regs)) {
		printf("5 ptrace_callMethod mmap ok\n");
	}

	void* newMemBase;
	//get new memory Addr
	if (!(newMemBase = (void*) (&regs)->ARM_r0))
	return -1;
	printf("mmap newMemAddr:%p\n", newMemBase);

	//init newMem
//	if (0 > ptrace_poketext(pid, newMemBase, (void*) "0", 0x400))
//		return -1;
//	printf("init newMem");

	//write soPath to mem
	if (0
			> ptrace_poketext(pid, newMemBase, (void*) PATH_INJECT_SO,
					strlen(PATH_INJECT_SO)))
		return -1;
	printf("ptrace_poketext PATH_INJECT_SO data write ok\n");
	printf("mmap newMemAddr:%p\n", newMemBase);
	printfRemoteStr(pid, newMemBase, strlen(PATH_INJECT_SO) + 1);
	printf("mmap newMemAddr:%p\n", newMemBase);

	//6.get remote dlopen、dlsym、dlclose method addr
	void* remoteDlopenAddr = getRemoteFuncAddr(pid, PATH_LINKER,
			(void *) dlopen);
	void* remoteDlsymAddr = getRemoteFuncAddr(pid, PATH_LINKER, (void *) dlsym);
	void* remoteDlcloseAddr = getRemoteFuncAddr(pid, PATH_LINKER,
			(void *) dlclose);
	void* remoteDlerror_addr = getRemoteFuncAddr(pid, PATH_LINKER,
			(void *) dlerror);
	printf(
			"get remote methodList: dlopenAddr:%p, dlsymAddr:%p, dlcloseAddr:%p, dlerrorAddr:%p\n",
			remoteDlopenAddr, remoteDlsymAddr, remoteDlcloseAddr,
			remoteDlerror_addr);
	params[0] = (long) newMemBase;
	params[1] = RTLD_NOW | RTLD_GLOBAL;
	// 6.1.call dlopen load so
	printf("library path = %s\n", PATH_INJECT_SO);
	if (0 == ptrace_callMethod(pid, remoteDlopenAddr, params, 2, &regs)) {
		printf("6.1 ptrace_callMethod dlopen\n");
	}
	// 6.1.get injectSoHandle
	void* injectSoHandle = (void*) (&regs)->ARM_r0;
	if (injectSoHandle) {
		printf("6.1 dlopen inject SO injectSoHandle:%p\n", injectSoHandle);
	} else {
		//get error
		if (0 > ptrace_callMethod(pid, remoteDlerror_addr, 0, 0, &regs)) {
			return -1;
		}

		uint8_t *errret = (uint8_t *) (&regs)->ARM_r0;
		uint8_t errbuf[100];
		void* errbufData = ptrace_readdata(pid, errret, errbuf, 100);
		printf("6.1 *****dlopen sohandle errret:%p; error:%s\n", errret,
				errbuf);
		ptrace_cont(pid);
		return -1;
	}

#define FUNCTION_NAME_ADDR_OFFSET       0x100
	// 6.2.write so.methodName to mem
	if (0
			== ptrace_poketext(pid,
					((uint8_t *) newMemBase) + FUNCTION_NAME_ADDR_OFFSET,
					(void*) injectSoMethodName,
					strlen(injectSoMethodName) + 1)) {
		printf("ptrace_poketext injectSo.MethodName write ok\n");
		printfRemoteStr(pid,
				((uint8_t *) newMemBase) + FUNCTION_NAME_ADDR_OFFSET,
				strlen(injectSoMethodName) + 1);
	}
	params[0] = (long) injectSoHandle;
	params[1] = (long) ((uint8_t*) newMemBase + FUNCTION_NAME_ADDR_OFFSET);
	// 6.2.call dlsym
	if (0 == ptrace_callMethod(pid, remoteDlsymAddr, params, 2, &regs)) {
		printf("6.2 ptrace_callMethod dlsym\n");
	}

	// 6.2.get so.method addr
	void* injectSoMethodAddr = (void*) (&regs)->ARM_r0;
	if (injectSoMethodAddr) {
		printf("6.2 dlsym get SO.method addr ok\n");
	} else {
		//get error
		if (0 > ptrace_callMethod(pid, remoteDlerror_addr, 0, 0, &regs)) {
			return -1;
		}

		uint8_t *errret = (uint8_t *) (&regs)->ARM_r0;
		uint8_t errbuf[100];
		void* errbufData = ptrace_readdata(pid, errret, errbuf, 100);
		printf("6.2 *****dlsym getSoMethodAddr errret:%p; error:%s\n", errret,
				errbuf);
		ptrace_cont(pid);
		return -1;
	}
#define FUNCTION_PARAM_ADDR_OFFSET      0x200
	// 6.3.write so.method param to mem
	ptrace_poketext(pid, ((uint8_t *) newMemBase) + FUNCTION_PARAM_ADDR_OFFSET,
			(void*) injectSoMethodParams, strlen(injectSoMethodParams) + 1);
	printfRemoteStr(pid, ((uint8_t *) newMemBase) + FUNCTION_PARAM_ADDR_OFFSET,
			strlen(injectSoMethodParams));
	params[0] = (long) (((uint8_t *) newMemBase) + FUNCTION_PARAM_ADDR_OFFSET);
	// 6.3.call so.mehtod
	if (0 == ptrace_callMethod(pid, injectSoMethodAddr, params, 1, &regs)) {
		printf("6.3 call so.method\n");
	}

	if (0 == ((void*) (&regs)->ARM_r0)) {
		printf("6.3 call so.method ok\n");
	}

	//6.3 dlclose so
	params[0] = (long) injectSoHandle;
	if (0 == ptrace_callMethod(pid, remoteDlcloseAddr, params, 1, &regs)) {
		printf("6.4 ptrace_callMethod call dlclose\n");
	}
	if (0 == ((void*) (&regs)->ARM_r0)) {
		printf("6.4 ptrace_callMethod dlclose so ok\n");
	}
	ptrace_setregs(pid, &OriginalRegs);
	ptrace_detach(pid);
	printf("pid:%d\n", pid);
	printf("cat /proc/%d/maps |grep libso\n", pid);
	return 0;
}

上代码在真机android6.0里执行并注入成功,时间是2018年11月27日。

如何知道是否懂了?

回答自己一个问题,调用mmap方法创建内存的目的是什么?

至于其他的基本都属于调用api,没难度的。O(∩_∩)O

x86的没有写,应只是寄存器的差别。自行完善。

创建了一个qq群,主要研究android逆向 安全 系统开发(逆向android系统)方面,欢迎各位加入。群号:492788365

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
AndroidInjectAndroid 注解框架,以简化 Android 开发 目前完成的注解(持续增加中): @AINoTitle: 类注解, 只适用于Activity(需继承于AIActivity), 设置Activity不显示Title     @AIFullScreen: 类注解, 只适用于Activity(需继承于AIActivity), 设置Activity全屏     @AILayout: 类注解         value[int]: 用于设置该Activity的布局 ---- setContentView(resId);     @AIView: 属性注解         id[int]: 用于绑定控件 ---- findViewById(resId);(default identifier[R.id.{field name}] if did not set id)         clickMethod[String]: 用于设置控件点击事件的回调方法, 可选, 方法名称任意, 参数必须为(View view)         longClickMethod[String]: 用于设置控件长按的回调方法, 可选, 方法名任意, 参数必须为(View view)         itemClickMethod[String]: 用于设置控件item点击的回调方法, 可选, 方法名任意, 参数必须为(AdapterView, View, int, long)         itemLongClickMethod[String]: 用于设置控件item长按的回调方法, 可选, 方法名任意, 参数必须为(AdapterView, View, int, long)     @AIBean: 属性注解, 为该属性生成一个对象并注入, 该对象必须有个默认的不带参数的构造方法     @AISystemService: 属性注解,为该属性注入系统服务对象     @AIClick: 方法注解         value[int[], 所要绑定控件的id]: 用于绑定控件点击事件的回调方法, 方法名称任意, 参数必须为(View view)     @AIItemClick: 方法注解         value[int[], 所要绑定控件的id]: 用于绑定控件item点击事件的回调方法, 方法名称任意, 参数必须为(AdapterView, View, int, long)     @AILongClick: 方法注解         value[int[], 所要绑定控件的id]: 用于绑定控件长按事件的回调方法, 方法名称任意, 参数必须为(View view)     @AIItemLongClick: 方法注解         value[int[], 所要绑定控件的id]: 用于绑定控件item长按事件的回调方法, 方法名称任意, 参数必须为(AdapterView, View, int, long)     @AIScreenSize: 属性注解         用于注入当前设备的屏幕大小(宽高)     @AIGet: 方法注解         value[String, 所要请求的url]:表示以GET来请求url         connTimeout[int, 连接超时时间]:连接一个url的连接等待时间         soTimeout[int, response返回超时时间]:连接上一个url,获取response的返回等待时间     @AIPost: 方法注解         value[String, 所要请求的url]:表示以Post来请求url         connTimeout[int, 连接超时时间]:连接一个url的连接等待时间         soTimeout[int, response返回超时时间]:连接上一个url,获取response的返回等待时间     @AIParam: 方法参数注解         value[String, 请求的参数别名]:注入@AIGet或@AIPost注解方法的请求参数     @AINetWorker: 属性注解         注入网络请求服务     @AIUpload: 方法注解         value[String, 所要请求的url]:表示要上传的url,默认用post请求(不需要使用@AIPost注解)         connTimeout[int, 连接超时时间]:连接一个url的连接等待时间         soTimeout[int, response返回超时时间]:连接上一个url,获取response的返回等待时间         注意:使用此注解的方法参数需要包含Collection或其子类型集合 或者包含File对象 来作为要上传的文件 使用fragment的注解,需要android-support-v4.jar的支持(以兼容低版本) 使用网络请求的注解,需要gson.jar的支持 使用文件上传的注解,需要httpmime.jar的支持

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值