Xposed 源码剖析(一)

  • 内容
  • 评论
  • 相关

0x00 简介



  • 是什么 Xposed     framework是一个基于Android系统实现的能够给用户提供修改系统层面或第三方APP功能的框架服务。

  • 如何工作 Android中有一个叫做Zygote的核心进程,它会随Android系统的启动而启动,然后加载系统所需的类,最后再调用初始化方法。每一个APP的进程都是从Zygote进程fork出的子进程,这个进程的文件是/system/bin/app_process。当安装Xposed framework后,Xposed framework会替换一个新的app_process/system/bin/中,同时还会替换虚拟机和其他若干文件。Zygote启动时,会加载Xposed所需的JAR包(/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar)至系统目录,并启动Xposed替换的虚拟机。 Xposed framework的主要接口由XposedBridge.jar提供,框架的核心功能在替换的虚拟机中实现。


0x01 示例代码



    用户可以使用Xposed frameworkHook方法,下面是作者本人给出的一个示例:

Xposed 源码剖析(一)

    这个示例代码实现了对Andoird系统时钟输出样子和颜色的修改,完整的源码地址是https://github.com/rovo89/XposedExamples/tree/master/RedClock。如此简单的几行代码,它的内部实现机制是怎么的呢?想知道这些,最好的办法当然是分析源码。


0x02 Hook流程分析



     这里使用Xposed framework Hook java.net.URLEncoder类的encode方法作为例子分析,调用逻辑如下所示:

Xposed 源码剖析(一)

handleLoadPackage会在任意一个APP加载的时候被调用,然后就可以在这个接口内部根据包名做逻辑判断,如果是Hook方法所在的包,则进一步调用findAndHookMethodHook指定的方法。 通过findAndHookMethod()接口找到指定方法并进行Hook。它的参数较多,第一个参数是类名,第二个参数是类加载器,第三个参数是方法名,后面是可变参数,把方法的参数类型依次作为参数,最后一个参数是XC_MethodHook()对象。另外,在其内部重写beforeHookedMethodafterHookedMethod方法,这两个方法分别会在Hook方法执行的前后执行,用户可以在其中实现自己的逻辑。 findAndHookMethod这个接口的实现在XposedHelpers.java这个文件中,代码如下:

Xposed 源码剖析(一)

     首先,接口内部会根据类名和类加载器查找Class对象,再根据Class对象和方法名去查找Method对象,最后调用XposedBridge.hookMethod完成对方法的Hook。 看一下方法查找的实现:

Xposed 源码剖析(一)

    在findMethodExact内部,fullMethodName变量就是这个方法的完全表示名,形如java.net.URLEncoder#encode(java.lang.String,java.lang.String)#exact。使用方法的完全表示名在methodCache表中查询,如果这个Method对象在表中就从表中获取并返回,如果不在就创建一个新的Method```对象放入表中并返回。

    进一步看一下XposedBridge.hookMethod方法的实现,代码片段如下:

Xposed 源码剖析(一)

     这里的逻辑比较清晰,如果这个方法不存在对应的回调对象集合,那么就创建一个并把回调对象放入这个集合中。然后如果是新Hook的方法,获取这个方法的参数和返回类型等信息作为参数去调用hookMethodNative方法,这是一个native方法,它注册的地方在libXposed_common.cpp中,注册逻辑如下:

Xposed 源码剖析(一)

NATIVE_METHOD是一个宏定义,根据这个宏定义,进一步找到hookMethodNative对应的native方法是XposedBridge_hookMethodNative。这里以art为例,这个方法的实现在libXposed_art.cpp文件中,代码如下:

Xposed 源码剖析(一)

    通过FromReflectedMethod获取要Hook方法对应的ArtMethod对象,然后再调用EnableXposedHook接口。在art虚拟机中,每一个加载的类方法都有一个对应的ArtMethod对象,它的实现在ArtMethod.cc中,下面是EnableXposedHook方法的代码实现:

Xposed 源码剖析(一)

     这部分代码是关键所在,首先创建一个备份的ArtMethod,并添加访问标志位kAccXposedOriginalMethod,表示其为Hook方法的原方法,然后为备份的ArtMethod创建对应的Method对象。把Method对象、方法额外信息和原始方法保存至XposedHookInfo结构体中,并调用SetEntryPointFromJni()把这个结构体变量的内存地址保存在ArtMethod对象中。     这个位置原本是用来保存native方法的入口地址的,既然使用了这个位置,那么就必须把对应的标志位清除,代码实现的最后调用SetAccessFlags((GetAccessFlags() & ~kAccNative & ~kAccSynchronized) | kAccXposedHookedMethod)来完成标志位的清除。这并不会有问题,此时这个ArtMethod对象对应是Hook后的方法,这个方法的实现不是native的。 接着看下面的代码逻辑,需要结合StackReplaceMethod()的实现来分析:

Xposed 源码剖析(一)

     它实现的功能是挂起所有线程并在每一个线程中查找是否存在这个修改后的ArtMethod对象,有就替换成未修改前的ArtMethod对象。

      再回到EnableXposedHook中,接着调用SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler())和SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge)分别设置机器指令和解释器的入口地址。从解释器入口进入之后会调用artInterpreterToCompiledCodeBridge,下面是artInterpreterToCompiledCodeBridge代码片段:

Xposed 源码剖析(一)

     前面的代码逻辑都先不用关注,看最后一行调用了ArtMehodInvoke方法,这正是机器指令执行的路径。所以说,当被Hook的方法被调用时,不管是机器指令还是解释器执行的,都会进入ArtMethod::Invoke()中,下面是它的代码片段:

Xposed 源码剖析(一)

   ArtMethod::Invoke()会进一步调用art_quick_invoke_stubart_quick_invoke_stub的内部实现是汇编语言的,art_quick_invoke_stub与具体的机器架构相关,这里以arm架构为例:

Xposed 源码剖析(一)

    汇编指令blx ip跳转到entry_point_from_compiled_code_指定的地址,这个地址就是通过SetEntryPointFromQuickCompiledCode()函数设置的,可知此时执行流跳入到GetQuickProxyInvokeHandler()的地址中,即artQuickProxyInvokeHandler:

Xposed 源码剖析(一)

   这个代理方法会判断当前是否是xposed环境

   如果是就调用InvokeXposedHandleHookedMethod()方法。

Xposed 源码剖析(一)

    这个方法的内部实现是,先获取保存在ArtMethod对象中的XposedHookInfo数据,然后通过CallStaticObjectMethodA接口调用XposedBridge.handleHookedMethod()方法,注意此时已经从native中跳入到JAVA里了。继续看XposedBridge.handleHookedMethod()方法的实现:


Xposed 源码剖析(一)


   进入handleHookedMethod()后,首先调用所有before method的回调方法,然后调用原始方法,最后调用所有after method的回调方法。到这里就可以看出跟之前应用层的代码形成的对应关系。

Xposed framework通过invokeOriginalMethodNative方法去调用原方法,其对应的native方法如下:

Xposed 源码剖析(一)

InvokeMethod()的实现在Reflection.cc中,代码如下所示:

Xposed 源码剖析(一)

   通过FromReflectedMethod()获取到Method对象对应的ArtMethod对象,这个就是在Hook时创建的原始ArtMethod对象的备份。然后,再通过InvokeWithArgArray调用原始的方法。

0x03 总结



    至此,完成了对Xposed framework Hook的源码分析。我们再整体看一下它的流程,当Hook一个方法时,首先需要提供方法的所在的包名、方法名、参数类型以及回调对象去调用Hook接口;Xposedart虚拟机中找到方法对应的ArtMethod对象,备份这个ArtMethod对象,然后修改这个对象的解释器和机器指令入口,关键所在。然后把每个线程中加载的这个被修改后的ArtMethod对象替换成原始备份的ArtMethod对象。最后在修改的机器指令入口地址指向的函数里,实现回调方法和原始方法的调用。

    很多细节还没有涉及

    下一期会从/system/bin/app_process的实现着手分析其框架部分代码的实现。



Xposed 源码剖析(一)


始发于微信公众号:同程研发中心