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

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

文章目录

前言

一、函数拦截需要的几个参数

二、插桩前先保存实际函数入口 6 字节数据

三、在插桩的函数入口写入跳转指令 | 构造拼接桩函数

前言

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


拦截 clock_gettime 函数 ;


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





一、函数拦截需要的几个参数


定义 hook_func 函数 , 执行 C/C++ 函数的 hook 操作 ;


void hook_func(uint8_t* pApi, uint8_t* pUser, uint8_t* pStub, size_t size)


上述函数的 4 44 个参数含义如下 :


uint8_t* pApi 参数 : 要拦截的实际函数 , int clock_gettime(clockid_t clk_id,struct timespec *tp); 函数 ;

uint8_t* pUser 参数 : 拦截函数后 , 跳转到的 dn_clock_gettime 函数 ;

uint8_t* pStub 参数 : 定义的 do_clock_gettime 桩代码 , 将 pApi 函数的前 6 字节拷贝到该 pStub 函数入口 , 然后跳转到 pApi 函数的第 6 66 字节开始执行 , 相当于调用了 uint8_t* pApi 参数对应的实际函数 , 即 int clock_gettime(clockid_t clk_id,struct timespec *tp); 函数 ;

size_t size 参数 : 跳转指令占 0xE9,0,0,0,0 5 55 字节 , 这里 将函数入口前 6 66 字节保存下来 ;


函数调用实例 :


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





二、插桩前先保存实际函数入口 6 字节数据


插桩前先 保存函数的入口 6 字节数据 , 因为之后插桩 , 会使用跳转代码 0xE9,0,0,0,0 覆盖函数入口内存 , 被破坏的实际函数 最终还是要执行 , 需要拷贝一下 , 供之后实际函数调用使用 ;


unsigned char code[64] = { 0 };
  /* 插桩前先保存函数的入口 6 字节数据 , 因为之后插桩 , 
  * 会使用跳转代码 0xE9,0,0,0,0 覆盖函数入口内存
  * 该函数最终还是要执行 , 需要拷贝一下 , 供之后实际函数调用使用 
  */
  memcpy(code, pApi, size);






三、在插桩的函数入口写入跳转指令 | 构造拼接桩函数


这里执行了 2 22 次插桩操作 :


第一次是实际函数跳转 : 函数插桩 , pApi 是实际函数 , pUser 是插桩后跳转到的拦截函数 ; 该情况是在 clock_gettime 函数的入口处插入跳转代码 , 跳转到 dn_clock_gettime 函数位置 ;

第二次是构造桩函数 ( 构造拼接桩函数 ) : 在自定义的 dn_clock_gettime 函数中 , 需要调用实际的 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 个字节被覆盖为 跳转指令了 ;


调用 do_clock_gettime 方法 , 就相当于调用了 clock_gettime 方法 ;


/* 函数插桩 , 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 方法 , 就相当于调用了 clock_gettime 方法 ; 
  */
  write_code(size + pStub, size + pApi);
  /* 将复制的 6 字节 代码存放到 pStub 函数中的 0 ~ 6 字节位置 */
  memcpy(pStub, code, size);


函数插桩的具体细节在之前的


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

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

博客中有详细的说明 , 先修改内存页属性 , 然后直接修改内存 , 写入跳转汇编指令对应的二进制机器码数据 ;



代码示例 :


/*
 * 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;
}


目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
222 4
|
5月前
|
开发工具 Android开发 开发者
Android平台如何不推RTMP|不发布RTSP流|不实时录像|不回传GB28181数据时实时快照?
本文介绍了一种在Android平台上实现实时截图快照的方法,尤其适用于无需依赖系统接口的情况,如在RTMP推送、RTSP服务或GB28181设备接入等场景下进行截图。通过底层模块(libSmartPublisher.so)实现了截图功能,封装了`SnapShotImpl.java`类来管理截图流程。此外,提供了关键代码片段展示初始化SDK实例、执行截图、以及在Activity销毁时释放资源的过程。此方案还考虑到了快照数据的灵活处理需求,符合GB/T28181-2022的技术规范。对于寻求更灵活快照机制的开发者来说,这是一个值得参考的设计思路。
|
3月前
|
程序员 开发工具 Android开发
Android|WebView 禁止长按,限制非白名单域名的跳转层级
如何限制 WebView 仅域名白名单网址能随意跳转,并禁用长按选择文字。
50 2
|
4月前
|
Android开发 UED Kotlin
Android中如何跳转到Wi-Fi开关设置页
本文介绍如何在Android应用开发中使用隐式Intent引导用户至特定系统设置页面,如Wi-Fi设置页,并提供Kotlin代码示例。通过设置Intent的Action属性并检查设备兼容性,可轻松实现跳转功能,提升用户体验。此外,还列举了其他常用设置页面的Intent Action及注意事项。
113 15
|
3月前
|
存储 大数据 数据库
Android经典面试题之Intent传递数据大小为什么限制是1M?
在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
115 0
|
5月前
|
存储 安全 物联网
Android经典实战之跳转到系统设置页面或其他系统应用页面大全
本文首发于公众号“AntDream”,关注获取更多技巧。文章总结了Android开发中跳转至系统设置页面的方法,包括设备信息、Wi-Fi、显示与声音设置等,并涉及应用详情与电池优化页面。通过简单的Intent动作即可实现,需注意权限与版本兼容性。每日进步,尽在“AntDream”。
586 2
|
5月前
|
JSON Java Android开发
Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
124 1
|
5月前
|
编解码 网络协议 前端开发
如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
后台采集摄像头,如果想再进一步扩展,可以把android平台gb28181的camera2 demo,都移植过来,实现功能更强大的国标设备侧,这里主要是展示,收到国标平台侧的回传请求后,才打开摄像头,才开始编码打包,最大限度的减少资源的占用
|
5月前
|
编解码 网络协议 Android开发
Android平台GB28181设备接入模块实现后台service按需回传摄像头数据到国标平台侧
我们在做Android平台GB28181设备对接模块的时候,遇到这样的技术需求,开发者希望能以后台服务的形式运行程序,国标平台侧没有视频回传请求的时候,仅保持信令链接,有发起视频回传请求或语音广播时,打开摄像头,并实时回传音视频数据或接收处理国标平台侧发过来的语音广播数据。
|
5月前
|
Java 测试技术 Android开发
Android项目架构设计问题之构造一个Android中的线程池如何解决
Android项目架构设计问题之构造一个Android中的线程池如何解决
39 0