epic是weishu大神开源的一个Hook框架,支持ART上的Java方法HOOK。

实现原理:http://weishu.me/2017/11/23/dexposed-on-art/

本文走马观花一下。

epic相当于ART上的Dexposed,所以也是Xposed-Style Method Hook。

从DexposedBridge.findAndHookMethod开始跟踪代码:

取出最后一个参数callback,然后调用XposedHelpers.findMethodExact得到想要Hook的method,最后调用DexposedBridge.hookMethod进行Hook。

XposedHelpers.findMethodExact的实现在之前的笔记中已经看过了,这里不看了。

直接看DexposedBridge.hookMethod:

所有已经Hook过的method及其对应的callbacks,全部存储在hookedMethodCallbacks中,这是一个HashMap。如果该method已经Hook过,那直接把callback回调对象加入到其对应的callbacks集合中就可以了。这样在该method被调用时,callbacks集合中所有回调都会被遍历执行。

如果该method没有被Hook过,那就调用Epic.hookMethod进行Hook。

(这里以Method为例,Constructor的Hook大同小异)

先看一下ArtMethod.of:

ArtMethod.of是以Method对象作为参数,创建一个me.weishu.epic.art.Epic.ArtMethod对象。

artOrigin. method保存原始的Java Method对象。

artOrigin. address保存的是原始的Method对象在ART中对应的art::mirror::ArtMethod对象的地址。

artOrigin. objectAddress保存的是原始的Java Method对象(Java Object)在内存中的地址。

(EpicNative.getMethodAddress和Unsafe.getObjectAddress的实现代码先不贴了。getMethodAddress的实现很简单,getObjectAddress的实现稍复杂,但也不难理解。这里先跟踪主要代码,忽略旁枝末节。)

继续看Epic.hookMethod(ArtMethod artOrigin):

(这个函数的实现有点长,分段贴)

首先创建一个MethodInfo对象,用于保存方法信息。其中methodInfo.method保存了原始的Method对象对应的me.weishu.epic.art.method.ArtMethod对象。

然后将MethodInfo对象保存到originSigs中。originSigs是一个Map对象,key是Method对象对应的art::mirror::ArtMethod对象的地址,value是MethodInfo。

调用setAccessible(true),取消Java方法调用时的访问权限检查。

调用ensureResolved,保证静态方法完成解析。为什么要这么干,已经写在注释里了。

如果要Hook的方法还未编译,则调用ArtMethod.compile主动进行编译,这么做也是因为epic是“dynamic callee-side rewriting”。

ArtMethod.compile是通过调用JIT的jit_compile_method来完成方法编译的。

最后,compiled_code入口点会保存到originEntry变量中。

为原Method创建一个备份,保存到Epic.backupMethodsMapping中。

前面是铺垫,最重要的一步来了,具体看注释。

哪些不同的Java方法会具有相同的compiled_code入口点呢?

1、所有ART版本上未被resolve的static函数

2、Android N 以上的未被编译的所有函数

3、代码逻辑一模一样的函数

4、JNI函数

其中,情况1和2在上面已经处理过了,应该不会遇到了,剩下3和4。

对于JNI函数,因为不会涉及到字节码编译,也没有对应的compiled_code,所以其compiled_code入口点会统一设置为GetQuickGenericJniStub,即art_quick_generic_jni_trampoline。

继续跟Trampoline.install,看看是如何安装跳板代码来最终完成Hook的。

这个函数的功能描述已经写到注释里了。核心操作有两点:

1)创建Trampoline(包括“二段跳板”BridgeJump,和CallOrigin)

2)创建和安装“一段跳板”,完成Hook。

简单看一下epic的基本原理图:

epic的Hook机制是“dynamic callee-side rewriting”。具体点说:

1)保证要Hook的method完成compile,也就是运行时要执行其compiled_code。

2)根据要Hook的method对应的art::mirror::ArtMethod找到compiled_code入口点。

3)在compiled_code的开始位置放置一段很短的跳转代码,称为“一段跳板”,作用是跳转到二段跳板。之所以弄一个一段跳板,是怕二段跳板太长,原方法的compiled_code区域放不下。

4)二段跳板会将一些必要的参数打包,调用Java-Bridge方法,并将打包在一起的参数,通过r3传递给Java-Bridge。

5)Java-Bridge方法取出传递进来的参数,然后根据r1、r2、r3以及sp(以Thumb2为例,除了r0~r3,剩余的参数会通过sp传递),构造出原方法的参数,最后调用DexposedBridge.handleHookedArtMethod。

6)由DexposedBridge.handleHookedArtMethod调用beforeHookedMethod、原方法和afterHookedMethod。

二段跳板的创建由Trampoline.create方法完成,一段跳板的创建和安装由Trampoline. activate方法完成。

先看Trampoline.create:

那这里创建的BridgeJump(二段跳板)和CallOrigin是什么样子的呢?分别看一下Trampoline. createTrampoline和shellCode.createCallOrigin方法。

先看Trampoline. createTrampoline:

先调用Entry.getBridgeMethod返回一个Bridge方法,这个Bridge是一个Java方法。然后调用shellCode.createBridgeJump创建BridgeJump(二段跳板)。

我们先看shellCode.createBridgeJump(以Thumb2为例)创建的BridgeJump(二段跳板),然后再去看Entry.getBridgeMethod返回的Bridge方法。调用shellCode.createBridgeJump时传入的各个参数的含义已经写到注释里了。

这里创建的就是上面原理图中的二段跳板代码,详情看注释。重点有两处:

1)art::mirror::ArtMethod对象地址的比较

2)打包参数,然后跳转到Java-Bridge,打包之后的参数通过r3传递。

现在可以去看上面由Entry.getBridgeMethod返回的Bridge方法了(以32位运行时为例)。

假设returnType是Object.class,那么返回的Bridge方法就是Entry.referenceBridge:

从前面的二段跳板代码可知,传递给referenceBridge的第3个参数struct是一个结构体指针。

按照之前的规则,依次取出sp、r2、r3和sourceMethod。sourceMethod是原Method对应的art::mirror::ArtMethod对象在内存中的地址。然后根据r1、r2、r3以及self、sp(以Thumb2为例,除了r0~r3,剩余的参数会通过sp传递),构造出原方法的参数。然后根据returnType的不同,分别调用onHookXXX函数。

Entry.constructArguments的实现逻辑不难理解,但是从weishu大神的处理来看,不同情况下的兼容还是最头疼的问题。

还是假设returnType是Object.class,看一下Entry. onHookObject:

其实就是调用了DexposedBridge.handleHookedArtMethod。

DexposedBridge.handleHookedArtMethod的逻辑,熟悉Xposed的人应该都很熟悉。前面的whale笔记也跟踪过代码了,这里就不看了。beforeHookedMethod、原方法和afterHookedMethod都是在DexposedBridge.handleHookedArtMethod里面调用的。

回到Trampoline.create,再看一下shellCode.createCallOrigin:

创建CallOrigin的逻辑就比较简单了,先是compiled_code的原前8个字节的指令(以Thumb2为例),然后是一条跳转指令,跳转到原Method对应的compiled_code的偏移8个字节的位置,也就是一段跳板代码的后面,去执行原Method的compiled_code中剩余的指令。

最后,再回到Trampoline.install,看一下Trampoline. activate是如何创建和安装一段跳板的。

Trampoline. activate直接调用EpicNative.activateNative。

1)参数jumpToAddress原Method的compiled_code入口点。

2)参数是pc是Trampoline代码的首地址,即:前面创建的一块内存,里面是二段跳板BridgeJump和CallOrigin。

3)最后一个参数是一段跳板代码,由shellCode.createDirectJump(pc)创建。

看一下shellCode.createDirectJump是如何创建一段跳板的(以Thumb2为例):

很简单,就是一条ldr指令,将要跳转的地址(二段跳板的代码地址)赋给pc。

最后,看一下EpicNative.activateNative,这是一个native方法,实现代码如下:

这个函数的实现也很简单。就是将一段跳板的代码拷贝到原Method对应的compiled_code的开始处,类似native函数的InlineHook。和Whale一样,这里在安装一段跳板前也暂停了ART的所有线程,原因已经写在注释里了。另外,在arm平台下,更新完指令要记得cacheflush。

至此,epic的Hook就算完成了。

本篇笔记只是草草的跟踪一下代码,并未将所有的实现细节全部看完。但主干代码和实现原理已经算是清楚了。

本文由看雪论坛 十八垧 原创

原文链接:https://bbs.pediy.com/thread-250490.htm

转载请注明来自看雪社区

xposed hook 静态函数_开源Hook框架-epic-实现浅析相关推荐

  1. xposed hook 静态函数_浅谈 Xposed 新概念【模块作用域】

    众所周知,Xposed 是一个系统级别的软件框架,它与 Cydia Substrate 不同,Xposed 仅可 hook app_process 中的 java 函数,不过对于大部分的 Androi ...

  2. python hook微信_基于hook的python机器人,彻底取代itchat

    本文档部分由itchat与wxpy的开发文档修改得出 禁止不规范转载 WechatBot WechatBot是一个半开源的微信个人号接口,使用python调用微信从未如此简单. 使用不到20行的代码, ...

  3. hook xposed 自定义类_使用Xposed实现Hook

    使用Xpose框架实现Hook操作 1.项目导入 在app/build.gradle中配置 repositories { jcenter() } dependencies { compileOnly ...

  4. xposed hook 构造函数_初识Xposed(上)

    " 前言:初学逆向 请多多指教" 总结: 1.学习了Xposed的正常hook,以及一些hook的时候需要注意的地方 Xposed介绍 - Android基于Linux,第一个启动 ...

  5. 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  7. xposed hook java_[原创]Android Hook 系列教程(一) Xposed Hook 原理分析

    章节内容 一. Android Hook 系列教程(一) Xposed Hook 原理分析 二. Android Hook 系列教程(二) 自己写APK实现Hook Java层函数 三. Androi ...

  8. struts框架的原理和应用_分布式开源调度框架TBSchedule原理与应用

    主要内容: 第一部分 TBSchedule基本概念及原理 1. 概念介绍 2. 工作原理 3. 源码分析 4. 与其他开源调度框架对比 第二部分 TBSchedule分布式调度示例 1. TBSche ...

  9. 开源web框架_带有酷名称的开源JavaScript和Web框架的词汇表

    开源web框架 It's getting to the point where there are so many cool open source projects that I can't kee ...

最新文章

  1. linux下,每次git pull 或者git push都需要输入账号密码的问题以及git remote 的一些基本操作
  2. 检验入参合法性有哪些_验证用户输入的参数合法性的shell脚本
  3. pyhon滤镜详细教程
  4. Cookie和会话状态 (转)
  5. 架构评审方法和思路总结
  6. 非洲儿童(南阳oj1036)(馋)
  7. 【机器学习PAI实践十二】机器学习算法基于信用卡消费记录做信用评分
  8. css中的px、em、rem 详解
  9. 当深度学习遇上图: 图神经网络的兴起!(文末送书)
  10. 华为LINUX转换NTFS格式,linux中生成考核用的NTFS文件系统结构样例(一)
  11. public 函数_UE4精品教程 | 渲染编程(C++篇)【第三卷:从仿函数到std::function再到虚幻4Delegate】...
  12. 协议簇:Ethernet Address Resolution Protocol (ARP) 解析
  13. dll修改和EXCOPE
  14. 调用Kubernetes SDK实现二次开发
  15. 简述 IntentFilter(意图过滤器)
  16. OpenStack点滴01-概览
  17. asp.net引入一个js文件名为JScript的js脚本 报错 缺少对象
  18. Atitit object 和class的理解 目录 1.1. 发现很多Object的方法都是相同的,他们被重复地放在一个个对象当中,太浪费了。 1 1.2. 那我们怎么把这些Object给创建起来
  19. eviews7.2pojie版-eviews7.2附使用教程
  20. docker搭建linux集群,搭建mpi环境,并使用MTT benchmark测试集群性能

热门文章

  1. 这个教程价值有点高,利用Python制作全自动化营销软件!
  2. 【已解决】清除linux系统的多余引导
  3. word文本转换为表格 ,如果文本是以硬回车的转换方式
  4. ASP.NET中使用C#文件下载四方法
  5. 漫步最优化三十六——基本共轭方向法
  6. flask执行python脚本_如何从Flask应用程序执行Shell脚本
  7. spark指定python版本_1.Spark学习(Python版本):Spark安装
  8. PointConv论文阅读笔记
  9. 计算机网络 —— OSI参考模型
  10. 去哪儿-22-async-components