Refactor DynLoad

This commit is contained in:
topjohnwu 2022-02-13 03:32:11 -08:00
parent 8f0ea5925a
commit a4aa4a91a3
2 changed files with 57 additions and 49 deletions

View File

@ -2,8 +2,11 @@ package com.topjohnwu.magisk;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log; import android.util.Log;
import java.io.File;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import io.michaelrocks.paranoid.Obfuscate; import io.michaelrocks.paranoid.Obfuscate;
@ -17,12 +20,21 @@ public class DelegateRootService extends ContextWrapper {
@Override @Override
protected void attachBaseContext(Context base) { protected void attachBaseContext(Context base) {
if (DynLoad.createApp(base) == null) if (!DynLoad.loadApk(base))
return; return;
// Create the actual RootService and call its attachBaseContext
try { try {
Constructor<?> ctor = DynLoad.apkData.getRootService().getConstructor(Object.class); // Create application to get the real root service class
var data = DynLoad.createApkData();
File apk = StubApk.current(base);
PackageManager pm = base.getPackageManager();
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
DynLoad.loader.loadClass(pkgInfo.applicationInfo.className)
.getConstructor(Object.class)
.newInstance(data.getObject());
// Create the actual RootService and call its attachBaseContext
Constructor<?> ctor = data.getRootService().getConstructor(Object.class);
ctor.setAccessible(true); ctor.setAccessible(true);
Object service = ctor.newInstance(this); Object service = ctor.newInstance(this);
DynLoad.attachContext(service, base); DynLoad.attachContext(service, base);

View File

@ -7,7 +7,6 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
@ -31,10 +30,17 @@ public class DynLoad {
// The current active classloader // The current active classloader
static ClassLoader loader = new RedirectClassLoader(); static ClassLoader loader = new RedirectClassLoader();
static Object componentFactory; static Object componentFactory;
static final StubApk.Data apkData = createApkData();
private static boolean loadedApk = false; private static boolean loadedApk = false;
static StubApk.Data createApkData() {
var data = new StubApk.Data();
data.setVersion(BuildConfig.STUB_VERSION);
data.setClassToComponent(Mapping.inverseMap);
data.setRootService(DelegateRootService.class);
return data;
}
static void attachContext(Object o, Context context) { static void attachContext(Object o, Context context) {
if (!(o instanceof ContextWrapper)) if (!(o instanceof ContextWrapper))
return; return;
@ -95,16 +101,16 @@ public class DynLoad {
} }
} }
// Dynamically load APK and create the Application instance from the loaded APK // Dynamically load APK from internal, external storage, or previous app
static Application createApp(Context context) { static boolean loadApk(Context context) {
// Trigger folder creation // Trigger folder creation
context.getExternalFilesDir(null); context.getExternalFilesDir(null);
File apk = StubApk.current(context);
loadApk(context.getApplicationInfo()); loadApk(context.getApplicationInfo());
// If no APK is loaded, attempt to copy from previous app // If no APK is loaded, attempt to copy from previous app
if (!isDynLoader() && !context.getPackageName().equals(APPLICATION_ID)) { if (!isDynLoader() && !context.getPackageName().equals(APPLICATION_ID)) {
File apk = StubApk.current(context);
try { try {
var info = context.getPackageManager().getApplicationInfo(APPLICATION_ID, 0); var info = context.getPackageManager().getApplicationInfo(APPLICATION_ID, 0);
var src = new FileInputStream(info.sourceDir); var src = new FileInputStream(info.sourceDir);
@ -120,54 +126,52 @@ public class DynLoad {
} }
} }
if (isDynLoader()) { return isDynLoader();
PackageManager pm = context.getPackageManager();
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
try {
return newApp(pkgInfo.applicationInfo);
} catch (ReflectiveOperationException | NullPointerException e) {
Log.e(DynLoad.class.getSimpleName(), "", e);
apk.delete();
}
}
return null;
} }
// Stub app setup entry // Dynamically load APK and create the Application instance from the loaded APK
static Application createAndSetupApp(Application context) { static Application createAndSetupApp(Application context) {
// On API >= 29, AppComponentFactory will replace the ClassLoader // On API >= 29, AppComponentFactory will replace the ClassLoader for us
if (Build.VERSION.SDK_INT < 29) if (Build.VERSION.SDK_INT < 29)
replaceClassLoader(context); replaceClassLoader(context);
Application app = createApp(context); if (!loadApk(context))
if (app != null) { return null;
File apk = StubApk.current(context);
PackageManager pm = context.getPackageManager();
try {
var info = pm.getPackageArchiveInfo(apk.getPath(), 0).applicationInfo;
// Create the receiver Application
var data = createApkData();
var app = (Application) loader.loadClass(info.className)
.getConstructor(Object.class)
.newInstance(data.getObject());
// Create the receiver component factory
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
Object factory = loader.loadClass(info.appComponentFactory).newInstance();
var delegate = (DelegateComponentFactory) componentFactory;
delegate.receiver = (AppComponentFactory) factory;
}
// Send real application to attachBaseContext // Send real application to attachBaseContext
attachContext(app, context); attachContext(app, context);
return app;
} catch (Exception e) {
Log.e(DynLoad.class.getSimpleName(), "", e);
apk.delete();
} }
return app;
return null;
} }
private static boolean isDynLoader() { private static boolean isDynLoader() {
return loader instanceof InjectedClassLoader; return loader instanceof InjectedClassLoader;
} }
private static Application newApp(ApplicationInfo info) throws ReflectiveOperationException {
// Create the receiver Application
var app = (Application) loader.loadClass(info.className)
.getConstructor(Object.class)
.newInstance(apkData.getObject());
// Create the receiver component factory
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
Object factory = loader.loadClass(info.appComponentFactory).newInstance();
var delegate = (DelegateComponentFactory) componentFactory;
delegate.receiver = (AppComponentFactory) factory;
}
return app;
}
// Replace LoadedApk mClassLoader // Replace LoadedApk mClassLoader
private static void replaceClassLoader(Context context) { private static void replaceClassLoader(Context context) {
// Get ContextImpl // Get ContextImpl
@ -188,12 +192,4 @@ public class DynLoad {
Log.e(DynLoad.class.getSimpleName(), "", e); Log.e(DynLoad.class.getSimpleName(), "", e);
} }
} }
private static StubApk.Data createApkData() {
var data = new StubApk.Data();
data.setVersion(BuildConfig.STUB_VERSION);
data.setClassToComponent(Mapping.inverseMap);
data.setRootService(DelegateRootService.class);
return data;
}
} }