上一篇尝试安装了Xposed框架并成功运行,该篇主要学习使用xposed框架
利用xposed框架编写插件主要有四步
AndroidManifest.xml中添加xposed配置xposedmodulexposeddescriptionxposedminversion- 拷贝
XposedBridgeApi-xx.jar到对应的libs库,或者通过jcenter gradle的方式导入XposedBridgeApi(github.com/rovo89/Xpos… ),并配置compileOnly到依赖项中 - 编写
xxxModule并继承IXposedHookLoadPackage,hook目标应用的类 - 声明
xxxModule到assets/xposed_init文件中
一个简单的demo
目标:创建目标app,包名com.learn.demo1,包括MainActivity,通过hook手段,在启动MainActivity.onCreate的时候打印call com.learn.demo1.MainActivity.onCreate()
通过android studio创建一个上述目标demo1,运行到手机上,此时成功安装,并显示默认界面
创建hook module(对demo1进行hook),在AndroidManifest.xml的Application标签中配置如下
<!--是否是xposed模块-->
<meta-data android:name="xposedmodule" android:value="true"/>
<!--描述,会显示在对应xposed installer中-->
<meta-data android:name="xposeddescription" android:value="learn hook"/>
<!--xposed bridge的最小版本-->
<meta-data android:name="xposedminversion" android:value="82" />
导入XposedBridgeApi库,此处我们使用gradle方式,注意由于jcenter已经停更,需要使用其他gradle源,这里使用了https://api.xposed.info/源
// 配置repositories,在project根目录下的的gradle文件中,找到repositories并加入以下源
repositories {
maven {
setUrl("https://api.xposed.info/")
}
...
}
// 配置依赖,具体项目中的gradle
dependencies {
compileOnly("de.robv.android.xposed:api:82")
}
创建LearnModule类,代码如下
package com.learn.xposed;
import android.os.Bundle;
import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class LearnModule implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
// 过滤非目标包
if (!lpparam.packageName.equals("com.learn.demo1")) {
return;
}
ClassLoader classLoader = lpparam.classLoader;
// hook MainActivity.onCreate函数并在onCreate调用结束后,执行打印逻辑
XposedHelpers.findAndHookMethod("com.learn.demo1.MainActivity", classLoader, "onCreate", Bundle.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i("testzy", "call com.learn.demo1.MainActivity.onCreate()");
}
});
}
}
创建assets资产目录,创建文件xposed_init并配置Module模块
com.learn.xposed.LearnModule
此时就完成了一个简单的插件编写,运行到手机中,打开xposed installer,选择module并勾选启用当前插件,重启后即可看到在logcat中成功打印了call com.learn.demo1.MainActivity.onCreate()
Xposed API使用
目标: 创建Dog类
- hook其中的构造函数/静态函数/非静态函数并打印日志
- 修改静态or非静态field,并主动调用Dog类中的函数打印其内容
对应目标app修改如下修改目标app的onCreate()
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 添加如下两行
Dog dog = new Dog();
Dog dogWithName = new Dog("wangwang", 1);
}
// 添加类
public class Dog {
private static final String TAG = "testzy";
private static String KIND = "Dog";
String name;
int age;
public Dog() {
Log.i(TAG, "call Dog()");
}
public Dog(String name, int age) {
Log.i(TAG, "call Dog(String, int)");
this.name = name;
this.age = age;
}
@Override
public String toString() {
return KIND + "{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
所有的xxxModule都派生自de.robv.android.xposed.IXposedHookLoadPackage,该接口当一个apk启动加载时会被调用,并通过回调函数void handleLoadPackage(LoadPackageParam lpparam)得到启动apk中的信息,其中LoadPackageParam中的信息如下
// 包名
public String packageName;
// 进程名
public String processName;
// 类加载器
public ClassLoader classLoader;
// 暂不关注
public ApplicationInfo appInfo;
// 暂不关注
public boolean isFirstApplication;
构造函数的Hook api如下,几乎所有的api都会有如下两种类似的调用方式,要么通过传入className字符串和classLoader,要么直接构造Class并使用
public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback)
public static XC_MethodHook.Unhook findAndHookConstructor(Class<?> clazz, Object... parameterTypesAndCallback)
我们通过hook Dog类中的构造函数可以成功打印了附加的信息
package com.learn.xposed;
import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class LearnModule implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.learn.demo1")) {
return;
}
ClassLoader classLoader = lpparam.classLoader;
XposedHelpers.findAndHookConstructor("com.learn.demo1.Dog", classLoader, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i("testzy", "before call com.learn.demo1.Dog()");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i("testzy", "after call com.learn.demo1.Dog()");
}
});
Class<?> dogClass = XposedHelpers.findClass("com.learn.demo1.Dog", classLoader);
XposedHelpers.findAndHookConstructor(dogClass, String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i("testzy", "before call com.learn.demo1.Dog(String, int)");
for (Object p: param.args) {
Log.i("testzy", "param = " + p);
}
Log.i("testzy", "result = " + param.getResult());
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i("testzy", "after call com.learn.demo1.Dog(String, int)");
for (Object p: param.args) {
Log.i("testzy", "param = " + p);
}
Log.i("testzy", "result = " + param.getResult());
Log.i("testzy", "this = " + param.thisObject);
}
});
}
}
注意:
- 不要在构造函数的
beforeHookedMethod()中使用param.thisObject - 获取class时采用
XposedHelpers.findClass()的方式,而不是classLoad.loadClass(),findClass最终调用的是Class.forName()与classLoader.loadClass()实现上有差异(当前xiaomi2s中采用loadClass方式插件不会生效,了解到其他手机可以使用该方法,猜测存在兼容问题)
打印结果如下
testzy com.learn.demo1 I before call com.learn.demo1.Dog()
testzy com.learn.demo1 I call Dog()
testzy com.learn.demo1 I after call com.learn.demo1.Dog()
testzy com.learn.demo1 I before call com.learn.demo1.Dog(String, int)
testzy com.learn.demo1 I param = wangwang
testzy com.learn.demo1 I param = 1
testzy com.learn.demo1 I result = null
testzy com.learn.demo1 I call Dog(String, int)
testzy com.learn.demo1 I after call com.learn.demo1.Dog(String, int)
testzy com.learn.demo1 I param = wangwang
testzy com.learn.demo1 I param = 1
testzy com.learn.demo1 I result = null
testzy com.learn.demo1 I this = com.learn.demo1.Dog@2333a300
接着尝试对Dog中的内容做修改,包括更改年龄,KIND&flag字段改成大写,并调用toString()方法
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i("testzy", "after call com.learn.demo1.Dog(String, int)");
for (Object p: param.args) {
Log.i("testzy", "param = " + p);
}
Log.i("testzy", "result = " + param.getResult());
Log.i("testzy", "this = " + param.thisObject);
// 添加如下代码
Class<?> aClass = XposedHelpers.findClass("com.learn.demo1.Dog", classLoader);
// 设置kind & flag静态变量
// kind
// Field kind = aClass.getDeclaredField("kind");
// kind.setAccessible(true);
// kind.set(null, "DOG");
XposedHelpers.setStaticObjectField(aClass, "kind", "DOG");
// flag
// 方案1
// Field flag = aClass.getDeclaredField("flag");
// flag.setAccessible(true);
// flag.set(null, 666); // 成功修改
// 方案2
// Class<?> integerClass = XposedHelpers.findClass("java.lang.Integer", classLoader);
// Object number = XposedHelpers.newInstance(integerClass, 123);
// XposedHelpers.setStaticObjectField(aClass, "flag", number);
// 方案3
// 注意这里是ObjectField
XposedHelpers.setStaticObjectField(aClass, "flag", 777);
// 调用非静态方法打印结果
// 方案1
// Method print = aClass.getMethod("print");
// print.invoke(param.thisObject); // 成功调用
// 方案2
XposedHelpers.callMethod(param.thisObject, "print");
}
此时可打印出
testzy com.learn.demo1 I DOG{name='wangwang', age=1}, 666
参考文章