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