1. 程式人生 > >Xposed免重啟調試工具類

Xposed免重啟調試工具類

允許 擔心 發生 但是 auth dal rri rac true

直接放代碼

package com.xirtam.hello;

import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import
java.util.List; import dalvik.system.PathClassLoader; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; /** * @author DX * 這種方案建議只在開發調試的時候使用,因為這將損耗一些性能(需要額外加載apk文件),調試沒問題後,直接修改xposed_init文件為正確的類即可 * 可以實現免重啟,由於存在緩存,需要殺死宿主程序以後才能生效 * 這種免重啟的方式針對某些特殊情況的hook無效 * 例如我們需要implements IXposedHookZygoteInit,並將自己的一個服務註冊為系統服務,這種就必須重啟了 * Created by DX on 2017/10/4.
*/ public class HookLoader implements IXposedHookLoadPackage { //按照實際使用情況修改下面幾項的值 /** * 當前Xposed模塊的包名,方便尋找apk文件 TODO 需要配置 */ private final String modulePackage = "com.xirtam.hello"; /** * 宿主程序的包名(允許多個),過濾無意義的包名,防止無意義的apk文件加載 */ private static List<String> hostAppPackages = new
ArrayList<>(); static { // TODO: Add the package name of application your want to hook! hostAppPackages.add("com.xirtam.hello"); } /** * 實際hook邏輯處理類 TODO 需要配置 */ private final String handleHookClass = TestHook2.class.getName(); /** * 實際hook邏輯處理類的入口方法 */ private final String handleHookMethod = "handleLoadPackage"; @Override public 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() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Context context = (Context) param.args[0]; loadPackageParam.classLoader = context.getClassLoader(); invokeHandleHookMethod(context, modulePackage, handleHookClass, handleHookMethod, loadPackageParam); } }); } } /** * 安裝app以後,系統會在/data/app/下備份了一份.apk文件,通過動態加載這個apk文件,調用相應的方法 * 這樣就可以實現,只需要第一次重啟,以後修改hook代碼就不用重啟了 * * @param context context參數 * @param modulePackageName 當前模塊的packageName * @param handleHookClass 指定由哪一個類處理相關的hook邏輯 * @param loadPackageParam 傳入XC_LoadPackage.LoadPackageParam參數 * @throws Throwable 拋出各種異常,包括具體hook邏輯的異常,尋找apk文件異常,反射加載Class異常等 */ private void invokeHandleHookMethod(Context context, String modulePackageName, String handleHookClass, String handleHookMethod, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { // File apkFile = findApkFileBySDK(modulePackageName);//會受其它Xposed模塊hook 當前宿主程序的SDK_INT的影響 // File apkFile = findApkFile(modulePackageName); //原來的兩種方式不是很好,改用這種新的方式 File apkFile = findApkFile(context, modulePackageName); if (apkFile == null) { throw new RuntimeException("尋找模塊apk失敗"); } //加載指定的hook邏輯處理類,並調用它的handleHook方法 PathClassLoader pathClassLoader = new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader()); Class<?> cls = Class.forName(handleHookClass, true, pathClassLoader); Object instance = cls.newInstance(); Method method = cls.getDeclaredMethod(handleHookMethod, XC_LoadPackage.LoadPackageParam.class); method.invoke(instance, loadPackageParam); } /** * 根據包名構建目標Context,並調用getPackageCodePath()來定位apk * * @param context context參數 * @param modulePackageName 當前模塊包名 * @return return apk file */ private File findApkFile(Context context, String modulePackageName) { if (context == null) { return null; } try { Context moudleContext = context.createPackageContext(modulePackageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); String apkPath = moudleContext.getPackageCodePath(); return new File(apkPath); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return null; } /** * 尋找這個Android設備上的當前apk文件,不受其它Xposed模塊hook SDK_INT的影響 * * @param modulePackageName 當前模塊包名 * @return File 返回apk文件 * @throws FileNotFoundException 在/data/app/下的未找到本模塊apk文件,請檢查本模塊包名配置是否正確. * 具體檢查build.gradle中的applicationId和AndroidManifest.xml中的package */ @Deprecated private File findApkFile(String modulePackageName) throws FileNotFoundException { File apkFile = null; try { apkFile = findApkFileAfterSDK21(modulePackageName); } catch (Exception e) { try { apkFile = findApkFileBeforeSDK21(modulePackageName); } catch (Exception e2) { //忽略這個異常 } } if (apkFile == null) { throw new FileNotFoundException("沒在/data/app/下找到文件對應的apk文件"); } return apkFile; } /** * 根據當前的SDK_INT尋找這個Android設備上的當前apk文件 * * @param modulePackageName 當前模塊包名 * @return File 返回apk文件 * @throws FileNotFoundException 在/data/app/下的未找到本模塊apk文件,請檢查本模塊包名配置是否正確. * 具體檢查build.gradle中的applicationId和AndroidManifest.xml中的package */ @Deprecated private File findApkFileBySDK(String modulePackageName) throws FileNotFoundException { File apkFile; //當前Xposed模塊hook了Build.VERSION.SDK_INT不用擔心,因為這是發生在hook之前,不會有影響 //但是其它的Xposed模塊hook了當前宿主的這個值以後,就會有影響了,所以這裏沒有使用這個方法 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { apkFile = findApkFileAfterSDK21(modulePackageName); } else { apkFile = findApkFileBeforeSDK21(modulePackageName); } return apkFile; } /** * 尋找apk文件(api_21之後) * 在Android sdk21以及之後,apk文件的路徑發生了變化 * * @param packageName 當前模塊包名 * @return File 返回apk文件 * @throws FileNotFoundException apk文件未找到 */ @Deprecated private File findApkFileAfterSDK21(String packageName) throws FileNotFoundException { File apkFile; File path = new File(String.format("/data/app/%s-%s", packageName, "1")); if (!path.exists()) { path = new File(String.format("/data/app/%s-%s", packageName, "2")); } if (!path.exists() || !path.isDirectory()) { throw new FileNotFoundException(String.format("沒找到目錄/data/app/%s-%s", packageName, "1/2")); } apkFile = new File(path, "base.apk"); if (!apkFile.exists() || apkFile.isDirectory()) { throw new FileNotFoundException(String.format("沒找到文件/data/app/%s-%s/base.apk", packageName, "1/2")); } return apkFile; } /** * 尋找apk文件(api_21之前) * * @param packageName 當前模塊包名 * @return File 返回apk文件 * @throws FileNotFoundException apk文件未找到 */ @Deprecated private File findApkFileBeforeSDK21(String packageName) throws FileNotFoundException { File apkFile = new File(String.format("/data/app/%s-%s.apk", packageName, "1")); if (!apkFile.exists()) { apkFile = new File(String.format("/data/app/%s-%s.apk", packageName, "2")); } if (!apkFile.exists() || apkFile.isDirectory()) { throw new FileNotFoundException(String.format("沒找到文件/data/app/%s-%s.apk", packageName, "1/2")); } return apkFile; } }

配置一下3處TODO標簽的位置,然後把xposed_init文件中的配置改成這個類即可,並不適用於所有情況的免重啟,註釋寫的很明確了,感謝原作者,本文代碼有少量修改。

Xposed免重啟調試工具類