面试:Hook框架Xposed、Dexposed、Epic原理

Xposed、Dexposed、Epic对比总结:

1、Xposed框架是需要root的,他的功能很全,能够hook掉系统方法,同时也可以hook掉其他应用的一些方法。

2、Dexposed框架是不需要root的,但是他只能hook掉在自己的应用进程中的一些方法,其他应用进程是没办法hook的。

3、Epic是基于Dexposed进行的修改,支持art虚拟机上面的Hook。

一、Xposed原理:

Xposed原理简介及其精简化 - 简书

万物皆可 Hook,探究 Xposed 框架 Hook 原理 - 知乎

Android 系统是基于 Linux 的,其第一个由内核启动的用户进程是 init 进程。init 进程随后会创建 zygote 进程,Android 应用程序进程都是由 zygote 进程孵化而来。zygote 所对应的可执行程序是 app_process,xposed 框架通过替换系统的 app_process 可执行文件以及虚拟机动态链接库,让 zygote 在启动应用程序进程时注入框架代码,进而实现对应用程序进程的劫持。

设备root之后,system/bin/下面的文件替换了app_process等文件,app_process就是zygote进程文件。所以Xposed通过替换zygote进程实现了控制手机上所有app进程。因为所有app进程都是由Zygote fork出来的。使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。

Xposed的基本原理是修改了ART/Davilk虚拟机,将需要hook的函数注册为Native层函数。当执行到这一函数时虚拟机会优先执行Native层函数,然后再去执行Java层函数,这样完成函数的hook。

EnableXposedHook 方法的实现,代码位于 android_art/runtime/art_method.c,这里涉及较深入的 ART 虚拟机知识,大致可以这么理解这一段代码的意思,首先拿到 Hook 对象方法的执行地址,在此地址上替换成增加了 before 以及 after 实现的方法,让 Hook 对象方法运行时,可以按顺序执行 before、原方法以及 after。

通过读Xposed源码发现其启动过程:

1、手机启动时init进程会启动zygote这个进程。由于zygote进程文件app_process已被替换,所以启动的时Xposed版的zygote进程。

2、Xposed_zygote进程启动后会初始化一些so文件(system/lib system/lib64),然后进入XposedBridge.jar中的XposedBridge.main中初始化jar包完成对一些关键Android系统函数的hook。

3、Hook则是利用修改过的虚拟机将函数注册为native函数。

4、然后再返回zygote中完成原本zygote需要做的工作。

这只是在宏观层面稍微介绍了下Xposed,要想详细了解需要读它的源码了。下面两篇写的挺好,要想深入理解的可以看看。
Android Hook框架Xposed原理与源代码分析
深入理解Android之Xposed详解

那么这里就引出了一个问题,为什么要替换app_process程序呢?

Android 系统是基于 Linux 的,其第一个由内核启动的用户进程是 init 进程。init 进程随后会创建 zygote 进程,Android 应用程序进程都是由 zygote 进程孵化而来。zygote 所对应的可执行程序是 app_process,xposed 框架通过替换系统的 app_process 可执行文件以及虚拟机动态链接库,让 zygote 在启动应用程序进程时注入框架代码,进而实现对应用程序进程的劫持。

每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例。这也是Xposed选择替换app_process的原因。

Zygote进程在启动的过程中,除了会创建一个Dalvik虚拟机实例之外,还会将Java运行时库加载到进程中来,以及注册一些Android核心类的JNI方法来前面创建的Dalvik虚拟机实例中去。注意,一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库。这也就是可以将XposedBridge这个jar包加载到每一个Android应用程序中的原因。

XposedBridge有一个私有的Native(JNI)方法hookMethodNative,这个方法也在app_process中使用。这个函数提供一个方法对象利用Java的Reflection机制来对内置方法覆写。

综上就是Xposed框架选择app_proess作为入口的一个原因,因为这个入口有两个好处:

1、一旦修改了,就可以修改了所有的app

2、这里的时机是最早的,可以加载所有的东西

二、Dexposed原理

前置知识点:修改非native方法为native方法

如何将Android中一个非native方法改成native的,并且可以将这个native方法指定成特定执行的函数。要对一个java函数进行hook需要步骤有
[1] 把修改method的属性修改成native
[2] 修改method的registersSize、insSize、nativeFunc、computeJniArgInfo
[3] RegisterNatives注册目标method的native函数

原理简述:

在Dalvik虚拟机下,主要是通过改变一个方法对象方法在Dalvik虚拟机中的定义来实现,具体做法就是将该方法的类型改变为Native并且将这个方法的实现链接到一个通用的Native Dispatch方法上。这个 Dispatch方法通过JNI回调到Java端的一个统一处理方法,最后在统一处理方法中调用before, after函数来实现AOP。在Art虚拟机上目前也是通过改变一个 ArtMethod的入口函数来实现。Alibaba-Dexposed Bug框架原理及源码解析_开发者-CSDN博客_dexposed原理

这里给出大致流程:

1. 调用DexposedBridge.findAndHookMethod,进入到DexposedBridge类(Java层逻辑)
2. 通过XposedHelpers.findMethodExact找到要hook的java方法,然后再用hookMethod进行真正的hook。
3. hookMethod方法先把hook成功后的callback、要hook的方法的参数和返回值类型保存到AdditionalHookInfo中,把它作为参数传给hookMethodNative。hookMethodNative是一个native方法,它的第3个参数slot表示该Method在class的方法表中所处的位置,在native的实现中会用到这个slot。
4. hookMethod 方法中调用hookMethodNative,执行Native层方法。
6. com_taobao_android_dexposed_DexposedBridge_hookMethodNative (JNI入口),把Java层传递的信息构造成DexposedInfo信息,然后设置hook方法accessFlags设置为ACC_NATIVE ,即native方法,并且指定nativeFunc函数 ( 为什么会这么做: method的结构体表示了一个Java层函数, 而其中的accessFlags属性如果是ACC_NATIVE , 则dvm在调用原Java函数的时候, 会转向调用属性nativeFunc 所指向的函数)。
7. native层用dvm的各种函数来操作Method的指针和对象来控制函数,最后通过反射调用handleHookedMethod回到Java层的方法handleHookedMethod,回到Java世界。
阿里 Dexposed 热修复原理_小嵩的博客-CSDN博客_dexposed原理

主要分为Java层和Native层的,通过下图一目了然:

一、Java层

1)需要hook的方法对象

2)需要hook的方法所属的类

3)额外附加信息,比如我们需要hook方法的回调的对象

4)handleHookedMethod方法,这个方法是整个Dexposed框架Java层最核心的一个方法,这个方法就是用来替换我们需要hook的那个方法,具体如何替换的等下面说到native层再说。然后这个方法中做了三件事:

1、执行需要hook之前的所有回调方法beforeMethod

2、执行被hook的原生方法

3、执行需要hook之后的所有回调方法afterMethod

二、native层的实现 dexposed.cpp

在com_taobao_android_dexposed_DexposedBridge_hookMethodNative函数中主要做了两件事:

        1)把Java层传递的信息构造成DexposedInfo信息

        2)设置hook方法为native,并且指定nativeFunc函数

最后在执行第二步中的nativeFunc函数dexposedCallHandler函数中主要做了两件事:

        1)获取刚刚构造的DexposedInfo信息

        2)调用Java层DexposedBridge.java中的handleHookedMethod方法

所以我们在整个过程中可以看到,先通过JNI注册,从Java世界转到Native世界,然后在native世界中主要修改被hook方法的一些信息,然后在通过反射调用handleHookedMethod回到Java世界。
Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复_尼古拉斯.赵四的博客-CSDN博客_dexposed

三、Epic原理

直接看作者的讲解:

我为Dexposed续一秒——论ART上运行时 Method AOP实现 | Weishu's Notes

ART虚拟机方法调用原理:

在ART中,每一个Java方法在虚拟机内部都由一个ArtMethod对象表示(native层,实际上是一个C++对象),这个native 的 ArtMethod对象包含了此Java方法的所有信息,比如名字,参数类型,方法本身代码的入口地址(entrypoint)等,一个Java方法的执行非常简单:

1、想办法拿到这个Java方法所代表的ArtMethod对象

2、取出其entrypoint,然后跳转到此处开始执行

ART有什么特别的?

为什么Dexposed能够在Dalvik上为所欲为,到ART时代就不行了呢?排除其他非技术因素来讲,ART确实比Dalvik复杂太多;更要命的是,从Android L到Android O,每一个Android版本中的ART变化都是天翻地覆的,大致列举一下:

  • Android L(5.0/5.1) 上的ART是在Dalvik上的JIT编译器魔改过来的,这个编译器会做一定程度的方法内联,因此很多基于入口替换的Hook方式一上来就跪了。
  • Android M(6.0) 上的ART编译器完全重新实现了:寄存器分配方式改变。且不说之前在Android L上的Hook实现要在M上重新做一遍,这个编译器的寄存器分配比quick好太多,结果就是hook实现的时候你要是乱在栈或者寄存器上放东西,代码很容易就跑飞。
  • Android N(7.0/7.1) N 开始采用了混合编译的方式,既有AOT也有JIT,还伴随着解释执行;混合模式对Hook影响是巨大的,以至于Xposed直到今年才正式支持Android N首先JIT的出现导致方法入口不固定,跑着跑着入口就变了,更麻烦的是还会有OSR(栈上替换),不仅入口变了,正在运行时方法的汇编代码都可能发生变化;其次,JIT的引入带来了更深度的运行时方法内联,这些都使得虚拟机层面的Hook更为复杂。
  • Android O(8.0) Android O的Runtime做了很多优化,传统Java VM有的一些优化手段都已经实现,比如类层次分析,循环优化,向量化等;除此之外,DexCache被删除,跨dex方法内联以及Concurrent compacting GC的引入,使得Hook技术变的扑朔迷离。

编译器的优化:

编译优化 - 循环优化 - 知乎

1、循环体优化 2、inlining 内联优化 3、充分利用寄存器 4、去除冗余操作

5、使用性价比高的指令 6、循环展开 7、循环流水线 8、向量化

Xposed原理: 

Xposed原理简介及其精简化 - 简书

Xposed 实现原理分析_l0neman 的博客-CSDN博客_xposed原理

Dexposed原理

Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复_尼古拉斯.赵四的博客-CSDN博客_dexposed

Epic原理

我为Dexposed续一秒——论ART上运行时 Method AOP实现 | Weishu's Notes

要编写 Xposed 模块的 XML 文件来 hook 系统框架,可以按照以下步骤进行: 1. 在模块的 res/xml 目录下创建一个新的 XML 文件,例如 hook_system_framework.xml。 2. 在 XML 文件中添加一个 `xposedmodule` 元素,并设置 `package` 属性为 Xposed 模块的包名。 ```xml <xposedmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://github.com/rovo89/XposedInstaller/blob/master/assets/xposedmod.xsd" package="com.example.myxposedmodule"> </xposedmodule> ``` 3. 在 `xposedmodule` 元素中添加一个 `hook` 元素,并设置 `class` 属性为要 hook 的类的全名(例如 android.app.Activity)。 ```xml <xposedmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://github.com/rovo89/XposedInstaller/blob/master/assets/xposedmod.xsd" package="com.example.myxposedmodule"> <hook class="android.app.Activity"> </hook> </xposedmodule> ``` 4. 在 `hook` 元素中添加一个或多个 `method` 元素,每个 `method` 元素代表一个要 hook 的方法,可以设置 `name` 属性来指定方法名,还可以添加一个 `parameter-types` 元素来指定方法参数类型。 ```xml <xposedmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://github.com/rovo89/XposedInstaller/blob/master/assets/xposedmod.xsd" package="com.example.myxposedmodule"> <hook class="android.app.Activity"> <method name="onCreate" parameter-types="android.os.Bundle"> </method> <method name="onResume"> </method> </hook> </xposedmodule> ``` 5. 在 `method` 元素中添加一个或多个 `hook` 元素,每个 `hook` 元素代表一个 hook 回调函数,可以设置 `inline` 属性为 true 或 false,来指定回调函数是否在原函数之前执行还是之后执行。 ```xml <xposedmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://github.com/rovo89/XposedInstaller/blob/master/assets/xposedmod.xsd" package="com.example.myxposedmodule"> <hook class="android.app.Activity"> <method name="onCreate" parameter-types="android.os.Bundle"> <hook inline="true"> com.example.myxposedmodule.MyHookClass.onCreate </hook> </method> <method name="onResume"> <hook inline="false"> com.example.myxposedmodule.MyHookClass.onResume </hook> </method> </hook> </xposedmodule> ``` 以上就是编写 Xposed 模块的 XML 文件来 hook 系统框架的基本步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值