文章目录

  • 前言
  • 一、刷新 CPU 高速缓存
  • 二、处理拦截函数
    • 1、桩函数
    • 2、处理拦截函数
  • 三、返回特定结果
  • 四、相关完整代码

前言

【Android 逆向】函数拦截实例 ( 函数拦截流程 | ① 定位动态库及函数位置 ) 博客中简单介绍了 hook 函数 ( 函数拦截 ) 的流程 , 本系列博客介绍函数拦截实例 ;

拦截 clock_gettime 函数 ;

#include <time.h>
int clock_gettime(clockid_t clk_id,struct timespec *tp);

【Android 逆向】函数拦截实例 ( ② 插桩操作 | 保存实际函数入口 6 字节数据 | 在插桩的函数入口写入跳转指令 | 构造拼接桩函数 ) 博客中进行了插桩操作 ,

一、刷新 CPU 高速缓存


执行 cache_flush 系统调用函数 刷新 CPU 的高速缓存 ; 该步骤 只在 ARM 架构的 CPU 中执行 , x86 架构的 CPU 不需要刷新缓存 ;

x86 不需要执行刷新缓存操作 , 但也可以执行系统调用操作 syscall 来刷新缓存 ;

刷新 CPU 高速缓存 代码示例 : pApi 是实际调用的函数指针 , size 是 6 字节 , 也就是说刷新 (int)pApi 地址到 (int)pApi + size 之间 666 字节对应的 CPU 高速缓存即可 ;

 /* 清空 CPU 高速缓存 */
#if !defined(__i386__)/* 在 arm 架构中必须刷新 CPU 高速缓存 , x86 不需要执行 */cacheflush((int)pApi, (int)pApi + size, ICACHE|DCACHE);
#else/* x86 下可以执行该系统调用 */syscall(0xF002, (int)pApi,(int)pApi + sizeE);

二、处理拦截函数


1、桩函数

在 【Android 逆向】函数拦截实例 ( ② 插桩操作 | 保存实际函数入口 6 字节数据 | 在插桩的函数入口写入跳转指令 | 构造拼接桩函数 ) 三、在插桩的函数入口写入跳转指令 | 构造拼接桩函数 博客章节 , 介绍了拼接装函数 do_clock_gettime 函数 , 实现了调用 do_clock_gettime 函数 与调用 clock_gettime 函数相同的效果 ;

构造拼接桩函数 : 前 6 字节是保存下来的 clock_gettime 函数的前 6 字节指令 , 执行到第 6 字节时 , 直接跳转到 clock_gettime 函数 执行 , 这样执行拼接的函数 等同于执行 clock_gettime 函数 ;

do_clock_gettime 函数构造成 clock_gettime 函数流程 : 执行 do_clock_gettime 方法的第 6 字节的指令时 , 跳转到 clock_gettime 函数的第 6 字节指令位置 , do_clock_gettime 的 0 ~ 6 字节指令是 clock_gettime 实际函数的前 6 字节 , 之所以这么定义 , 是因为 clock_gettime 的前 6 个字节被覆盖为 跳转指令了 ;

2、处理拦截函数

处理拦截函数 :

当函数执行到 clock_gettime 之后 , 就会执行插入的跳转指令 , 跳转到 dn_clock_gettime 函数中 ;

在该函数中 , 可以调用 do_clock_gettime 函数 , 执行原有的指令 ;

do_clock_gettime 函数执行前后 , 都可以插入自己的业务逻辑 , 监控或修改都可以 ;

处理拦截函数 代码示例 :

/* 拦截函数 , 拦截 clock_gettime 函数后 , 跳转到此处 */
int dn_clock_gettime(clockid_t id, struct timespec* ts) {/*if (ts == NULL) {return -1;}*//* 如果设备实现了系统调用 , 可以通过该代码调用原有的 clock_gettime 函数 *///int ret = syscall(__NR_clock_gettime, id, ts);/* 此处实际上调用的是原有的 clock_gettime 函数 如果设备上没有实现系统调用 , 使用如下方法可以调用原有的 clock_gettime 函数 */do_clock_gettime(id, ts);/* clock_gettime 函数执行完后 , 继续执行一些自己实现的部分 *//* 上面的代码是 hook 住的真实代码 我们可以在真实代码 前面 / 后面 执行一些自定义内容 */if (id > CLOCK_MONOTONIC)return 0;if (clock_base[id] == 0.0) {clock_base[id] = ts->tv_sec * 1000000000.0 + ts->tv_nsec;clock_new[id] = clock_base[id];}else {//mutex.lock();double tick = ts->tv_sec * 1000000000.0 + ts->tv_nsec;//printf("tick : %f base: %f delta: %f\n", tick, clock_base[id], tick - clock_base[id]);if (tick > clock_base[id]) {clock_new[id] += (tick - clock_base[id]) * time_scale;ts->tv_sec = (time_t)(clock_new[id] / 1000000000.0);ts->tv_nsec = (long)(fmod(clock_new[id], 1000000000.0));clock_base[id] = tick;}//mutex.unlock();}return 0;
}

三、返回特定结果


执行上述 dn_clock_gettime 函数的返回值 , 就是最终的返回结果 ;

四、相关完整代码


下面是相关代码 , 只是逆向代码中的函数拦截部分代码 :

调用代码 :

/* 这是 hook 标准库中的 clock_gettime 函数的入口方法 , 跳转到自定义的 dn_clock_gettime 方法中 */
hook_func((uint8_t*)clock_gettime, (uint8_t*)dn_clock_gettime, (uint8_t*)do_clock_gettime, 6);

函数拦截代码 :

/* hook 函数的完整流程 , 跳转指令 size 是 6 字节*/
/* 这是 hook 标准库中的 clock_gettime 函数的入口方法 , 跳转到自定义的 dn_clock_gettime 方法中 */
/* hook_func((uint8_t*)clock_gettime, (uint8_t*)dn_clock_gettime, (uint8_t*)do_clock_gettime, 6); */
void hook_func(uint8_t* pApi, uint8_t* pUser, uint8_t* pStub, size_t size)
{unsigned char code[64] = { 0 };/* 插桩前先保存函数的入口 6 字节数据 , 因为之后插桩 , * 会使用跳转代码 0xE9,0,0,0,0 覆盖函数入口内存* 该函数最终还是要执行 , 需要拷贝一下 , 供之后实际函数调用使用 */memcpy(code, pApi, size);/* 函数插桩 , pApi 是实际函数 , pUser 是插桩后跳转到的拦截函数 */write_code(pApi, pUser);/* 执行 size + pStub 位置的指令时 , 直接跳转到 size + pApi 位置如 : 执行 do_clock_gettime 方法的第 6 字节的指令时 , 跳转到 clock_gettime 函数的第 6 字节指令位置   do_clock_gettime 的 0 ~ 6 字节指令是 clock_gettime 实际函数的前 6 字节 , 之所以这么定义 , 是因为 clock_gettime 的前 6 个字节被覆盖为 跳转指令了调用 do_clock_gettime 方法 , 就相当于调用了*/write_code(size + pStub, size + pApi);/* 将复制的 6 字节 代码存放到 pStub 函数中的 0 ~ 6 字节位置 */memcpy(pStub, code, size);/* 清空 CPU 高速缓存 */
#if !defined(__i386__)/* 在 arm 架构中必须刷新 CPU 高速缓存 , x86 不需要执行 */cacheflush((int)pApi, (int)pApi + size, ICACHE|DCACHE);
#else/* x86 下可以执行该系统调用 */syscall(0xF002, (int)pApi,(int)pApi + sizeE);
#endif
}/** 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));
#endifreturn 0;
}/* 拦截函数 , 拦截 clock_gettime 函数后 , 跳转到此处 */
int dn_clock_gettime(clockid_t id, struct timespec* ts) {/*if (ts == NULL) {return -1;}*//* 如果设备实现了系统调用 , 可以通过该代码调用原有的 clock_gettime 函数 *///int ret = syscall(__NR_clock_gettime, id, ts);/* 此处实际上调用的是原有的 clock_gettime 函数 如果设备上没有实现系统调用 , 使用如下方法可以调用原有的 clock_gettime 函数 */do_clock_gettime(id, ts);/* clock_gettime 函数执行完后 , 继续执行一些自己实现的部分 *//* 上面的代码是 hook 住的真实代码 我们可以在真实代码 前面 / 后面 执行一些自定义内容 */if (id > CLOCK_MONOTONIC)return 0;if (clock_base[id] == 0.0) {clock_base[id] = ts->tv_sec * 1000000000.0 + ts->tv_nsec;clock_new[id] = clock_base[id];}else {//mutex.lock();double tick = ts->tv_sec * 1000000000.0 + ts->tv_nsec;//printf("tick : %f base: %f delta: %f\n", tick, clock_base[id], tick - clock_base[id]);if (tick > clock_base[id]) {clock_new[id] += (tick - clock_base[id]) * time_scale;ts->tv_sec = (time_t)(clock_new[id] / 1000000000.0);ts->tv_nsec = (long)(fmod(clock_new[id], 1000000000.0));clock_base[id] = tick;}//mutex.unlock();}return 0;
}int do_clock_gettime(clockid_t which_clock, struct timespec* tp)
{//未使用,内核中该函数实际的代码,反汇编libc.so得到/*这些指令都不重要 都会被覆盖调 , 写的这些指令主要是占坑用的 实际上调用的是 clock_gettime 函数 下面的汇编代码都会被覆盖为 跳转代码 , 跳转到 clock_gettime 函数 , 注意 , clock_gettime 函数 的前 6 字节的指令会被拷贝到函数入口 , 执行第 6 字节位置时 , 跳转到 clock_gettime 函数 的第 6 字节位置*///return syscall(__NR_clock_gettime, which_clock, tp);__asm__ __volatile__("push %%ebx"::: "ebx");__asm__ __volatile__("push %%ecx\n"::: "ecx");__asm__ __volatile__("mov 12(%%esp),%%ebx"::: "ebx");__asm__ __volatile__("mov 16(%%esp),%%ecx"::: "ecx");__asm__ __volatile__("mov $0x109,%%eax"::: "eax");__asm__ __volatile__("int $0x80");__asm__ __volatile__("pop %%ecx":::"ecx");__asm__ __volatile__("pop %%ebx":::"ebx");__asm__ __volatile__("retn");
}

【Android 逆向】函数拦截实例 ( ③ 刷新 CPU 高速缓存 | ④ 处理拦截函数 | ⑤ 返回特定结果 )相关推荐

  1. 【Android 逆向】函数拦截 ( 使用 cache_flush 系统函数刷新 CPU 高速缓存 | 刷新 CPU 高速缓存弊端 | 函数拦截推荐时机 )

    文章目录 一.使用 cache_flush 系统函数刷新 CPU 高速缓存 二.使用 cache_flush 系统函数刷新 CPU 高速缓存的弊端 三.函数拦截推荐时机 一.使用 cache_flus ...

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

    文章目录 一.CPU 高速缓存机制 二.CPU 高速缓存机制 导致 函数拦截失败 一.CPU 高速缓存机制 CPU 架构模型中 , 指令 在开始时 , 存放在内存中 , 如 : /proc/pid/m ...

  3. 【Android 逆向】函数拦截实例 ( ② 插桩操作 | 保存实际函数入口 6 字节数据 | 在插桩的函数入口写入跳转指令 | 构造拼接桩函数 )

    文章目录 前言 一.函数拦截需要的几个参数 二.插桩前先保存实际函数入口 6 字节数据 三.在插桩的函数入口写入跳转指令 | 构造拼接桩函数 前言 [Android 逆向]函数拦截实例 ( 函数拦截流 ...

  4. 【Android 逆向】函数拦截实例 ( 函数拦截流程 | ① 定位动态库及函数位置 )

    文章目录 一.函数拦截流程 二.定位动态库及函数位置 一.函数拦截流程 函数拦截流程 : 定位动态库及函数位置 : 获取该动态库在内存中的位置 , 以便于 查找函数位置 ; 插桩 : 在函数的入口处插 ...

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

    文章目录 一.ARM 架构下的插桩拦截 二.完整代码示例 一.ARM 架构下的插桩拦截 ARM 架构下的跳转指令 : 下面的二进制数都是十六进制数 ; 323232 位指令 ; 04 F0 1F E5 ...

  6. android 代码浏览,Webview实现android简单的浏览器实例代码

    WebView是Android中一个非常实用的组件,它和Safai.Chrome一样都是基于Webkit网页渲染引擎,可以通过加载HTML数据的方式便捷地展现软件的界面,下面通过本文给大家介绍Webv ...

  7. Python中的startswith和endswith函数使用实例

    Python中的startswith和endswith函数使用实例 在Python中有两个函数分别是startswith()函数与endswith()函数,功能都十分相似,startswith()函数 ...

  8. python中max函数用法_Python中max函数用法实例分析

    Python中max函数用法实例分析 更新时间:2015年07月17日 15:45:09 作者:优雅先生 这篇文章主要介绍了Python中max函数用法,实例分析了Python中max函数的功能与使用 ...

  9. php的可变函数,php之可变函数的实例详解

    php之可变函数的实例详解 php的可变函数,今天大概的了解下,是看php手册总结的,觉得用处不大: PHP 支持可变函数的概念.这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数, ...

最新文章

  1. 从未在一起更让人遗憾_明明是真爱,却又不能在一起
  2. 数 学 专 业 劝 退 指 南
  3. 细述 Java垃圾回收机制→Java Garbage Collection Monitoring and Analysis
  4. 游戏中每日刷新实现思路浅析
  5. Java代码的维护与更新,Java常用的规则引擎,让你变动的代码更加容易维护
  6. Mybatis非mapper代理配置
  7. 关于计算机的幻想作文600字,科学幻想作文600字
  8. P1020 导弹拦截 dp 树状数组维护最长升序列
  9. ffmpeg 最简单的转码封装mp4文件
  10. html上下表格合并单元格,html表格合并单元格的方法_WEB前端开发
  11. OpenCV图像锐化
  12. C++ API中文文档分享
  13. 浅谈制作BIM模型后期展示视频
  14. python爬取校花网的图片
  15. 怎么把旧iPhone上的备份迁移到新iPhone上?
  16. Pedersen承诺
  17. 遥感影像处理的几个概念
  18. linux 程序加启动项,linux 让一个程序开机自启动并把一个程序加为服务
  19. 如何抓取淘宝天猫上多个宝贝商品图片视频素材
  20. [record]橙小胖的简单生活

热门文章

  1. 优秀简历要遵循哪些规则
  2. Guava学习笔记:Preconditions优雅的检验参数
  3. juniper防火墙做ipsec ***必须开放的端口
  4. python测试开发django-35.xadmin注册表信息
  5. tomcat配置文件修改
  6. concurrent(六)同步辅助器CyclicBarrier 源码分析
  7. 地图篇-01.获取用户位置
  8. Android签名机制之---签名验证过程详解
  9. 转:VirtualBox虚拟机网络连接设置的四种方式
  10. js 调用C#.NET后台方法 转载自:http://www.cnblogs.com/lizhao/archive/2010/11/23/1990436.html...