diff --git a/core/src/main/java/de/robv/android/xposed/XposedInit.java b/core/src/main/java/de/robv/android/xposed/XposedInit.java index a1aa6ee2..71bc65af 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java @@ -221,7 +221,7 @@ public final class XposedInit { } public static void loadModules() { - var moduleList = serviceClient.getModulesList(); + var moduleList = serviceClient.getLegacyModulesList(); moduleList.forEach(module -> { var apk = module.apkPath; var name = module.packageName; @@ -286,7 +286,7 @@ public final class XposedInit { * in assets/xposed_init. */ private static boolean loadModule(String name, String apk, PreLoadedApk file) { - Log.i(TAG, "Loading module " + name + " from " + apk); + Log.i(TAG, "Loading legacy module " + name + " from " + apk); var sb = new StringBuilder(); var abis = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS; diff --git a/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java b/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java index 8afac0c8..616b8b69 100644 --- a/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java +++ b/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java @@ -66,6 +66,15 @@ public class ApplicationServiceClient implements ILSPApplicationService, IBinder return null; } + @Override + public List getLegacyModulesList() { + try { + return service.getLegacyModulesList(); + } catch (RemoteException | NullPointerException ignored) { + } + return Collections.emptyList(); + } + @Override public List getModulesList() { try { diff --git a/core/src/main/java/org/lsposed/lspd/core/Startup.java b/core/src/main/java/org/lsposed/lspd/core/Startup.java index 4ceb697e..21bea1f2 100644 --- a/core/src/main/java/org/lsposed/lspd/core/Startup.java +++ b/core/src/main/java/org/lsposed/lspd/core/Startup.java @@ -28,6 +28,7 @@ import android.content.res.CompatibilityInfo; import com.android.internal.os.ZygoteInit; import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter; +import org.lsposed.lspd.hooker.AttachHooker; import org.lsposed.lspd.hooker.CrashDumpHooker; import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker; import org.lsposed.lspd.hooker.LoadedApkCtorHooker; @@ -59,6 +60,8 @@ public class Startup { ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class, ClassLoader.class, boolean.class, boolean.class, boolean.class, new LoadedApkCtorHooker()); + XposedHelpers.findAndHookMethod(ActivityThread.class, "attach", boolean.class, + long.class, new AttachHooker()); } public static void bootstrapXposed() { diff --git a/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java new file mode 100644 index 00000000..0a3fb3d4 --- /dev/null +++ b/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java @@ -0,0 +1,72 @@ +package org.lsposed.lspd.hooker; + +import static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient; + +import android.app.ActivityThread; +import android.app.LoadedApk; +import android.content.Context; +import android.content.ContextParams; +import android.os.Build; +import android.os.Process; + +import org.lsposed.lspd.util.Hookers; +import org.lsposed.lspd.util.LspModuleClassLoader; + +import java.io.File; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedHelpers; +import de.robv.android.xposed.XposedInit; + +public class AttachHooker extends XC_MethodHook { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + var at = (ActivityThread) param.thisObject; + var moduleList = serviceClient.getModulesList(); + moduleList.forEach(module -> { + try { + XposedInit.getLoadedModules().add(module.packageName); + var loadedApk = at.getPackageInfoNoCheck(module.applicationInfo, null); + var sb = new StringBuilder(); + var abis = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS; + for (String abi : abis) { + sb.append(module.apkPath).append("!/lib/").append(abi).append(File.pathSeparator); + } + var librarySearchPath = sb.toString(); + + var initLoader = XposedInit.class.getClassLoader(); + var mcl = LspModuleClassLoader.loadApk(module.apkPath, module.file.preLoadedDexes, librarySearchPath, initLoader); + XposedHelpers.setObjectField(loadedApk, "mClassLoader", mcl); + var c = Class.forName("android.app.ContextImpl"); + var ctor = c.getDeclaredConstructors()[0]; + ctor.setAccessible(true); + var args = new Object[ctor.getParameterTypes().length]; + for (int i = 0; i < ctor.getParameterTypes().length; ++i) { + if (ctor.getParameterTypes()[i] == LoadedApk.class) { + args[i] = loadedApk; + continue; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (ctor.getParameterTypes()[i] == ContextParams.class) { + args[i] = new ContextParams.Builder().build(); + continue; + } + } + if (ctor.getParameterTypes()[i] == ActivityThread.class) { + args[i] = at; + continue; + } + if (ctor.getParameterTypes()[i] == int.class) { + args[i] = 0; + continue; + } + args[i] = null; + } + var ctx = (Context) ctor.newInstance(args); + Hookers.logD("Loaded module " + module.packageName + ": " + ctx); + } catch (Throwable e) { + Hookers.logE("Loading module " + module.packageName, e); + } + }); + } +} diff --git a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java index e8b98910..1cdbcc4a 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java @@ -40,6 +40,9 @@ public class LoadedApkCtorHooker extends XC_MethodHook { try { LoadedApk loadedApk = (LoadedApk) param.thisObject; String packageName = loadedApk.getPackageName(); + if (XposedInit.getLoadedModules().contains(packageName)) { + return; + } Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir"); Hookers.logD("LoadedApk# ends: " + mAppDir); diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index 74ddf83c..eb31126d 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -368,9 +368,11 @@ public class ConfigFileManager { // TODO: we can store more info like api version, module description, etc. in META-INF readName(apkFile, "META-INF/xposed/xposed_init", moduleClassNames); if (moduleClassNames.isEmpty()) { + file.usingContext = false; readName(apkFile, "assets/xposed_init", moduleClassNames); readName(apkFile, "assets/native_init", moduleLibraryNames); } else { + file.usingContext = true; readName(apkFile, "META-INF/xposed/native_init", moduleLibraryNames); } } catch (IOException e) { diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java index dee77012..a787e0e8 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java @@ -37,6 +37,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public class LSPApplicationService extends ILSPApplicationService.Stub { final static int DEX_TRANSACTION_CODE = 1310096052; @@ -115,8 +116,7 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { } } - @Override - public List getModulesList() throws RemoteException { + private List getAllModulesList() throws RemoteException { var processInfo = ensureRegistered(); if (processInfo.uid == 1000 && processInfo.processName.equals("android")) { return ConfigManager.getInstance().getModulesForSystemServer(); @@ -126,6 +126,16 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { return ConfigManager.getInstance().getModulesForProcess(processInfo.processName, processInfo.uid); } + @Override + public List getLegacyModulesList() throws RemoteException { + return getAllModulesList().stream().filter(m -> !m.file.usingContext).collect(Collectors.toList()); + } + + @Override + public List getModulesList() throws RemoteException { + return getAllModulesList().stream().filter(m -> m.file.usingContext).collect(Collectors.toList()); + } + @Override public String getPrefsPath(String packageName) throws RemoteException { ensureRegistered(); diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl index 643e9d67..a92cf53b 100644 --- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl +++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl @@ -4,4 +4,5 @@ parcelable PreLoadedApk { List preLoadedDexes; List moduleClassNames; List moduleLibraryNames; + boolean usingContext; } diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl index 430f3acc..8ff6199c 100644 --- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl +++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl @@ -5,6 +5,8 @@ import org.lsposed.lspd.models.Module; interface ILSPApplicationService { IBinder requestModuleBinder(String name); + List getLegacyModulesList(); + List getModulesList(); String getPrefsPath(String packageName);