1、背景介绍
Xposed框架是Android中Hook技术的一个著名框架,还有一个框架是CydiaSubstrate,但是这个框架是收费的,而Xposed框架是免费和开源的,网上很多文章介绍Xposed框架的原理实现,本文章主要介绍如何使用这个框架。
2、按装教程
在安装这个工具之前,要先解决如下几个问题:
- 首先知道这个框架的核心点是系统进程注入技术,那么如果要注入系统进程,就必须要root权限,所以如果想用这个框架的话就必须得有一个root设备
- 然后就是这个框架的适配问题,不是所有的设备、所有的系统都支持这个框架的使用,我成功的运行Xposed框架,用的是红米Note 1,Android 版本为4.4.4
- 最后一个问题就是Xposed框架本身的版本问题,它针对不同系统发布了多个版本,所以得对于自己的设备系统安装正确的Xposed版本。
解决了这三个问题才能成功安装Xposed框架。
3、环境搭建
如果上面问题都解决了,那么恭喜你,你可以开始安装Xposed框架了。安装步骤如下:
1、首先下载安装Xposed installer apk到手机
2、打开Xposed apk,会显示下面的界面
3、点击安装,会提示重启手机生效,因为要注入系统进程,所以必须重启才能有效果。如下图所示(图中的状态已经是安装成功和激活):
这个过程中肯定有人遇到问题,个人觉得解决问题的办法就是刷机,我们本篇文章的手机参数:红米Note1,系统版本为4.4.4
4、编写模块
环境搭建好了,接下来开始编写模块去hook系统了。上面我们安装的工具其实是一个模块管理器,如果想要去hook某一些系统方法,做一些事情还得自己去编写模块,然后通过这个模块管理器把编写的模块安装到设备里面。接下来我们来举个例子,看如果完整的编写和运行一个模块。
4.1、新建模块项目
首先我们得下载XposedBridge.jar,我们要编写代码需要依赖Xposed的API,但是由于这些类已经被Xposed Installer注入到系统里面,所以我们的模块不能将XposedBridge.jar里面的class包含到模块apk里面。我们只需要通过 gradle的provide(compileOnly)方式依赖这个XposedBridge.jar就ok.
XposedBridge.jar下载地址:链接
由于国内网络无法访问这个下载地址,我这里附上里面的jar包的本站附件地址:链接
gradle配置如下:
4.2、编写模块代码
新建一个java类,并实现IXposedHookLoadPackage接口。该接口我们需要实现这个方法:
1 2 3 |
@Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { } |
在该方法里面我们可以进行拦截操作。这个时候我们可以使用XposedBridge.jar里面的两个方法来hook我们想要针对的目标方法:
方法一:
1 |
XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback); |
这个方法我们需要传入完整的类名、方法名、方法的参数类型。
方法二:
1 |
XposedBridge.hookMethod(method, xmh); |
这个方法里面关键的是反射拿到的Method对象
以下是我针对不同的使用场景总结的三种hook方法的实现:
1、当你明确的知道你要hook的类名、方法名、方法参数类型列表
1 2 3 4 5 6 7 |
private void hookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) { try { XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback); } catch (Exception e) { XposedBridge.log(e); } } |
2、当你不确定参数类型列表时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private void hookMethods(String className, String methodName, XC_MethodHook xmh) { try { Class<?> clazz = Class.forName(className); Method[] methods = clazz.getDeclaredMethods(); for(Method method : methods) { if(method.getName().equals(methodName) && !Modifier.isAbstract(method.getModifiers()) && Modifier.isPublic(method.getModifiers())) { XposedBridge.hookMethod(method, xmh); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } |
3、当你需要hook 内部类的方法时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
private void hookInnerClassMethod(String className, String innerClass, String methodName, XC_MethodHook xmh) { try { Class[] innerClassArr = Class.forName(className).getClasses(); int len = innerClassArr != null ? innerClassArr.length : 0; for(int i=0; i<len; i++) { Class innerClazz = innerClassArr[i]; String innerClassName = innerClazz.getName(); String targetInnerClassName = className + "$" + innerClass; Log.d("conio", "hook method name: " + targetInnerClassName+", innerClassName:"+innerClassName); if(targetInnerClassName.equals(innerClassName)) { Method[] methods = innerClazz.getDeclaredMethods(); Log.d("conio", "method size: " + (methods != null ? methods.length : 0)); for(Method method : methods) { Log.d("conio", "method: " + method.getName()); if(method.getName().equals(methodName) && !Modifier.isAbstract(method.getModifiers()) && Modifier.isPublic(method.getModifiers())) { Log.d("conio", "start hook method: " + method.getName()); XposedBridge.hookMethod(method, xmh); } } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } |
接下来举一个例子:
拦截所有调用系统SharedPreferences的内部类Editor的putString方法操作
由于要hook 内部类,所以我们参考上面的第三个hook方法。然后我们查看源码知道SharedPreferences和Editor实际上都是接口,我们需要找到它们的具体实现类,即:android.app.SharedPreferencesImpl和EditorImpl。代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Main implements IXposedHookLoadPackage { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { Log.d("conio", "handleLoadPackage:"+loadPackageParam.packageName); hookInnerClassMethod("android.app.SharedPreferencesImpl", "EditorImpl", "putString", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Log.d("conio", "afterHookedMethod:"); Object[] args = param.args; if(args != null && args.length == 2) { String key = String.valueOf(args[0]); String value = String.valueOf(args[1]); Log.d("conio", "sp put string, key:" + key + ", value:" + value); if("JsonDeviceID".equals(key)) { Log.d("conio", "trace begin >>>>"); Log.d("conio", Log.getStackTraceString(new Throwable())); } } } }); } } |
此时我们可以hook所有的使用SharedPreferences并调用putString的操作,然后打印出对应的key和value,同时也可以针对特定的key打印调用putString方法时的调用栈。
4.3、添加模块入口
完成上面的两部你已经完成了一大半了,别着急,我们还需要把刚编写好的模块通过模块管理器(Xposed Intaller)注入到系统里面,并激活。那么模块管理器怎么能找到您模块里面刚刚写的那个类呢。那就是我们需要添加模块的入口。
这一步非常重要,也是最容易忘记的,我们需要在模块工程的assets 目录添加一个名为xposed_init的文件,里面的内容就是模块入口类的全称名称即可,如下图所示:
4.4、添加模块的额外信息
最后一步需要在模块的AndroidManifest.xml文件添加额外信息,具体包括模块的描述信息、版本号等,如下所示:
其中:
- xposedmodule: 是Android程序作为Xposed中的一个模块,所以值为true
- xposeddescription: 是对本模块的功能描述,可以自己简单描述一下就可以了
- xposedminversion: 是对本模块开发时用到的Xposed的jar包的最低版本号,这里是54
5、运行模块
下面就来运行一下模块程序,安装到设备后,Xposed会提示模块为激活,我们需要勾选激活,如下图所示:
这个Xposed Installer程序工具应该是通过接收安装广播,然后得到这个应用信息分析它是否包含了Xposed 模块的特殊属性来判断的。激活成功后,会提示再次重启设备才能生效。每当有新的模块或者模块代码有更新时都需要重启设备模块才生效。
我们看一下hook SharedPreferences 的putString方法的效果:
我们打开QQ:
6、本章小结
本章主要介绍了Xposed的基本使用规则和方法,XposedInstaller.apk 其实是一个模块载体和管理器,如果想实现具体的Hook操作,就必须自己编写模块程序,然后激活加载方可生效。在实际过程中,这个框架是非常有用的,可以通过修改系统的一些信息来帮助测试模拟复杂的测试环境,但是这个框架现在用得最广泛的当属破解操作,比如用这个框架可以进行应用的脱壳,编写游戏的外挂等。