一、 Api说明
IXposedHookLoadPackage
加载回调接口,在xposed入口类继承,实现handleLoadPackage(XC_LoadPackage.LoadPackageParam)
,这个方法用于在打开app的时候回调,参数LoadPackageParam
包含了加载的应用程序的一些基本信息。
IXposedHookInitPackageResources
加载回调接口,用于修改app的资源文件,在xposed入口类继承,实现handleInitPackageResources(InitPackageResourcesParam)
方法,用于在加载应用程序的包的时候执行用户的操作 ,参数InitPackageResourcesParam
包含了加载的应用程序的一些资源基本信息。
IXposedHookZygoteInit
加载回调接口,在xposed入口类继承,实现initZygote(IXposedHookZygoteInit.StartupParam)
,这个方法用于在开始hookApp的时候回调,参数StartupParam
包含了模块app的路径。
XposedHelpers.java
Xposed功能辅助类,里面提供了一些辅助方法,简化连接和调用方法/构造函数,获取和设置字段,这里直说重要的方法
-
findAndHookMethod: hook类中的某个方法
参数:
className: 要hook的方法的所在类
classloader: 要hook的包的classLoader,一般都写loadPackageParam.classLoader
methodName: 要hook的方法
parameterTypesAndCallback: 方法的参数和监听器。 -
callMethod: 在目标app中调用方法
参数:
Object: 要调用方法的所在类
methodName: 要调用的方法名称
args: 方法的参数 -
findClass: 获取class类实例
参数:
className: 类名
classLoader: 类加载器
XposedBridge.java
- log:在Xposed的app的日志功能里输出日志和/data/xposed/debug.log 这个文件中
参数
text: 要输出的内容
二、 Hook过程
Xposed 框架中真正起作用的是对方法的hook,hook到之后,在方法之前之后进行修改添加或者替换,那么寻找正确的目标方法名和所在的类名就很关键了。一般寻找目标方法有两种方法:
- 开源的直接查看源码,主要是系统应用
- 反编译查看源码,常见的有apkTool,jadx,jeb
1. Find
通过特定的类加载器加载要hook的类,通过反射找到被hook的成员。工具类XposedHelpers提供了一些工具方法来简化find过程;XposedBridge的hook*方法用于处理hook并执行回调。
XposedHelpers静态方法 | 描述 |
---|---|
findClass | 使用classLoader加载class |
findField | 通过反射查找类的数据成员并设置可访问性(setAccessible(true)) |
findMethod | 通过反射查找类的成员函数并设置可访问性 |
findConstructor | 通过反射查找类的构造函数并设置可访问性 |
setStatic | 通过反射设置类静态变量的值 |
set | 通过反射设置对象数据成员的值 |
findAndHook | 查找并hook |
2. Hook
XC_MethodHook中定义了回调方法:
- beforeHookedMethod(MethodHookParam param):被hook方法调用前执行,调用param.setResult可以跳过被hook的方法。
- afterHookedMethod(MethodHookParam param): 被hook方法调用后执行,调用param.setResult更改被hook方法的执行结果。
- replaceHookedMethod: 继承自XC_MethodReplacement,能替换Hook的方法
- XC_MethodReplacement: 继承自XC_MethodHook,通过在beforeHookedMethod中调用param.setResult实现了方法的替换。
@Override
protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {
try {
Object result = replaceHookedMethod(param);
param.setResult(result);
} catch (Throwable t) {
param.setThrowable(t);
}
}
三、部分注意点和技巧
1. Hook内部类
通过美元符号 $ 连接内部类, 例如:
//获取ItemDecoration的类路径
String clsName = "androidx.recyclerview.widget.RecyclerView$ItemDecoration"
2. hook的方法
只能hook正常的方法,或者实现的方法或者构造方法,不能hook接口和抽象方法
3. context上下文获取
拿到之后可以用静态变量存放起来
try {
Class<?> ContextClass = findClass("android.content.ContextWrapper", loadPackageParam.classLoader);
findAndHookMethod(ContextClass, "getApplicationContext", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Context applicationContext = (Context) param.getResult();
XposedBridge.log("得到上下文");
}
});
} catch (Throwable t) {
XposedBridge.log("获取上下文出错"+t);
}
4. 获取控件
例如获取一个EditText,上一篇文章的例子,获取TextView,修改字符。。打开被hook的app就会发现文本变成了"我是被Xposed修改的啦"
public class Main implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
findAndHookMethod("com.tpnet.companycalc.MainActivity",
loadPackageParam.classLoader,
"onCreate",
Bundle.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//获取到当前hook的类,这里是MainActivity
Class clazz = param.thisObject.getClass();
XposedBridge.log("class name:" + clazz.getName());
// 输入框不为私有private 可通过以下方式获取
//Field field = clazz.getField("tvText");// 密码输入框对象变量名称
// 通过反射获取控件,无论parivate或者public
Field field = clazz.getDeclaredField("tvText");
// 设置访问权限
field.setAccessible(true);
TextView textView = (TextView) field.get(param.thisObject);
String string = textView.getText().toString();
XposedBridge.log("原来的字符 : " + string);
// 设置属性
textView.setText("我是被Xposed修改的啦");
}
}
);
}
}
5. 参数中有自定义类
通过反射获得得到自定义类
Class<?> hookMessageListenerClass = null;
hookMessageListenerClass = lpparam.classLoader.loadClass("org.jivesoftware.smack.MessageListener");
findAndHookMethod("org.jivesoftware.smack.ChatManager", lpparam.classLoader, "createChat", String.class , hookMessageListenerClass ,new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String sendTo = (String) param.args[0];
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
6. hook 构造方法
XposedBridge类里面的findAndHookConstructor方法
7.打开Activity
Activity activity = (Activity) param.thisObject;
Intent intent = new Intent();
intent.setClassName(activity,"com.ushaqi.zhuishushenqi.ui.home.HomeActivity");
activity.startActivity(intent);
8. 自定义接口的处理
当你想调用宿主app里面的一个方法,方法的参数是一个接口,接口不能new的,这时候应该怎么办? 使用Proxy.newProxyInstance
/**
* 生成接口实例
* @param loader 类加载器
* @param interface 接口的Class实例
* @param h 接口回调
* /
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)