个人开发的 Xposed 模块中,确实有检查自己模块是否已启用的需求,那么该如何实现呢?
思路分析
最直接思路就是直接去 Xposed Installer
的模块列表里面,自己的模块是否已被勾选。那么, Xposed Installer
有提供相关 api 调用来返回某模块是否被勾选吗?答案是,没有。
那么,能不能 Hook Xposed Installer
的模块列表界面,从而获取是否被勾选呢? 想到这里的时候其实是把此问题复杂化了:只要能 Hook 成功了,就说明自己模块已经启用了,那么何不 Hook 自己的模块应用本身呢?
这样也避免了还需要去逆向 Xposed Installer
的麻烦。
想到这里,其实整个问题都已经迎刃而解了。
编程实现
当前模块相关的工具类 ModuleUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class ModuleUtils {
private ModuleUtils() {}
private static int getModuleVersion() { return -1; }
public static boolean isModuleEnabled() { return getModuleVersion() > 0; } }
|
ModuleUtils.isModuleEnabled()
正常条件下一定是返回 false
的,而我们接下来要做的就是去 Hook getModuleVersion()
方法,让其在已启用的情况下返回正值,这样就能通过 isModuleEnabled()
判断模块是否已经启用了。
ModuleUtilsHook
用来 Hook ModuleUtils
:
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
| public class ModuleUtilsHook implements IXposedHookLoadPackage {
private static final String SMSCODE_PACKAGE = BuildConfig.APPLICATION_ID; private static final int MODULE_VERSION = BuildConfig.MODULE_VERSION;
@Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (SMSCODE_PACKAGE.equals(lpparam.packageName)) { try { XLog.i("Hooking current Xposed module status..."); hookModuleUtils(lpparam); } catch (Throwable e) { XLog.e("Failed to hook current Xposed module status."); } }
}
private void hookModuleUtils(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { String className = ModuleUtils.class.getName();
XposedHelpers.findAndHookMethod(className, lpparam.classLoader, "getModuleVersion", XC_MethodReplacement.returnConstant(MODULE_VERSION)); } }
|
其中上面的 MODULE_VERSION
是在项目 module
的 build.gradle
自定义的:
1 2 3 4 5 6 7
| android { defaultConfig { buildConfigField("int", "MODULE_VERSION", "18") } }
|
最后,因为 ModuleUtilsHook
在 Hook 过程中用到了反射,所以不应该混淆 ModuleUtils
的 getModuleVersion
方法:
1 2 3
| -keep class com.github.tianma8023.xposed.smscode.utils.ModuleUtils { int getModuleVersion(); }
|
源码请戳 ModuleUtilsHook