mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-02 20:41:51 +00:00
Support RootService on stub APKs
This commit is contained in:
1
stub/proguard-rules.pro
vendored
1
stub/proguard-rules.pro
vendored
@@ -30,3 +30,4 @@
|
||||
-keepclassmembers class com.topjohnwu.magisk.dummy.* { <init>(); }
|
||||
-keepclassmembers class com.topjohnwu.magisk.DownloadActivity { <init>(); }
|
||||
-keepclassmembers class com.topjohnwu.magisk.FileProvider { <init>(); }
|
||||
-keepclassmembers class com.topjohnwu.magisk.DelegateRootService { <init>(); }
|
||||
|
||||
@@ -18,7 +18,7 @@ public class DelegateApplication extends Application {
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
|
||||
receiver = InjectAPK.setup(this);
|
||||
receiver = DynLoad.setup(this);
|
||||
if (receiver != null) try {
|
||||
// Call attachBaseContext without ContextImpl to show it is being wrapped
|
||||
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
|
||||
@@ -27,6 +27,13 @@ public class DelegateApplication extends Application {
|
||||
} catch (Exception ignored) { /* Impossible */ }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (receiver != null)
|
||||
receiver.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
@@ -16,7 +16,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
||||
AppComponentFactory receiver;
|
||||
|
||||
public DelegateComponentFactory() {
|
||||
InjectAPK.componentFactory = this;
|
||||
DynLoad.componentFactory = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import io.michaelrocks.paranoid.Obfuscate;
|
||||
|
||||
@Obfuscate
|
||||
public class DelegateRootService extends ContextWrapper {
|
||||
|
||||
public DelegateRootService() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
if (DynLoad.inject(base) == null)
|
||||
return;
|
||||
|
||||
// Create the actual RootService and call its attachBaseContext
|
||||
try {
|
||||
Constructor<?> ctor = DynLoad.apkData.getRootService().getConstructor(Object.class);
|
||||
ctor.setAccessible(true);
|
||||
Object service = ctor.newInstance(this);
|
||||
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
|
||||
m.setAccessible(true);
|
||||
m.invoke(service, base);
|
||||
} catch (Exception e) {
|
||||
Log.e(DelegateRootService.class.getSimpleName(), "", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ public class DownloadActivity extends Activity {
|
||||
File apk = dynLoad ? DynAPK.current(this) : new File(getCacheDir(), "manager.apk");
|
||||
request(apkLink).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR).getAsFile(apk, file -> {
|
||||
if (dynLoad) {
|
||||
InjectAPK.setup(this);
|
||||
DynLoad.setup(this);
|
||||
runOnUiThread(() -> {
|
||||
dialog.dismiss();
|
||||
Toast.makeText(themed, relaunch_app, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -23,13 +23,10 @@ import java.lang.reflect.Field;
|
||||
import io.michaelrocks.paranoid.Obfuscate;
|
||||
|
||||
@Obfuscate
|
||||
public class InjectAPK {
|
||||
public class DynLoad {
|
||||
|
||||
static Object componentFactory;
|
||||
|
||||
private static DelegateComponentFactory getComponentFactory() {
|
||||
return (DelegateComponentFactory) componentFactory;
|
||||
}
|
||||
static final DynAPK.Data apkData = createApkData();
|
||||
|
||||
private static void copy(InputStream src, OutputStream dest) throws IOException {
|
||||
try (InputStream s = src) {
|
||||
@@ -42,13 +39,9 @@ public class InjectAPK {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
static Application setup(Context context) {
|
||||
// Get ContextImpl
|
||||
while (context instanceof ContextWrapper) {
|
||||
context = ((ContextWrapper) context).getBaseContext();
|
||||
}
|
||||
|
||||
// Dynamically load APK, inject ClassLoader into ContextImpl, then
|
||||
// create the actual Application instance from the loaded APK
|
||||
static Application inject(Context context) {
|
||||
File apk = DynAPK.current(context);
|
||||
File update = DynAPK.update(context);
|
||||
|
||||
@@ -64,7 +57,7 @@ public class InjectAPK {
|
||||
try {
|
||||
copy(new FileInputStream(external), new FileOutputStream(apk));
|
||||
} catch (IOException e) {
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
} finally {
|
||||
external.delete();
|
||||
@@ -84,7 +77,7 @@ public class InjectAPK {
|
||||
copy(src, new FileOutputStream(apk));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
}
|
||||
}
|
||||
@@ -95,21 +88,30 @@ public class InjectAPK {
|
||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
|
||||
try {
|
||||
return createApp(context, cl, pkgInfo.applicationInfo);
|
||||
} catch (Exception e) {
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
}
|
||||
// fallthrough
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Inject and create Application, or setup redirections for the current app
|
||||
static Application setup(Context context) {
|
||||
Application app = inject(context);
|
||||
if (app != null) {
|
||||
return app;
|
||||
}
|
||||
|
||||
ClassLoader cl = new RedirectClassLoader();
|
||||
try {
|
||||
setClassLoader(context, cl);
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
getComponentFactory().loader = cl;
|
||||
((DelegateComponentFactory) componentFactory).loader = cl;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -120,40 +122,42 @@ public class InjectAPK {
|
||||
// Create the receiver Application
|
||||
Object app = cl.loadClass(info.className)
|
||||
.getConstructor(Object.class)
|
||||
.newInstance(DynAPK.pack(dynData()));
|
||||
.newInstance(apkData.getObject());
|
||||
|
||||
// Create the receiver component factory
|
||||
Object factory = null;
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
factory = cl.loadClass(info.appComponentFactory).newInstance();
|
||||
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
|
||||
Object factory = cl.loadClass(info.appComponentFactory).newInstance();
|
||||
DelegateComponentFactory delegate = (DelegateComponentFactory) componentFactory;
|
||||
delegate.loader = cl;
|
||||
delegate.receiver = (AppComponentFactory) factory;
|
||||
}
|
||||
|
||||
setClassLoader(context, cl);
|
||||
|
||||
// Finally, set variables
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
getComponentFactory().loader = cl;
|
||||
getComponentFactory().receiver = (AppComponentFactory) factory;
|
||||
}
|
||||
|
||||
return (Application) app;
|
||||
}
|
||||
|
||||
// Replace LoadedApk mClassLoader
|
||||
private static void setClassLoader(Context impl, ClassLoader cl)
|
||||
private static void setClassLoader(Context context, ClassLoader cl)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
Field mInfo = impl.getClass().getDeclaredField("mPackageInfo");
|
||||
// Get ContextImpl
|
||||
while (context instanceof ContextWrapper) {
|
||||
context = ((ContextWrapper) context).getBaseContext();
|
||||
}
|
||||
|
||||
Field mInfo = context.getClass().getDeclaredField("mPackageInfo");
|
||||
mInfo.setAccessible(true);
|
||||
Object loadedApk = mInfo.get(impl);
|
||||
Object loadedApk = mInfo.get(context);
|
||||
Field mcl = loadedApk.getClass().getDeclaredField("mClassLoader");
|
||||
mcl.setAccessible(true);
|
||||
mcl.set(loadedApk, cl);
|
||||
}
|
||||
|
||||
private static DynAPK.Data dynData() {
|
||||
private static DynAPK.Data createApkData() {
|
||||
DynAPK.Data data = new DynAPK.Data();
|
||||
data.version = BuildConfig.STUB_VERSION;
|
||||
data.classToComponent = Mapping.inverseMap;
|
||||
data.setVersion(BuildConfig.STUB_VERSION);
|
||||
data.setClassToComponent(Mapping.inverseMap);
|
||||
data.setRootService(DelegateRootService.class);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user