Android 逆向函数拦截 ( 修改内存页属性 | x86 架构插桩拦截 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向函数拦截 ( 修改内存页属性 | x86 架构插桩拦截 )相关的知识,希望对你有一定的参考价值。
一、修改内存页属性
实际函数 的 函数指针为 unsigned char* pFunc
, 拦截函数 的函数指针为 unsigned char* pStub
, 在执行 pFunc
函数时 , 无条件跳转到 pStub
函数中 ;
要修改 pFunc
函数 , 要先设置该函数所在的内存页的访问 属性 , 否则如果用户没有相关内存访问权限 , 强行修改会报错 ;
首先 , 获取 pFunc
函数 所在内存页地址 , 每个内存页 4KB ;
/* 获取 pFunc 函数入口 , 先获取该函数所在内存页地址 */
void* pBase = (void*)(0xFFFFF000 & (int)pFunc);
然后 , 修改内存页属性 , 修改为 可读 | 可写 | 可执行 , 避免因为内存访问权限问题导致操作失败 ; mprotect 函数只能对整个页内存的属性进行修改 , 每个 内存页 大小都是 4KB ;
/* 修改整个内存页属性 , 修改为 可读 | 可写 | 可执行 ,
* 避免因为内存访问权限问题导致操作失败
* mprotect 函数只能对整个页内存的属性进行修改
* 每个 内存页 大小都是 4KB
*/
int ret = mprotect(pBase, 0x1000, PROT_WRITE | PROT_READ | PROT_EXEC);
二、x86 架构下的插桩拦截
插桩拦截 时 , 在 实际函数 入口处写入的 跳转代码 就是 汇编中的 跳转指令 ;
跳转指令 可以理解为 " 指令 " 或 " 机器码 " , 指令是人看到的 汇编指令 , 机器码是给 CPU 执行的 二进制机器码 ; 二者是等效的 ;
x86 架构下的跳转指令 : 下面的二进制数都是十六进制数 ; 32 32 32 位指令 ;
E9 00 00 00 00
, JMP target
;
JMP 是强制跳转指令 , E9
是对应的机器码 ;
首先 , 准备跳转指令 ,
/* E9 是 JMP 无条件跳转指令 , 后面 4 字节是跳转的地址 */
unsigned char code[] = { 0xE9,0,0,0,0 };
然后 , 计算 pStub 函数跳转地址 , 目标函数 pStub 地址 - 当前函数 pFunc 地址 - 5 , x86 架构中 , 跳转指令 跳转的是 偏移量 , 不是绝对地址值 ;
/* 计算 pStub 函数跳转地址 , 目标函数 pStub 地址 - 当前函数 pFunc 地址 - 5
* 跳转指令 跳转的是 偏移量 , 不是绝对地址值
*/
*(unsigned*)(code + 1) = pStub - pFunc - 5;
最后 , 将跳转代码拷贝到 pFunc 地址处 , 这是 pFunc 函数的入口地址 ;
/* 将跳转代码拷贝到 pFunc 地址处 , 这是 pFunc 函数的入口地址 */
memcpy(pFunc, code, sizeof(code));
以上是关于Android 逆向函数拦截 ( 修改内存页属性 | x86 架构插桩拦截 )的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向函数拦截 ( CPU 高速缓存机制 | CPU 高速缓存机制 导致 函数拦截失败 )
Android 逆向函数拦截原理 ( 通过修改 GOT 全局偏移表拦截函数 | 通过在实际被调用的函数中添加跳转代码实现函数拦截 )
Android 逆向函数拦截 ( GOT 表拦截 与 插桩拦截 | 插桩拦截简介 | 插桩拦截涉及的 ARM 和 x86 中的跳转指令 )
Android 逆向函数拦截实例 ( 函数拦截流程 | 定位动态库及函数位置 )