From 072845650d5d1344fb2b4806df6045ad8d862fbc Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sun, 19 Sep 2021 02:24:34 +0800 Subject: [PATCH] Correct classloader --- .../lspatch/loader/LSPApplication.java | 114 +++++------------- .../org/lsposed/lspatch/loader/LSPLoader.java | 14 +-- .../appstub/LSPAppComponentFactoryStub.java | 51 +------- 3 files changed, 43 insertions(+), 136 deletions(-) diff --git a/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java b/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java index 2bf1339c..a158f6b5 100644 --- a/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java +++ b/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java @@ -3,17 +3,15 @@ import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE; import static org.lsposed.lspatch.share.Constants.ORIGINAL_APK_ASSET_PATH; import static org.lsposed.lspatch.share.Constants.ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH; -import static org.lsposed.lspatch.share.Constants.PROXY_APP_COMPONENT_FACTORY; import static org.lsposed.lspd.service.ConfigFileManager.loadModule; -import android.annotation.SuppressLint; import android.app.ActivityThread; -import android.app.AppComponentFactory; import android.app.LoadedApk; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.Signature; +import android.content.res.CompatibilityInfo; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -21,6 +19,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.system.Os; +import android.util.ArrayMap; import android.util.Log; import org.lsposed.lspatch.loader.util.FileUtils; @@ -36,18 +35,14 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.zip.ZipFile; -import dalvik.system.DelegateLastClassLoader; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; @@ -62,11 +57,11 @@ public class LSPApplication extends ApplicationServiceClient { private static final String USE_MANAGER_CONTROL_PATH = "use_manager.ini"; private static final String TAG = "LSPatch"; + private static ActivityThread activityThread; + private static LoadedApk appLoadedApk; private static boolean useManager; private static String originalSignature = null; private static ManagerResolver managerResolver = null; - private static Object activityThread; - private static LoadedApk loadedApkObj; final static public int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; final static public int PER_USER_RANGE = 100000; @@ -86,15 +81,14 @@ public static void onLoad() { XLog.d(TAG, "skip isolated process"); return; } - Context context = createAppContext(); + activityThread = ActivityThread.currentActivityThread(); + var context = createLoadedApkWithContext(); if (context == null) { XLog.e(TAG, "create context err"); return; } - initAppComponentFactory(context); - - useManager = Boolean.parseBoolean(Objects.requireNonNull(FileUtils.readTextFromAssets(context, USE_MANAGER_CONTROL_PATH))); + useManager = Boolean.parseBoolean(FileUtils.readTextFromAssets(context, USE_MANAGER_CONTROL_PATH)); originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH); if (useManager) try { @@ -116,25 +110,31 @@ public static void onLoad() { XposedInit.loadModules(); // WARN: Since it uses `XResource`, the following class should not be initialized // before forkPostCommon is invoke. Otherwise, you will get failure of XResources - LSPLoader.initModules(context); + LSPLoader.initModules(appLoadedApk); } catch (Throwable e) { Log.e(TAG, "Do hook", e); } } - @SuppressLint("DiscouragedPrivateApi") - private static void initAppComponentFactory(Context context) { + private static Context createLoadedApkWithContext() { try { - ApplicationInfo aInfo = context.getApplicationInfo(); - ClassLoader baseClassLoader = context.getClassLoader(); - Class stubClass = Class.forName(PROXY_APP_COMPONENT_FACTORY, false, baseClassLoader); + var baseClassLoader = LSPApplication.class.getClassLoader().getParent(); + + var mBoundApplication = XposedHelpers.getObjectField(activityThread, "mBoundApplication"); + var stubLoadedApk = (LoadedApk) XposedHelpers.getObjectField(mBoundApplication, "info"); + var appInfo = (ApplicationInfo) XposedHelpers.getObjectField(mBoundApplication, "appInfo"); + var compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(mBoundApplication, "compatInfo"); - String originPath = aInfo.dataDir + "/cache/lspatch/origin/"; - String originalAppComponentFactoryClass = FileUtils.readTextFromInputStream(baseClassLoader.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH)); + String originPath = appInfo.dataDir + "/cache/lspatch/origin/"; String cacheApkPath; - try (ZipFile sourceFile = new ZipFile(aInfo.sourceDir)) { + try (ZipFile sourceFile = new ZipFile(appInfo.sourceDir)) { cacheApkPath = originPath + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc(); } + + appInfo.sourceDir = cacheApkPath; + appInfo.publicSourceDir = cacheApkPath; + appInfo.appComponentFactory = FileUtils.readTextFromInputStream(baseClassLoader.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH)); + if (!Files.exists(Paths.get(cacheApkPath))) { Log.i(TAG, "extract original apk"); FileUtils.deleteFolderIfExists(Paths.get(originPath)); @@ -143,26 +143,17 @@ private static void initAppComponentFactory(Context context) { Files.copy(is, Paths.get(cacheApkPath)); } } - // TODO: The last param should be baseClassLoader.getParent(), but it breaks sigbypass and I don't know why - var appClassLoader = new DelegateLastClassLoader(cacheApkPath, aInfo.nativeLibraryDir, baseClassLoader); - AppComponentFactory originalAppComponentFactory; - try { - originalAppComponentFactory = (AppComponentFactory) appClassLoader.loadClass(originalAppComponentFactoryClass).newInstance(); - } catch (ClassNotFoundException | NullPointerException ignored) { - if (originalAppComponentFactoryClass != null && !originalAppComponentFactoryClass.isEmpty()) - Log.w(TAG, "original AppComponentFactory not found"); - originalAppComponentFactory = new AppComponentFactory(); - } - Field mClassLoaderField = LoadedApk.class.getDeclaredField("mClassLoader"); - mClassLoaderField.setAccessible(true); - mClassLoaderField.set(loadedApkObj, appClassLoader); - stubClass.getDeclaredField("appClassLoader").set(null, appClassLoader); - stubClass.getDeclaredField("originalAppComponentFactory").set(null, originalAppComponentFactory); + var mPackages = (ArrayMap) XposedHelpers.getObjectField(activityThread, "mPackages"); + mPackages.remove(appInfo.packageName); + appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo); + XposedHelpers.setObjectField(mBoundApplication, "info", appLoadedApk); + Log.i(TAG, "appClassLoader initialized: " + appLoadedApk.getClassLoader()); - Log.d(TAG, "set up original AppComponentFactory"); + return (Context) XposedHelpers.callStaticMethod(Class.forName("android.app.ContextImpl"), "createAppContext", activityThread, stubLoadedApk); } catch (Throwable e) { - Log.e(TAG, "initAppComponentFactory", e); + Log.e(TAG, "createLoadedApk", e); + return null; } } @@ -358,51 +349,6 @@ private static int fetchSigbypassLv(Context context) { return 0; } - private static Object getActivityThread() { - if (activityThread == null) { - try { - activityThread = ActivityThread.currentActivityThread(); - } catch (Throwable e) { - Log.e(TAG, "getActivityThread", e); - } - } - return activityThread; - } - - public static Context createAppContext() { - try { - - ActivityThread activityThreadObj = ActivityThread.currentActivityThread(); - - Field boundApplicationField = ActivityThread.class.getDeclaredField("mBoundApplication"); - boundApplicationField.setAccessible(true); - Object mBoundApplication = boundApplicationField.get(activityThreadObj); // AppBindData - if (mBoundApplication == null) { - Log.e(TAG, "mBoundApplication null"); - return null; - } - Field infoField = mBoundApplication.getClass().getDeclaredField("info"); // info - infoField.setAccessible(true); - loadedApkObj = (LoadedApk) infoField.get(mBoundApplication); // LoadedApk - if (loadedApkObj == null) { - Log.e(TAG, "loadedApkObj null"); - return null; - } - Class contextImplClass = Class.forName("android.app.ContextImpl"); - Method createAppContextMethod = contextImplClass.getDeclaredMethod("createAppContext", ActivityThread.class, LoadedApk.class); - createAppContextMethod.setAccessible(true); - - Object context = createAppContextMethod.invoke(null, activityThreadObj, loadedApkObj); - - if (context instanceof Context) { - return (Context) context; - } - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) { - Log.e(TAG, "Fail to create app context", e); - } - return null; - } - @Override public IBinder requestModuleBinder(String name) { return null; diff --git a/app/src/main/java/org/lsposed/lspatch/loader/LSPLoader.java b/app/src/main/java/org/lsposed/lspatch/loader/LSPLoader.java index c7e40557..bce721f2 100644 --- a/app/src/main/java/org/lsposed/lspatch/loader/LSPLoader.java +++ b/app/src/main/java/org/lsposed/lspatch/loader/LSPLoader.java @@ -1,7 +1,7 @@ package org.lsposed.lspatch.loader; import android.app.ActivityThread; -import android.content.Context; +import android.app.LoadedApk; import android.content.res.XResources; import de.robv.android.xposed.XposedBridge; @@ -9,15 +9,15 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage; public class LSPLoader { - public static void initModules(Context context) { - XposedInit.loadedPackagesInProcess.add(context.getPackageName()); - XResources.setPackageNameForResDir(context.getPackageName(), context.getPackageResourcePath()); + public static void initModules(LoadedApk loadedApk) { + XposedInit.loadedPackagesInProcess.add(loadedApk.getPackageName()); + XResources.setPackageNameForResDir(loadedApk.getPackageName(), loadedApk.getResDir()); XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam( XposedBridge.sLoadedPackageCallbacks); - lpparam.packageName = context.getPackageName(); + lpparam.packageName = loadedApk.getPackageName(); lpparam.processName = ActivityThread.currentProcessName(); - lpparam.classLoader = context.getClassLoader(); - lpparam.appInfo = context.getApplicationInfo(); + lpparam.classLoader = loadedApk.getClassLoader(); + lpparam.appInfo = loadedApk.getApplicationInfo(); lpparam.isFirstApplication = true; XC_LoadPackage.callAll(lpparam); } diff --git a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java index df41c302..ae15de2f 100644 --- a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java +++ b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java @@ -1,30 +1,20 @@ package org.lsposed.lspatch.appstub; import android.annotation.SuppressLint; -import android.app.Activity; import android.app.AppComponentFactory; -import android.app.Application; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentProvider; -import android.content.Intent; import android.util.Log; import java.io.ByteArrayOutputStream; import java.lang.reflect.Method; +import java.util.Objects; -@SuppressLint({"UnsafeDynamicallyLoadedCode", "DiscouragedPrivateApi"}) +@SuppressLint("UnsafeDynamicallyLoadedCode") public class LSPAppComponentFactoryStub extends AppComponentFactory { - private static final String TAG = "LSPatch"; - public static byte[] dex = null; - public static ClassLoader appClassLoader = null; - public static ClassLoader baseClassLoader = null; - public static AppComponentFactory originalAppComponentFactory = null; - public LSPAppComponentFactoryStub() { - baseClassLoader = getClass().getClassLoader(); - try (var is = baseClassLoader.getResourceAsStream("assets/lsp"); + static { + var cl = Objects.requireNonNull(LSPAppComponentFactoryStub.class.getClassLoader()); + try (var is = cl.getResourceAsStream("assets/lsp"); var os = new ByteArrayOutputStream()) { byte[] buffer = new byte[8192]; int n; @@ -44,39 +34,10 @@ public LSPAppComponentFactoryStub() { vmInstructionSet.setAccessible(true); String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null)); - String path = baseClassLoader.getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5); + String path = cl.getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5); System.load(path); } catch (Throwable e) { Log.e("LSPatch", "load lspd error", e); } } - - @Override - public Application instantiateApplication(ClassLoader cl, String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException { - Log.d(TAG, "baseClassLoader is " + baseClassLoader); - Log.d(TAG, "appClassLoader is " + appClassLoader); - Log.d(TAG, "originalAppComponentFactory is " + originalAppComponentFactory); - Log.i(TAG, "lspd initialized, instantiate original application"); - return originalAppComponentFactory.instantiateApplication(cl, className); - } - - @Override - public Activity instantiateActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { - return originalAppComponentFactory.instantiateActivity(cl, className, intent); - } - - @Override - public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { - return originalAppComponentFactory.instantiateReceiver(cl, className, intent); - } - - @Override - public Service instantiateService(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { - return originalAppComponentFactory.instantiateService(cl, className, intent); - } - - @Override - public ContentProvider instantiateProvider(ClassLoader cl, String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { - return originalAppComponentFactory.instantiateProvider(cl, className); - } } \ No newline at end of file