Xposed知多少?

本文简要介绍了下Xposed框架的工作原理,详细分析了Xposed提供的挂钩函数,以及以Inspeckage插件为例介绍了Xposed插件的开发。希望对刚接触使用Xposed的人有所帮助。

Xposed知多少?

作者:宜人贷安全应急响应中心

前言


Xposed是Android平台上的一个框架,可以加载很多插件程序,这些插件可以使用Xposed框架提供的API实现对系统以及其他应用程序的hook。本文将简要介绍下Xposed原理及插件开发。

20.PNG

zygote进程


首先我们需要了解一下Android系统中的zygote(受精卵)进程,有助于对后续内容的理解。在Android中,zygote是整个系统创建新进程的核心进程。zygote会先创建一个Dalvik虚拟机实例,继而加载一些必要的系统资源和系统类,最后进入一种监听状态。在之后的运作中,当其他系统模块或应用程序希望创建新进程时,只需向zygote进程发出请求,zygote进程监听到该请求后,会相应地fork出新的进程,这个新进程具有zygote的Dalvik虚拟机拷贝以及系统资源。

Xposed构成


下面我们看看Xposed的构成,Xposed包含几个部分:

1. Xposed:Xposed框架的C++代码部分,它将被XposedInstaller用于替换原生的zygote(zygote就是/system/bin/app_process),并且为XposedBridge提供JNI方法。可以理解为它就是Xposed版的zygote。

2. XposedBridge:Xposed框架的JAVA代码部分,编译出来是一个XposedBridge.jar包,Xposed版的zygote进程启动时会加载该jar包,Xposed插件的开发与运行都是基于该jar包。

3. XposedInstaller:负责安装Xposed框架以及管理Xposed插件,运行需要root权限。

4. XposedTools:XposedTools用于辅助编译Xposed和XposedBridge。

安装Xposed操作


要使用Xposed框架,首先要安装它,当XposedInstaller 安装Xposed框架时有以下操作:

1、将Xposed版的zygote(app_process)拷贝到/system/bin/,系统原来的app_process改名为app_process.orig。

2、将XposedBridge.jar放到/data/data/de.robv.android.xposed.installer下。

3、删除 /data/data/de.robv.android.xposed.installer/conf/disabled文件。如果有disabled文件则表明整个Xposed功能被禁止使用。

启动后Xpose操作


Xposed框架安装完成后,我们看看Android系统启动后Xposed框架都干了些什么:

1.PNG

1.    Android系统启动后的第一个进程是Init进程,该进程将会启动/system/bin/app_process,该路径的app_process已经被替换为Xposed版app_process,所以启动的是Xposed版zygote。

2.     Xposed版zygote进程在启动时会创建一个Dalvik虚拟机实例,以及注册一些Android核心类的JNI方法到Dalvik虚拟机实例中去。同时Xposed版zygote把XposedBridge.jar添加到CLASSPATH环境变量,并将Java运行时库加载到进程中。一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库,所以XposedBridge.jar可以被加载到每一个Android应用程序中。

3.     Xposed版zygote在启动时还会获得一个JNIEnv实例,该实例描述的是zygote进程的主线程的JNI环境,Xposed版zygote进程通过JNIEnv实例的成员函数CallStaticVoidMethod()调用de.robv.android.xposed.XposedBridge的main函数作为java代码的入口点。

4.     de.robv.android.xposed.XposedBridge.main函数做了以下几件事:

(1)    初始化xposed框架。

(2)    调用initForZygote()方法hook应用进程创建时调用的一些关键函数,比如通过挂钩LoadedApk的构造函数获得应用进程的相关信息并保存至XC_LoadPackage.LoadPackageParam的实例中,该实例在后续hook应用程序中的函数时可用于获取应用程序相关信息。通过挂钩handleBindApplication方法,可以在应用程序启动时调用所有IXposedHookLoadPackage类型的钩子(其实最终调用的是IXposedHookLoadPackage的handleLoadPackage方法)。该类型的钩子用于对应用程序进行挂钩,假如要hook应用程序中的函数,我们编写的xposed插件中的钩子类必须实现IXposedHookLoadPackag接口,重写它的handleLoadPackage方法并在方法体中调用xposed框架提供的挂钩函数(比如findAndHookMethod)hook想要挂钩的应用程序函数。

(3)    调用loadModules()加载所有的xposed插件,将这些插件中不同钩子类型的钩子分别保存起来。有三种类型的钩子,IXposedHookLoadPackage类型的钩子对应用程序挂钩,IXposedHookZygoteInit类型钩子对Zygote的初始化进行挂钩,IXposedHookInitPackageResources类型钩子对资源进行挂钩。

(4)    最后再调用原始的ZygoteInit.main函数,完成zygote的全部初始化工作。

挂钩函数findAndHookMethod


接下来的内容我们主要来分析下initForZygote()中使用的hook方法,也就是xposed框架提供的挂钩函数findAndHookMethod,该挂钩函数也是xposed插件开发中最主要使用的函数。当然xposed框架还提供了挂钩函数findAndHookConstructor来hook构造函数,该函数与findAndHookMethod类似,在这就不做介绍了。

2.PNG


一、 findAndHookMethod


8.png

1. 方法参数说明

clazz: 被hook方法所在的类

methodName: 被hook方法的名字

parameterTypesAndCallback: 被hook方法的参数的类型(比如String.class),以及最后一个参数必需是XC_MethodHook钩子对象。

2. 调用findMethodExact方法获得被hook方法在java层的Method对象(Method对象描述了被hook方法的全部信息,包括方法修饰符、方法名、参数列表等等)。

3. 然后将java层Method对象及XC_MethodHook callback钩子对象作为参数调用XposedBridge.hookMethod方法。

二、 hookMethod


9.png

1、该方法一开始判断只能hook具体的方法或构造函数,不能hook接口方法或抽象函数。

2、sHookedMethodCallbacks是个hashmap存放了所有被hook的方法及其钩子集合,钩子集合callbacks是一个自定义的set(CopyOnWriteSortedSet)。该方法将钩子对象XC_MethodHook callback添加到callbacks。

3、接下来通过getIntField方法获取java层Method对象的slot变量值,如果是ART模式下slot值设为0。slot值在后面将被用于获取被hook方法在Dalvik中的Method结构体。在Dalvik虚拟机内部,每个Java方法都有一个对应的Method结构体,虚拟机根据此结构体获取方法的所有信息。Method结构体是hook的关键,还要注意java层的Method和Dalvik中的Method结构体并不是一个东西。

4、最后调用JNI方法hookMethodNative,该方法进行了主要的hook操作。该方法的native实现是XposedBridge_hookMethodNative。

三、 XposedBridge_hookMethodNative


10.png

1、以slot为参数调用dvmSlotToMethod获取被hook方法在Dalvik中的Method结构体Method* method。

2、然后通过memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct))将method指针指向的内存中的Method结构体复制一份到hookInfo指针所指向的内存,这一步操作是为了后续能够调用原始的被hook方法。

3、调用SET_METHOD_FLAG(method, ACC_NATIVE)将method结构体成员变量accessFlags设置为ACC_NATIVE,表示该方法为JNI native方法。

4、设置method结构体成员变量nativeFunc为hookedMethodCallback方法。JVM通过dvmCallMethod执行java方法时,会调用dvmCallMethodV,该方法先判断java方法的Method结构体成员变量accessFlags为ACC_NATIVE的话,就调用nativeFun指向的native方法。否则的话,就说明结构体Method描述的是一个Java函数,这时候就需要继续调用函数dvmInterpret来解释执行该函数的java字节码。所以当被hook方法被调用时,真正被调用的是hookedMethodCallback方法。

5、设置method结构体成员insns为hookInfo。insns保存方法的执行地址。在这里保存的hookInfo实际上是指向被hook方法原始method结构体的指针,后续将被用于调用原始的被hook方法。

四、hookedMethodCallback


下面我们看看hookedMethodCallback方法:

11.png

1、    从method的insns中取出hookInfo再赋给original,所以original现在指向被hook方法原始method结构体。

2、    调用方法dvmCallMethod(),参数methodXposedBridgeHandleHookedMethod对应的是JAVA层方法handleHookedMethod的Method结构体指针。dvmCallMethod实际上调用的是JAVA层方法handleHookedMethod。

五、 handleHookedMethod


 接下来看看handleHookedMethod方法:

12.png

13.png

1、if (disableHooks)判断是否禁止挂钩,如果禁止了,则调用native方法invokeOriginalMethodNative执行原始的被hook方法,后面再详细介绍该方法。

2、前面介绍过callbacks是个钩子对象集合,这里调用getSnapshot()获得钩子对象数组callbacksSnapshot,判断数组长度,如果为0即被hook方法无钩子对象,则调用invokeOriginalMethodNative执行原始的被hook方法。

3、创建MethodHookParam param对象,保存被hook方法的Method结构体、参数等信息。

4、递增beforeIdx从头逐个调用钩子对象的beforeHookedMethod(param)方法,同时判断param.returnEarly的值,如果值为true则跳过后续钩子对象的beforeHookedMethod和afterHookedMethod。

5、判断param.returnEarly的值,如果为true则跳过原始被hook方法的调用,如果为false则调用原始被hook方法。

6、递减beforeIdx从后往前逐个调用钩子对象的afterHookedMethod(param)方法。

六、XposedBridge_invokeOriginalMethodNative


下面看看invokeOriginalMethodNative在native层的实现:

14.png

1、Method* meth = (Method*) args[1] 将参数originalMethodId赋给Method结构体指针meth,而originalMethodId就是hookedMethodCallback方法中的original,即指向被hook方法原始method结构体的指针。

2、pResult->l = dvmInvokeMethod(thisObject, meth, argList, params, returnType, true) ,由于传入的参数是meth,所以最终调用执行的是被hook前的方法原始代码。

开发Xposed插件


 到此我们了解了Xposed框架提供的挂钩函数的实现,下面看看如何利用挂钩函数开发Xposed插件:

 1.    在AndroidManifest.xml里声明三个meta-data告诉Xposed自己是一个插件APP。

   <meta-data

       android:name=”xposedmodule”

       android:value=”true”/>  //标识为xposed插件

   <meta-data

       android:name=”xposeddescription”

       android:value=”my app”/>  //描述功能

   <meta-data

       android:name=”xposedminversion”

       android:value=”82″/>  //说明支持的最低版本xposed框架

 

2.    在app/src/main/assets/下放一个名叫xposed_init文件,里边填写插件APP的挂钩类名,该类实现了xposed的钩子接口(IXposedHookLoadPackage或IXposedHookZygoteInit或IXposedHookInitPackageResources)。一般hook应用程序的函数时,挂钩类实现IXposedHookLoadPackage接口。

 

我们以Xposed插件Inspeckage为例,分析一下它是如何hook加解密算法的。首先我们看看通过android sdk 实现的AES算法加解密,其他算法如DES用到的API基本都一样。

15.png

16.png

hook加解密算法


接下来看看Inspeckage都hook了其中的哪些函数:

17.png

Module类实现了两个钩子接口IXposedHookLoadPackage 和IXposedHookZygoteInit,重写了IXposedHookLoadPackage接口的handleLoadPackage方法。前面介绍过当应用程序启动时,Xposed会调用所有IXposedHookLoadPackage钩子类的handleLoadPackage方法。在该方法里Inspeckage又调用了CryptoHook类的initAllHooks方法,下面看看Inspeckage在initAllHooks方法里做了什么:

18.png

19.png

1、通过findAndHookConstructor挂钩函数hook了SecretKeySpec的构造函数,由上面AES算法分析可知,该构造函数的两个参数分别是密钥和加密算法名称。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.args[0]和param.args[1]获得SecretKeySpec构造函数的两个参数,然后添加到StringBuffer sb中。

2、通过findAndHookMethod挂钩函数hook了Cipher类的doFinal方法,由上面AES算法分析可知,该方法的参数与返回值对应着明文与密文。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.args[0]和param.getResult()获得该方法的参数与返回值,然后添加到StringBuffer sb中。然后调用XposedBridge.log()将sb中保存的内容写到Xposed的日志中。

3、通过findAndHookMethod挂钩函数hook了Cipher类的getIV方法,该方法返回一个随机的初始化IV。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.getResult()获得该方法的返回值,然后添加到StringBuffer sb中。

4、通过findAndHookConstructor挂钩函数hook了IvParameterSpec的构造函数,由上面AES算法分析可知,该构造函数的参数是构造初始化IV的字符串的byte[]格式。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.args[0]获得IvParameterSpec构造函数的参数,然后添加到StringBuffer sb中。

5、通过findAndHookMethod挂钩函数hook了SecureRandom类的setSeed方法,由上面AES算法分析可知,该方法的参数是用户设置的密码。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.args[0]获得该方法的参数,然后添加到StringBuffer sb中。

6、通过findAndHookMethod挂钩函数hook了Cipher类的getInstance方法,由上面AES算法分析可知,该方法的参数是描述加密算法模式的字符串。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.args[0]获得该方法的参数,然后添加到StringBuffer sb中。

7、通过findAndHookConstructor挂钩函数hook了PBEKeySpec的构造函数,该构造函数的参数是用户设置的密码、盐、迭代次数和密钥长度。在XC_MethodHook的钩子对象中,afterHookedMethod方法通过param.args[0]和param.args[1]获得PBEKeySpec构造函数的前两个参数,然后添加到StringBuffer sb中。然后调用XposedBridge.log()将sb中保存的内容写到Xposed的日志中。

所以当应用程序调用加解密算法时,密钥、明文、密文等重要信息都会被保存到Xposed日志中。这些信息也可以在Inspeckage提供的web界面中查看,如下图:

3.png

本文简要介绍了下Xposed框架的工作原理,详细分析了Xposed提供的挂钩函数,以及以Inspeckage插件为例介绍了Xposed插件的开发。希望对刚接触使用Xposed的人有所帮助。

参考:


1. http://blog.csdn.net/Innost/article/details/50461783

2. http://blog.csdn.net/wxyyxc1992/article/details/17320911

3. http://blog.csdn.net/dingjikerbo/article/details/50448145

4. http://blog.csdn.net/wyzzgo/article/details/53647994

5. http://www.cnblogs.com/xinhuaxuan/p/6019531.html

6. https://github.com/rovo89

7. https://github.com/ac-pm/Inspeckage

8. http://www.cnblogs.com/vmax-tam/p/4624032.html

5

取消
Loading...

填写个人信息

姓名
电话
邮箱
公司
行业
职位
css.php