BT

深入理解Android(三):Xposed详解

作者 邓凡平 发布于 2016年1月4日 | 被首富的“一个亿”刷屏?不如定个小目标,先把握住QCon上海的优惠吧!

编者按:随着移动设备硬件能力的提升,Android系统开放的特质开始显现,各种开发的奇技淫巧、黑科技不断涌现,InfoQ特联合《深入理解Android》系列图书作者邓凡平,开设深入理解Android专栏,探索Android从框架到应用开发的奥秘。

一、背景

Xposed,大名鼎鼎得Xposed,是Android平台上最负盛名的一个框架。在这个框架下,我们可以加载很多插件App,这些插件App可以直接或间接操纵系统层面的东西,比如操纵一些本来只对系统厂商才open的功能(实际上是因为Android系统很多API是不公开的,而第三方APP又没有权限)。有了Xposed后,理论上我们的插件APP可以hook到系统任意一个Java进程(zygote,systemserver,systemui好不啦!)。

功能太强大,自然也有缺点。Xposed不仅仅是一个插件加载功能,而是它从根上Hook了Android Java虚拟机,所以它需要root,所以每次为它启用新插件APP都需要重新启动。而如果仅是一个插件加载模块的话,当前有很多开源的插件加载模块,就没这么复杂了。

Anyway,Xposed强大,我们可以学习其中的精髓,并且可以把它的思想和技术用到自己的插件加载模块里。这就是我们要学习Xposed的意义。

Xposed支持32位和64位的dalvik以及ART,同时支持selinux。我仔细看了下,如果拓展把这些东西都讲的话,一个是很枯燥,另外一个是背离了我们本章讲解插件的主线。所以,本章将围绕下面几个点开展介绍:

l  32位dalvik上非selinux模式下的xposed实现原理

64位、selinux模式其实难度不在虚拟机上,而是在selinux上。

二、  初识Xposed

本章先来介绍下Xposed,这是一个大工程,包含多个项目,我也是花了不少时间才把它整个玩转起来的。Xposed包含如下几个工程:

  • XposedInstaller,这是Xposed的插件管理和功能控制APP,也就是说Xposed整体管控功能就是由这个APP来完成的,它包括启用Xposed插件功能,下载和启用指定插件APP,还可以禁用Xposed插件功能等。注意,这个app要正常无误得运行必须能拿到root权限。
  • Xposed,这个项目属于Xposed框架,其实它就是单独搞了一套xposed版的zygote。这个zygote会替换系统原生的zygote。所以,它需要由XposedInstaller在root之后放到/system/bin下。
  • XposedBridge。这个项目也是Xposed框架,它属于Xposed框架的Java部分,编译出来是一个XposedBridge.jar包。
  • XposedTools。Xposed和XposedBridge编译依赖于Android源码,而且还有一些定制化的东西。所以XposedTools就是用来帮助我们编译XposedXposedBridge的。

提示,提示,提示:

我把所有相关代码都下载并放到下面的地址了:

https://code.csdn.net/Innost/xposed-learning  里边包含:

1  xposed所有代码库的内容

2  XposedDemo:Xposed插件APP Demo,非常简单

3  XposedDemoTarget:XposedDemo将hook上的目标App

2.1  编译Xposed

下面介绍下如何编译Xposed,这里以Android 4.4.4为例。做Android开发最好配一个Nexus手机或Pad。我用得是Nexus 7 2013Wi-Fi版。Anyway,Xposed的编译和具体机器无关,不过下面的前提条件需要满足:

  • 准备Android 4.4.4源码。我分享了很多AOSP源码,注意:我提供的源码没有.repo和.git,虽然省了很多空间,不过在编译Xposed的时候,还是需要有.repo。
  • 下载我提供的Xposed源码和测试Demo。

好,马上开始我们的步骤:

2.1.1  下载支持库

根据XposedTools的说明,我们先要修改下AOSP源码里的.repo,具体步骤如下:

  • 进入AOSP/.repo目录。
  • 在.repo目录下新建local_manifests目录。
  • 把XposedTools/local_manifests/下的目标文件拷贝过去。local_manifests/目录下是各种API版本(即SDK=19,21之类)对应的xml文件。由于本例对应的SDK版本是19,所以需要把该目录下xposed_sdk19.xml文件拷贝到.repo/local_manifests/目录下。

这个文件是什么内容呢?来看图1:

这个文件内容是啥意思呢?

  • <remote>:新添远程仓库地址为github。
  • 第一个<project>:为frameworks/base/cmds/下新增加xposed工程。当然,这个工程我们刚才下载过了。你可以直接copy到指定目录下。
  • <remove-project>:移除AOSP/build目录。xposed有自己的处理。
  • 第二个<project>:下载xposed自己的build。path="build",表示下载路径就是AOSP/build。

配置好后,请在AOSP目录下执行repo sync。这样它会根据manifests更新AOSP源码。当然,也可以只是下载frameworks/base/cmds/xposed工程和更新build工程。

注意,repo sync是一个重型操作,会导致所有工程都进行一次同步。我建议的方法是直接下载和更新对应的工程

比如,下载xposed工程,用repo sync frameworks/base/cmds/xposed即可。对于build目录,先把原来的build挪到其他地方。然后repo sync build即可。

好了,到此,所有源码都已经ready了。

2.1.2  修改build.conf

下面我们进入XposedTools目录,然后修改其中的build.conf文件。该文件用于指示AOSP源码等参数。如图2所示:

XposdTools提供了一个build.conf.sample模板,图2中的build.conf文件是在这个模板基础上修改而来。红框中是我修改的结果。其他选项没有变化。

2.1.3  执行build.pl

到XposedTools目录下,执行:./build.pl -t arm:19命令,这表明我要编译arm平台上SDK=19版本的xposed框架。注意,./build.pl --help会打印出使用方法。build.pl是一个perl脚本。图3是编译过程截图:

图3中,你可以发现build.pl跑到AOSP源码目录下,执行了:

  • . build/envsetup.sh:初始化AOSP编译环境
  • lunch aosp_flo-userdebug:选择交叉编译平台。注意,这一块我是修改了XposedTools/xposed.pom,使它单独为我的nexus 7编译,而不是针对ARM平台做generic的编译。
  • make -j4 xposed libxposed_dalvik:编译xposed和libxposed_dalvik这两个目标文件。

在使用build.pl时,它还依赖一些Perl的类库,请童鞋们按照下面步骤下载这些依赖库:

sudo apt-get install libconfig-inifiles-perl

sudo apt-get install libio-all-perl

sudo apt-get install libfile-readbackwards-perl

sudo apt-get install libfile-tail-perl

sudo apt-get install libtie-ixhash-perl

build.pl执行过程中,如果报还有其他依赖库未找到,请通过下面命令

apt-cache search perl XXX  来查找需要apt-get install哪个目标库。XXX是build.pl执行过程中报错时提供的库信息

2.1.4  编译结果

编译完成后,将产生一个zip包到AOSP/out/sdk19/arm下。AOSP/out是我在build.conf中指定的目录。如图4所示:

编译结果是一个xposed-v65-arm-custom-build-xyz-20151030.zip包,这个包可以通过recovery刷到手机上。包的内容就是files文件夹下的内容,包含:

  • system/bin/app_process_xposed:xposed版zygote。
  • system/bin/libxposed_dalvik.so:xposed框架的native层。
  • system/xposed.prop:xposed框架信息,包含版本号等。内容如右边图所示。

三、  XposedInstaller

XposedInstaller是Xposed的App,用于管理Xposed框架和插件App。本节我们主要讨论它是如何安装Xposed框架和插件App的。

XposedInstaller启动界面如图5所示:

图5中可知XposedInstaller提供好几项子页面。第一个“框架”用来安装或卸载Xposed框架的。我们来看它。

3.1  安装Xposed框架

如图6所示:

注意,图6右上角的“程序自带”两个版本号,分别是app_process版本号为58,XposedBridge.jar版本号是54.

而“激活”这两项为空,因为我们的系统还没有安装Xposed框架。

“程序自带”是什么意思?原来,XposedInstaller在自己的assets目录下携带了所需要的xposed框架程序和模块,如图7所示:

从图7可知,XposdInstaller自带了xposed版zyote(比如app_process_xposed_sdk16),为了更好得支持不同版本的Android,它还区分了SDK版本。另外,XposedInstaller也支持刷机包把xposed框架模块刷入系统,比如Xposed-Installer-Recovery.zip,里边包含的主要内容就是一个脚本,在recovery模式下运行,其内部也是把assets里的文件拷贝到/system相关目录中。这一块我们后续看代码就知道怎么玩儿了。

安装Xposed框架的主要功能由InstallerFragment.java提供,我们看看相关代码。

3.1.1  onCreateView

onCreateView函数是Fragment里初始化UI的核心回调,其代码如图8所示:

图8代码中最后的refreshVersions用于获取版本号,也就是图6中右上角“程序自带”要显示的信息,它包括两个东西:

  • xposed版app_process的版本,包括程序自带(也就是XposedInstaller放在assets目录下的)和系统安装的。当然,只有该系统安装过Xposed版app_process才有必要检查它的版本。图6中由于没有装过,所以“激活”那一栏显示为“-”。
  • XposedBridge.jar:也包括程序自带和系统已安装两个jar包的版本。

检查版本主要是为了兼容性考虑。代码中的refreshVersions用于获取他们的版本号,代码如图9所示:

图9中:

  • getInstalledAppProcessVersion:读取/system/bin/app_process的版本号。
  • getLatestAppProcessVersion:读取自带app_process_sdkXX的版本号。
  • getJarInstalledVersion,读取/data/data/de.robv.android.xposed.installer/bin/ XposedBridge.jar版本号。对,你没看错,XposedBridge.jar是放在XposedInstaller自己的目录下的。
  • getJarLatestVersion:读取自带XposedBridge.jar的版本号。

想知道怎么获取版本系想你吗?来看图10:


 

  •  对于app_process,直接读取文件内容,然后找到红框中的那一行,后面的58就是版本号。
  •  对于XposedBrdige.jar,打开这个jar包,读取assets/VERSION的内容,里边就是存储了一个版本号信息。

太简单了,不惜得说。

注意XposedBridge.jar包安装版的位置,它在/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar里

3.1.2  安装xposed框架

InstallerFragment的install函数用于安装xposed框架相关的模块。这个函数一点也不复杂,不过还是给大家看看。如图11所示:

install函数没什么难度,但是我们要总结下xposed框架安装到底做了什么手脚:

  • 将xposed版的app_process拷贝到/system/bin/,系统原来的app_process改名为app_process.orig
  • 将XposedBridge.jar放到/data/data/de.robv.android.xposed.installer
  • 删除/data/data/de.robv.android.xposed.installer/conf/disabled文件。如果有disabled文件则表明整个Xposed功能被禁止使用。

嗯嗯,也没什么太多可说的。

3.1.3  安装xposed插件

xposed插件,在xposed世界里我们说它是插件,但是放到Android世界里它就是一种特殊的APP。这种类型的APP由xposed框架识别并加载,然后hook到其他的App进程。

当然,这里既然提到进程,那么这些APP就必须要被先启动起来才是。

XposedInstaller的插件管控由ModulesFragment界面来处理。本节主要想介绍下XposedInstaller是怎么对待Xposed插件APP的。

来看代码,如图12所示:

我们重点来看看Xposed插件APP是怎么被load的,代码其实在ModuleUtil的reloadInstalledModules函数中。代码很简单如图13所示:

图13左下角已经告诉各位Xposed插件APP该是什么样的了,右下角是XposedDemo示例。

在AndroidManifest.xml里定义这些东西只是告诉Xposed自己是一个插件APP。但是作为一个挂钩插件,Xposed还需要知道这个APP里哪个类是用来挂钩的。这句话的意思是:

1  这个APP是一个插件APP。该APP包含很多功能。

2  这个APP包含的众多功能中,有一个功能是给目标进程挂钩。挂钩操作是Xposed框架来做的,所以它需要知道该APP中哪个类是继承了Xposed钩子接口。这样,Xposed框架找到插件APP之后会触发这个app的钩子接口进行挂钩。

要做到第二步就得在assets/下放一个名叫xposed_init文件,里边指明插件APP的挂钩类名。XposedDemo的

assets/xposed_init的内容就是:com.xposed.demo.MyXposedModule。当然,这个插件类必须实现Xposed的IXposedHookLoadPackage接口类。这个我们以后再讨论。

四、  Xposed框架介绍

Xposed框架分为xposed版app_process和XposedBridge.jar两部分。app_process就是zygote,我们先看看xposed版的zygote干了些什么。

4.1  Xposed版zygote

注意,本章只分析32位,dalvik版的xposed app_process,其入口main函数位于app_main.cpp里。

图14所示的代码展示了Xposed版zygote与众不同之处。

图14中,左上角的框是app_main的main函数,里边有两处不同之处:

  • AppRuntime:此处的AppRuntime类是xposed版的AppRuntime。它的与众不同之处从左下图的onVmCreated开始。当检查到isXposedLoaded为真是时,将调用xposed::onVmCreated函数。
  • xposed::onVmCreated函数位于右上框,它先判断当前虚拟机是ART还是dalvik,然后加载xposed一个特殊so,此处是/system/bin/libxposed_dalvik.so。注意,这里的xposed框架和XposedInstaller略有区别。因为XposedInstaller比较旧了(也就支持sdk 16的样子),还没有涉及到ART和dalvik的区别。在onVmCreate中,它将调用libxposed_dalvik.so中的xposedInitLib函数,然后再调用so中设置的onVmCreated函数。这个onVmCreated函数由xposedInitLib设置。
  • 左上框中还有一个xposed:initialized函数。这个函数初始化了XposedShared类型的全局变量xposed,同时还把一个重要的jar包加到了CLASSPATH里。来看代码。如图15所示:

图9展示了initialize函数的内容,主要是最后一个把/system/bin/XposedBridge.jar(这个jar包的位置和我们在XposedInstaller那里看到得不同,原因前面解释过了)加到CLASSPATH比较重要。

注意,我们这里虽然对initialize介绍的内容很少,但实际上这个函数要真正看明白还是很需要技术实力的:

1  logcat:start:里边fork了logcat进程用来存储xposed自己的log

2  service:startAll:为了完美支持selinux,这里的处理更是很有技巧。selinux是一个完整的知识体系,想彻底掌握它的童鞋请参考我的三部曲文章《深入理解SELinux SEAndroid》 

1&2其实很真实得反映出xposed的作者在Android、Linux上水平很高,经验很丰富。

最后,如果xposed框架启用成功,那么zygote的入口类将由以前的com.android.internal.os.ZygoteInit变成de.robv.android.xposed.XposedBridge

下面我们按照执行流程,把相关函数分析一遍:

  • AppRuntime的onVmCreated,它最终会导致libxposed_dalvik.so中的onVmCreated被调用。我们直接分析最后这个onVmCreated
  • 然后AppRuntime里会调用de.robv.android.xposed.XposedBridge.main函数。

4.2  onVmCreated

图16为代码:

onVmCreated比较简单了:

  • 针对android/content/res/XResource&XTypedArray进行了一些处理,这是为将来资源替换时候做准备。以后我们碰到再说。
  • XposedBridge注册JNI函数

xposed官方说MIUI大量用了xposed的东西,并且不共享,以后碰到MIUI的问题他们不再支持。Don't know what to say....

4.3  XposdBridge.main函数

main函数代码如图17所示:

main函数里有三个重要函数:

  • initNative:好好学习下
  • initForZygote:好好学习下
  • loadModules:加载App插件

4.3.1  initNative

initNative很重要,来看代码,如图18所示:

图18中,我们重点看一下callback_XposedBridge_initNativeregister_natives_XResources这两个函数。这两个函数比较简单,我们统一放到图19中:

不多说了,没什么难度。

4.3.2  initForZygote

从这个函数开始,xposed就开始给系统一些关键函数挂钩子了。我们看看它怎么玩儿的。代码如图20所示:

图20中我在eclipse里用了代码缩略显示的方法,可知一共有五个框,分别hook了一些关键内容。我们从上到下一次分析。先来分析下Xposed框架提供的挂钩函数findAndHookMethod

(1)  findAndHookMethod介绍

findAndHookMethod用来对指定类的指定函数进行挂钩。这个函数很重要,开发插件APP时用得最多。来看它的代码,如图21所示:

findAndHookMethod代码Java层面的逻辑还是比较好理解的:

  • 首先是通过class相关的函数找到指定的函数对象。这里的查找需要指定函数所在的类,函数名,函数参数信息等,属于精确(exact)查找。注意,findAndHookMethod函数的最后一个参数必须是XC_MethodHook类型,即钩子对象的数据类型。
  • 然后就是XposedBridge.hookMethod

来看hookMethod,如图22所示:

hookMethod可知,一个目标函数可以挂多个钩子,这些钩子由一个集合来存储。然后我们将转到JNI层去看看hookMethodNative干了什么事情。这才是hook的核心。代码如图23所示:

hookMethodNative完成了真正的挂钩处理,其思想很简单:

  • 先找到目标函数在native层对应的Method对象。
  • 修改这个Method为native方法,并设置nativeFunchookedMethodCallback函数。
  • 然后还要保存原函数的一些信息到insns域。

我们在《深入理解Android之dalvik》一文中介绍过,JVM调用java函数时候,发现这个函数为native的话,就调用它的nativeFunc。下面我们看看钩子函数的调用。

(2)  调用钩子函数

hook钩子函数后我们就要调用它。上一节我们发现xposed在挂钩子的时候会把原函数改造成native属性(即Dalvik会按native函数的方式调用它),对应的nativeFunc是hookedMethodCallback,其代码如图24所示:

注意喔,我们现在已经在处理被挂钩函数的调用了喔....从JNI会进入到java层的钩子函数dispatch总入口,及handleHookedMethod,这个函数比较复杂,我们一段一段来看它。

很简单。接着看第二段,如图26所示:

貌似也很简单喔...

现在来看原目标函数的调用,即invokeOriginalMethodNative。代码如图27所示:

同样很容易,不多说了。到此,我们已经看到了Xposed挂钩的所有过程。好像也没什么复杂的,只要对dalvik稍微属性点,应该是比较容易做的。

当然,本文只是给大家show了主要流程,真要自己动手做,我觉得难点在于参数传递等方面。anyway,了解了大体流程,后面的事情也好办,多尝试几次就好。

下面我们回过头来看initForZygote里加的几个钩子都干了什么。

(3)  initForZygote钩子设置

图20的initForZygote代码示意中可知xposed对下面几个函数进行hook(此处先不讨论钩子函数干了什么)

  • ActivityThread.handleBindApplication:这个函数是ActivityManagerService内部调用ActivityThread.bindApplication间接触发的。该函数的详情可参考《深入理解Android卷2》第六章深入理解ActivityManagerService的“ApplicationThread的bindApplication分析”一节。handleBindApplication主要工作是初始化APP(APP由zygote进程fork而来,在hanldeBindApplication之前,这个APP进程和zygote没什么区别。只有调用完handleBindApplication之后,这个APP进程才是APP,比如该进程有了对应的名字,Aplication对象被创建等)。
  • com.android.server.ServerThread. initAndLoop函数:这个函数是给system_server用的,用于启动系统各种重要服务。
  • LoadedApk构造函数:通过hookAllConstructors来对LoadedApk各种构造函数进行挂钩。LoadedApk是APK文件在APP里的代表对象,里边有很多重要的信息。
  • android.app.ApplicationPackageManager. getResourcesForApplication:挂钩PackageManager的资源获取函数。
  • hookResources:对资源进行hook。这一块由于涉及到android APP对资源的管控,以后有机会我们再详细介绍。

4.3.3  loadModules

main函数在initForZygote之后的下一个动作就是loadModules,这就是加载所有的插件APP。我们来看看这个函数,代码如图28所示:

我们来看下loadModules的处理:

  • 先从XposedInstaller那获取当前已经安装(并启用)的插件APP列表,然后调用loadModule来加载这个APP。
  • loadModule从APP的assets/xposed_init文件里看看这个apk里声明了哪些钩子类。
  • loadModule校验这些钩子类是不是符合Xposed定义的钩子类类型,然后作对应处理。图29列出了Xposed支持的钩子类类型。

图30展示了hookLoadPackagehookInitPackageResource两个函数的内容,特别简单。

嗯嗯,就是把对应的钩子函数保存起来先。

好了,下面我们就来开始分析initForZygote里挂上的几个钩子分别有什么用。

4.3.4  handleBindApplication钩子

initForZygote为hanldeBindApplication设置了前处理钩子,代码如图31所示:

前面说过,在APP生命周期内,handleBindApplication是APP刚准备好相关信息的一个重要点,在这个点去进行挂钩处理简直是最好不过了。当然,此处的挂钩处理也就是准备好这个APP的相关信息然后调用所有的IXposedHookLoadPackage类型的钩子。

注意,除了handleBindApplication之外,由于一个APP进程事实上可以加载多个APK(比如那些申明同样的uid和运行在同一进程的APP),在LoadedApk的构造函数中也做了类似的处理

IXposedHookLoadPackage钩子一般会干些什么呢?图31对XposedInstaller的处理就很明显了。一般而言,这种钩子会对目标APP中感兴趣的函数进行挂钩(调用findAndHookMethod),比如XposedInstaller对getActiveXposedVersion进行了挂钩,用于返回系统里正在使用的Xposed框架版本。

我们应该在钩子函数里干些什么?这是一个重要问题。我也不废话了,直接上XposedDemo的源码,如图32所示:

图32是我在xposed-learning项目中提供的XposedDemo示例,可知:

  • Xposed框架对handleBindApplication进行hook后,当有APP启动的时候,该框架就会调用所有IXposedHookLoadPackage类型的钩子。
  • 这种钩子一定要区分当前被hook的APP是不是自己想要Hook的APP。如果不是,请直接return。如果是自己的目标APP,则进一步通过findAndHookMethod进行挂钩。

简单点说,我们在IXposedHookLoadPackage的handleLoadPackage中把该挂的钩子都挂上就好。

4.3.5  initAndLoop钩子

initAndLoop是system_server进程的关键函数,在这个函数里Android Framework的绝大部分Service都将被创建。真是艺高人胆大,这个进程居然都提供了挂钩处理。其代码如图33所示:

代码倒是很简单,无非是针对system_server进行hook。插件函数如果想区分被hook的进程是否为system_server的话,只需要判断packageName是否为"android"即可。

再次强调,system_server是Android Java层Framework的核心,要hook它需要万分小心,否则或导致手机系统出现会各种不稳定,崩溃,重启等情况。

下面我们介绍下Xposed框架对资源是怎么Hook的。

4.4  对资源的Hook

前面章节介绍了Xposed框架如何对代码调用逻辑进行hook。在Android APP中,除了代码逻辑外,Xposed还支持对资源进行Hook。对资源Hook的原理其实和对代码调用进行Hook的原理类似。这里我们简单介绍下Xposed框架如何对资源进行Hook。

在Android APP中,资源有三个重要类:

  • ResourcesManager:一个APP进程有一个ResourcesManager。一个ResourcesManager可以管理多个Resouces。
  • Resources:Resources是一个APK中资源的代表,注意,它不是指单独的一种类型的资源(比如字符串,图片等),它是一个APK文件中资源文件的代表。Resources对象有一个AssetManagerAssetManager能操作APK文件(其实就是一个压缩包)assets以及res目录里的内容。
  • Resources通过ResourcesKey将自己保存到ResourceManager中的一个ArrayMap里。
  • 对于资源挂钩来说,Xposed框架及插件APP做如下几件重要的事情:
  • handleBindApplication类似,在APP进程某个时间点的时候(猜测:初始化资源相关模块)进行Hook,然后调用IXposedHookInitPackageResources类型的钩子。
  • IXposedHookInitPackageResources类型的钩子通过Xposed提供的XResources类的setReplacement对原有的资源进行替换。在Xposed框架中,XResources是用于替代Resources的。
  •  App运行时候,Xposed框架截获相关的资源获取函数(比如Context的getString),先看看是否被replace了,如果是,则返回被replace的结果。

就这么简单。我们一步一步来看。

注意,XposedDemo并没有hook资源,请感兴趣的童鞋们自行加上该功能进行测试。

4.4.1  hookResource

hookResource对ResourceManager等进行了挂钩处理。来看代码,如图34所示:

hookResources主要是对ResourcesManager的getTopLevelResources进行了Hook。APP中原来使用的是Resources代表资源,Hook之后,Xposed用XResources代替了Resources。图35展示了XResources的派生关系。

接着看hookResources第二段代码,如图36所示:

第二段代码中,Xposed资源Hook框架调用了资源hook类型的钩子。同样,我们需要关注在这种类型的钩子里插件APP要干得事情,那就是:

  • 通过XResources的setReplacement函数将目标资源的id和内容进行替换。

上面替换的还是APP自己的资源,除此之外,Xposed还能替换系统资源(即framework-res.apk里声明的资源),这部分代码也在hookResources里,来看图37:

图37中,Xposed将Resources里代表系统资源的mSystem对象也进行了替换。不过这里没有独立调用回调。没关系,因为在第二部分的回调中,插件APP通过XResourcessetSystemWideReplacement可以对系统资源进行替换。

注意,hookResources之后,APP里调用的Resources相关的函数就全部转到XResources来处理了。比如获取字符串的getString函数,其最终会调用到XResources的getText函数,代码如图38所示:

到此,我们对Xposed框架如何hook资源进行了介绍。不过,layout资源的hook我这里并没有介绍,请童鞋们阅读XResources的getLayout函数和init函数。

五、 总结

到此Xposed 32位dalvik版框架基本介绍完了。这一趟绝对不是本篇这20多页文章这么轻松的事情。正如我在《深入理解Android之Dalvik》一文写得那样,我是在研究xposed的时候,发现必须要搞清楚dalvik,所以才先写了dalvik的文章,然后才能走到今天这一步。

Xposed是一个成熟框架,高度体现了开发者在Java虚拟机这块有着非常深厚的知识积累。同时,如果加上selinux的话,那开发者对linux系统也是相当相当熟悉。另外,貌似开发者是用业余时间搞出来的,这在当下上班时间强制为996的码农而言几乎是不可能的事情。

再次向开发者致敬,也同时呼吁基于xposed框架的派生框架开发者遵守相关开源协议。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论
提供反馈
错误报告
商务合作
内容合作
Marketing
InfoQ.com及所有内容,版权所有 © 2006-2016 C4Media Inc. InfoQ.com 服务器由 Contegix提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司 京ICP备09022563号-7 隐私政策
BT

We notice you’re using an ad blocker

We understand why you use ad blockers. However to keep InfoQ free we need your support. InfoQ will not provide your data to third parties without individual opt-in consent. We only work with advertisers relevant to our readers. Please consider whitelisting us.