Xposed免重启更新模块

    Xposed模块在每次更新后都需要重启才能生效,公司给我的测试机是小米4,非常古董,每次重启都需要花费大量时间.而且手机系统是我刷的一个原生6.0的系统,总有些小bug,有时候需要重启很多次才行,等的我黄花菜都凉了.所以有必要把这个搞一搞了.

   简单总结一下原理:安装模块时,Android系统会在data/app/对应包名的目录下保存原始apk,通过读取这个原始的apk,然后new一个PathClassLoader,该PathClassLoader用于加载写有hook逻辑的类,最后通过反射的方式完成hook的具体逻辑.

          工具: XposedBridgeApi-54.jar

        我使用的是一个已root的原生6.0系统的小米4

     方法:1.新建一个HookLoader类,具体代码如下:

  1 package com.example.xposedhook;
  2 
  3 import android.app.Application;
  4 import android.content.Context;
  5 import android.content.pm.ApplicationInfo;
  6 import android.content.pm.PackageManager;
  7 
  8 import java.io.File;
  9 
 10 import dalvik.system.PathClassLoader;
 11 import de.robv.android.xposed.IXposedHookLoadPackage;
 12 import de.robv.android.xposed.IXposedHookZygoteInit;
 13 import de.robv.android.xposed.XC_MethodHook;
 14 import de.robv.android.xposed.XposedBridge;
 15 import de.robv.android.xposed.XposedHelpers;
 16 import de.robv.android.xposed.callbacks.XC_LoadPackage;
 17 
 18 /**
 19  * @author DX
 20  * 这种方案建议只在开发调试的时候使用,因为这将损耗一些性能(需要额外加载apk文件),调试没问题后,直接修改xposed_init文件为正确的类即可
 21  * 可以实现免重启,由于存在缓存,需要杀死宿主程序以后才能生效
 22  * Created by DX on 2017/10/4.
 23  * Modified by chengxuncc on 2019/4/16.
 24  */
 25 
 26 public class HookLoader implements IXposedHookLoadPackage, IXposedHookZygoteInit {
 27     //按照实际使用情况修改下面几项的值
 28     /**
 29      * 当前Xposed模块的包名,方便寻找apk文件
 30      */
 31     private final static String modulePackageName = HookLoader.class.getPackage().getName();
 32 
 33     /**
 34      * 实际hook逻辑处理类
 35      */
 36     private final String handleHookClass = HookLogic.class.getName();
 37     /**
 38      * 实际hook逻辑处理类的入口方法
 39      */
 40     private final String handleHookMethod = "handleLoadPackage";
 41 
 42     private final String initMethod = "initZygote";
 43 
 44     private IXposedHookZygoteInit.StartupParam startupparam;
 45 
 46     /**
 47      * 重定向handleLoadPackage函数前会执行initZygote
 48      *
 49      * @param loadPackageParam
 50      * @throws Throwable
 51      */
 52     @Override
 53     public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
 54         // 排除系统应用
 55         if (loadPackageParam.appInfo == null ||
 56                 (loadPackageParam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) == 1) {
 57             return;
 58         }
 59         //将loadPackageParam的classloader替换为宿主程序Application的classloader,解决宿主程序存在多个.dex文件时,有时候ClassNotFound的问题
 60         XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
 61             @Override
 62             protected void afterHookedMethod(MethodHookParam param) throws Throwable {
 63                 Context context = (Context) param.args[0];
 64                 loadPackageParam.classLoader = context.getClassLoader();
 65                 Class<?> cls = getApkClass(context, modulePackageName, handleHookClass);
 66                 Object instance = cls.newInstance();
 67                 try {
 68                     cls.getDeclaredMethod(initMethod, startupparam.getClass()).invoke(instance, startupparam);
 69                 }catch (NoSuchMethodException e){
 70                     // 找不到initZygote方法
 71                 }
 72                 cls.getDeclaredMethod(handleHookMethod, loadPackageParam.getClass()).invoke(instance, loadPackageParam);
 73             }
 74         });
 75     }
 76 
 77     /**
 78      * 实现initZygote,保存启动参数。
 79      *
 80      * @param startupParam
 81      */
 82     @Override
 83     public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) {
 84         this.startupparam = startupParam;
 85     }
 86 
 87     private Class<?> getApkClass(Context context, String modulePackageName, String handleHookClass) throws Throwable {
 88         File apkFile = findApkFile(context, modulePackageName);
 89         if (apkFile == null) {
 90             throw new RuntimeException("寻找模块apk失败");
 91         }
 92         //加载指定的hook逻辑处理类,并调用它的handleHook方法
 93         PathClassLoader pathClassLoader = new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader());
 94         Class<?> cls = Class.forName(handleHookClass, true, pathClassLoader);
 95         return cls;
 96     }
 97 
 98     /**
 99      * 根据包名构建目标Context,并调用getPackageCodePath()来定位apk
100      *
101      * @param context           context参数
102      * @param modulePackageName 当前模块包名
103      * @return apk file
104      */
105     private File findApkFile(Context context, String modulePackageName) {
106         if (context == null) {
107             return null;
108         }
109         try {
110             Context moudleContext = context.createPackageContext(modulePackageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
111             String apkPath = moudleContext.getPackageCodePath();
112             return new File(apkPath);
113         } catch (PackageManager.NameNotFoundException e) {
114             e.printStackTrace();
115         }
116         return null;
117     }
118 }

       2.将代码第36行改为你自己写的hook逻辑的类.class.getName()即可,其它地方基本不用动.比如我写的hook类名字为XposedUtils,里面是具体的hook逻辑,只需将第36行代码改成:

            

       3.将xposed_init文件程序入口处改为新建的HookLoader这个类.如:

 

 

    4.大功告成,开始为所欲为.

          注意:有的可能会抛出"寻找apk模块失败"的异常,这是因为在findApkFile方法中传入的modulePackageName可能不是当前模块的完整包名,可以手动改成app build.gradle中appcationId的值.如:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

     感谢大佬提供的方案.

   原文:https://blog.csdn.net/u011956004/article/details/78612502

   GitHub地址:https://github.com/shuihuadx/XposedHook

posted @ 2019-10-08 16:47  移动安全星球  阅读(3195)  评论(0编辑  收藏  举报