模块入口
IXposedMod接口定义了Xposed模块。但用户不能直接实现这个接口(具体原因参见的main方法),只能实现作者定义好的四个接口,每个接口用于特定的用途:
-
public interface IXposedHookInitPackageResources extends IXposedMod 用于修改app的资源文件
-
public interface IXposedHookLoadPackage extends IXposedMod 用于hook应用的代码
-
public interface IXposedHookZygoteInit extends IXposedMod 在Zygote初始化时执行
-
public interface IXposedHookCmdInit extends IXposedMod 用于hook基于java的命令行工具(例如pm)。它需要app_process 55版本,还需要在XposedInstaller的data目录下创建一个特殊文件(conf/enable_for_tools)
##类加载器
从上图可以发现Xposed Module的类加载器与要运行Apk的类加载器都继承自PathClassLoader,但它们之间相互独立,每个ClassLoader只用于加载某一特定的apk(dex),所以在Xposed module中如果要加载apk中的类需要用原应用的ApkClassLoader进行加载。
##Hook过程
- Find: 通过特定的类加载器加载要hook的类,通过反射找到被hook的成员。
- Hook: 被hook成员调用前后执行特定的回调方法。
###Find
工具类XposedHelpers提供了一些工具方法来简化find过程;XposedBridge的hook*方法用于处理hook并执行回调。
XposedHelpers静态方法 | _ |
---|---|
findClass | 使用classLoader加载class |
findField* | 通过反射查找类的数据成员并设置可访问性(setAccessible(true)) |
findMethod* | 通过反射查找类的成员函数并设置可访问性 |
findConstructor* | 通过反射查找类的构造函数并设置可访问性 |
setStatic* | 通过反射设置类静态变量的值 |
set* | 通过反射设置对象数据成员的值 |
findAndHook* | 查找并hook |
###Hook
XC_MethodHook中定义了回调方法: beforeHookedMethod(MethodHookParam param):被hook方法调用前执行,调用param.setResult可以跳过被hook的方法。 afterHookedMethod(MethodHookParam param) : 被hook方法调用后执行,调用param.setResult更改被hook方法的执行结果。
XC_MethodReplacement继承自XC_MethodHook,通过在beforeHookedMethod中调用param.setResult实现了方法的替换。
@Overrideprotected final void beforeHookedMethod(MethodHookParam param) throws Throwable { try { Object result = replaceHookedMethod(param); param.setResult(result); } catch (Throwable t) { param.setThrowable(t); }}
实例
目标
笔者在网上找到了一个有广告的小游戏。我们的目标是通过xposed将广告去掉,测试的应用包可以
step1
首先要找到目标方法,通过逆向找到主要的广告类com.umonistudio.utils.Ads.AdsAdmob
,它有两个方法来显示广告public void showBannerAd(final boolean paramBoolean)
和 public void showFullScreenAd(final Boolean paramBoolean)
,其主要代码如下: 因为方法完成了广告的请求与显示,所以只要hook这两个方法就可以了(其中传入PluginWrapper.runOnMainThread方法的Runnable对象就是用于加载并显示广告的线程类)。
step2
接下来就是hook了,代码挺简单,只要让原来的方法什么都不干就行。
public class XModule implements IXposedHookLoadPackage { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { if (lpparam.packageName.equals("com.umonistudio.tile")) { Class adClz = lpparam.classLoader.loadClass("com.umonistudio.utils.Ads.AdsAdmob");//需要使用ApkClassLoader加载apk内的类 XposedBridge.log("已加载广告类" + adClz.getSimpleName()); XposedHelpers.findAndHookMethod(adClz, "showBannerAd", boolean.class, new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("Banner广告已屏蔽"); return null; } }); XposedHelpers.findAndHookMethod(adClz, "showFullScreenAd", Boolean.class, new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { XposedBridge.log("全屏广告已屏蔽"); return null; } }); } }}
效果很不错,可以在xposedInstaller的日志中看到XposedBridge.log()输出的日志:
附录