如何配置 Android Studio 项目为 Xposed 插件?
1、配置项目 Gradle 的依赖
compileOnly 'de.robv.android.xposed:api:82'compileOnly 'de.robv.android.xposed:api:82:sources'
注:需要 compileOnly 来依赖,如果不想通过 Gradle 配置,也可以下载 XposedBridgeApi.jar ,放到项目 libs 目录。
2、配置 AndroidManifest.xml
- xposedmodule:是否配置为 Xposed 插件,设置为 true
- xposeddescription:模块名称
- xposedminversion:最低版本号
3、新建 Hook 入口类
该类需要实现接口 IXposedHookLoadPackage
,并实现里面关键方法handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam)
,该方法会在每个软件被启动的时候回调,所以一般需要通过目标包名过滤。
/*** @author zhicheng.chen*/
public class TargetHook implements IXposedHookLoadPackage {@Overridepublic void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {//通过目标包名过滤if (lpparam.packageName.equals("com.xxx.xxx")) {XposedBridge.log("启动了xxx软件");}}
}
4、Xposed 免重启调试
Xposed插件每次代码改动,都需要重启手机才能生效,有时候重启一次还不生效(我的手机有一次重启 3 次,才看到生效,还好是公司测试机,不心疼),所以代码最好写上相关 Log 信息,来看代码生效没。
XposedBridge.log("启动了xxx软件");
不过这里分享一个免重启调试的方法,方法来自网上,感谢 DX :
/*** &#64;author DX* 这种方案建议只在开发调试的时候使用&#xff0c;因为这将损耗一些性能(需要额外加载apk文件)&#xff0c;调试没问题后&#xff0c;直接修改xposed_init文件为正确的类即可* 可以实现免重启&#xff0c;由于存在缓存&#xff0c;需要杀死宿主程序以后才能生效* 这种免重启的方式针对某些特殊情况的hook无效* 例如我们需要implements IXposedHookZygoteInit,并将自己的一个服务注册为系统服务&#xff0c;这种就必须重启了* Created by DX on 2017/10/4.*/public class HookLoader2 implements IXposedHookLoadPackage {//按照实际使用情况修改下面几项的值/*** 当前Xposed模块的包名,方便寻找apk文件*/private final String modulePackage &#61; "com.xxx.plugin";/*** 宿主程序的包名(允许多个),过滤无意义的包名,防止无意义的apk文件加载*/private static List hostAppPackages &#61; new ArrayList<>();static {// TODO: Add the package name of application your want to hook!hostAppPackages.add("com.eg.android.AlipayGphone");hostAppPackages.add("com.xxx.plugin");}/*** 实际hook逻辑处理类*/private final String handleHookClass &#61; TargetHook.class.getName();/*** 实际hook逻辑处理类的入口方法*/private final String handleHookMethod &#61; "handleLoadPackage";&#64;Overridepublic void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {if (hostAppPackages.contains(loadPackageParam.packageName)) {//将loadPackageParam的classloader替换为宿主程序Application的classloader,解决宿主程序存在多个.dex文件时,有时候ClassNotFound的问题XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {&#64;Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {Context context&#61;(Context) param.args[0];loadPackageParam.classLoader &#61; context.getClassLoader();invokeHandleHookMethod(context,modulePackage, handleHookClass, handleHookMethod, loadPackageParam);}});}}/*** 安装app以后&#xff0c;系统会在/data/app/下备份了一份.apk文件&#xff0c;通过动态加载这个apk文件&#xff0c;调用相应的方法* 这样就可以实现&#xff0c;只需要第一次重启&#xff0c;以后修改hook代码就不用重启了* &#64;param context context参数* &#64;param modulePackageName 当前模块的packageName* &#64;param handleHookClass 指定由哪一个类处理相关的hook逻辑* &#64;param loadPackageParam 传入XC_LoadPackage.LoadPackageParam参数* &#64;throws Throwable 抛出各种异常,包括具体hook逻辑的异常,寻找apk文件异常,反射加载Class异常等*/private void invokeHandleHookMethod(Context context, String modulePackageName, String handleHookClass, String handleHookMethod, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {File apkFile&#61;findApkFile(context,modulePackageName);if (apkFile&#61;&#61;null){throw new RuntimeException("寻找模块apk失败");}//加载指定的hook逻辑处理类&#xff0c;并调用它的handleHook方法PathClassLoader pathClassLoader &#61; new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader());Class> cls &#61; Class.forName(handleHookClass, true, pathClassLoader);Object instance &#61; cls.newInstance();Method method &#61; cls.getDeclaredMethod(handleHookMethod, XC_LoadPackage.LoadPackageParam.class);method.invoke(instance, loadPackageParam);}/*** 根据包名构建目标Context,并调用getPackageCodePath()来定位apk* &#64;param context context参数* &#64;param modulePackageName 当前模块包名* &#64;return return apk file*/private File findApkFile(Context context, String modulePackageName){if (context&#61;&#61;null){return null;}try {Context moudleContext &#61; context.createPackageContext(modulePackageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);String apkPath&#61;moudleContext.getPackageCodePath();return new File(apkPath);} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}return null;}
}