Android 逆向函数拦截 ( ARM 架构下的插桩拦截 | 完整代码示例 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向函数拦截 ( ARM 架构下的插桩拦截 | 完整代码示例 )相关的知识,希望对你有一定的参考价值。





一、ARM 架构下的插桩拦截



ARM 架构下的跳转指令 : 下面的二进制数都是十六进制数 ; 32 32 32 位指令 ;

04 F0 1F E5 00 00 00 00 , B target ;

B 指令是无条件跳转指令 , 04 F0 1F E5 是对应的机器码 ;


【Android 逆向】函数拦截 ( 修改内存页属性 | x86 架构插桩拦截 ) 一、修改内存页属性 基础上 , 先修改内存页属性 , 取得修改内存的权限 ;


然后开始进行函数拦截 ;

首先 , 拼装 ARM 架构下的无条件跳转指令 ;

	/* B 无条件跳转指令 */
	unsigned char code[] = { 0x04,0xF0,0x1F,0xE5,0x00,0x00,0x00,0x00 };

然后 , 设置跳转指令的绝对地址 ; 注意这里与 x86 的跳转指令不同 , x86 设置的跳转地址是相对地址 , arm 的跳转地址是绝对地址 ;

	/* arm 的跳转是绝对地址跳转 , 传入 pStub 函数指针即可 */
	*(unsigned*)(code + 4) = (unsigned)pStub;

最后 , 将 arm 跳转指令二进制机器码拷贝到函数开始位置 ;

	/* 将机器码复制到函数开始位置 */
	memcpy(pFunc, code, sizeof(code));




二、完整代码示例



下面是 插桩函数拦截 的代码 , 兼容 x86 与 arm 架构 ;

注意 : 写完之后推荐刷新 CPU 高速缓存 , 调用 cache_flush 系统调用函数 ;

/*
 * unsigned char* pFunc
 * unsigned char* pStub
 * 上述两个参数分别是两个函数指针
 * 
 * 注意 : 写完之后要刷新 CPU 高速缓存 , 调用 cache_flush 系统调用函数
 */
int write_code(unsigned char* pFunc, unsigned char* pStub) {
	/* 获取 pFunc 函数入口 , 先获取该函数所在内存页地址 */
	void* pBase = (void*)(0xFFFFF000 & (int)pFunc);
	/* 修改整个内存页属性 , 修改为 可读 | 可写 | 可执行 , 
	 * 避免因为内存访问权限问题导致操作失败
	 * mprotect 函数只能对整个页内存的属性进行修改 
	 * 每个 内存页 大小都是 4KB 
	 */
	int ret = mprotect(pBase, 0x1000, PROT_WRITE | PROT_READ | PROT_EXEC);
	/* 修改内存页属性失败的情况 */
	if (ret == -1) {
		perror("mprotect:");
		return -1;
	}
#if defined(__i386__) // arm 情况处理
	/* E9 是 JMP 无条件跳转指令 , 后面 4 字节是跳转的地址 */
	unsigned char code[] = { 0xE9,0,0,0,0 };
	/* 计算 pStub 函数跳转地址 , 目标函数 pStub 地址 - 当前函数 pFunc 地址 - 5 
	 * 跳转指令 跳转的是 偏移量 , 不是绝对地址值
	 */
	*(unsigned*)(code + 1) = pStub - pFunc - 5;
	/* 将跳转代码拷贝到 pFunc 地址处 , 这是 pFunc 函数的入口地址 */
	memcpy(pFunc, code, sizeof(code));
#else // arm 情况处理
	/* B 无条件跳转指令 */
	unsigned char code[] = { 0x04,0xF0,0x1F,0xE5,0x00,0x00,0x00,0x00 };
	/* arm 的跳转是绝对地址跳转 , 传入 pStub 函数指针即可 */
	*(unsigned*)(code + 4) = (unsigned)pStub;
	/* 将机器码复制到函数开始位置 */
	memcpy(pFunc, code, sizeof(code));
#endif
	return 0;
}

/* C/C++ 中的 hook 函数方式 */
void hook_func(uint8_t* pApi, uint8_t* pUser, uint8_t* pStub, size_t size)
{
	unsigned char code[64] = { 0 };
	memcpy(code, pApi, size);
	write_code(pApi, pUser);
	write_code(size + pStub, size + pApi);
	memcpy(pStub, code, size);
}

以上是关于Android 逆向函数拦截 ( ARM 架构下的插桩拦截 | 完整代码示例 )的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向函数拦截 ( 修改内存页属性 | x86 架构插桩拦截 )

Android 逆向函数拦截 ( GOT 表拦截 与 插桩拦截 | 插桩拦截简介 | 插桩拦截涉及的 ARM 和 x86 中的跳转指令 )

Android 逆向arm 汇编 ( 使用 IDA 解析 arm 架构的动态库文件 | 分析 malloc 函数的 arm 汇编语言 )

Android 逆向arm 汇编 ( 使用 IDA 解析 arm 架构的动态库文件 | 分析 malloc 函数的 arm 汇编语言 )

Android 逆向Android 进程注入工具开发 ( 远程调用 | x86 架构的返回值获取 | arm 架构远程调用 )

Android 逆向函数拦截 ( CPU 高速缓存机制 | CPU 高速缓存机制 导致 函数拦截失败 )