mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-27 12:05:30 +00:00
Generate class mapping at runtime
This commit is contained in:
parent
a4aa4a91a3
commit
c09b4dabc4
@ -74,13 +74,6 @@ fun genKeyData(keysDir: File, outSrc: File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun genStubManifest(srcDir: File, outDir: File): String {
|
fun genStubManifest(srcDir: File, outDir: File): String {
|
||||||
class Component(
|
|
||||||
val real: String,
|
|
||||||
val stub: String,
|
|
||||||
val xml: String,
|
|
||||||
val genClass: Boolean = false
|
|
||||||
)
|
|
||||||
|
|
||||||
outDir.deleteRecursively()
|
outDir.deleteRecursively()
|
||||||
|
|
||||||
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
||||||
@ -88,25 +81,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
|
|
||||||
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
||||||
|
|
||||||
val cmpList = mutableListOf<Component>()
|
val cmpList = mutableListOf<String>()
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"androidx.core.app.CoreComponentFactory",
|
|
||||||
"DelegateComponentFactory",
|
|
||||||
"",
|
|
||||||
true
|
|
||||||
))
|
|
||||||
|
|
||||||
cmpList.add(Component(
|
|
||||||
"com.topjohnwu.magisk.core.App",
|
|
||||||
"DelegateApplication",
|
|
||||||
"",
|
|
||||||
true
|
|
||||||
))
|
|
||||||
|
|
||||||
cmpList.add(Component(
|
|
||||||
"com.topjohnwu.magisk.core.Provider",
|
|
||||||
"dummy.DummyProvider",
|
|
||||||
"""
|
"""
|
||||||
|<provider
|
|<provider
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@ -114,11 +91,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| android:directBootAware="true"
|
| android:directBootAware="true"
|
||||||
| android:exported="false"
|
| android:exported="false"
|
||||||
| android:grantUriPermissions="true" />""".ind(2)
|
| android:grantUriPermissions="true" />""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.core.Receiver",
|
|
||||||
"dummy.DummyReceiver",
|
|
||||||
"""
|
"""
|
||||||
|<receiver
|
|<receiver
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@ -135,11 +110,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| <data android:scheme="package" />
|
| <data android:scheme="package" />
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
|</receiver>""".ind(2)
|
|</receiver>""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.ui.MainActivity",
|
|
||||||
"DownloadActivity",
|
|
||||||
"""
|
"""
|
||||||
|<activity
|
|<activity
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@ -149,11 +122,9 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| <category android:name="android.intent.category.LAUNCHER" />
|
| <category android:name="android.intent.category.LAUNCHER" />
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
|</activity>""".ind(2)
|
|</activity>""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.ui.surequest.SuRequestActivity",
|
|
||||||
"",
|
|
||||||
"""
|
"""
|
||||||
|<activity
|
|<activity
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
@ -167,26 +138,22 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
| <category android:name="android.intent.category.DEFAULT"/>
|
| <category android:name="android.intent.category.DEFAULT"/>
|
||||||
| </intent-filter>
|
| </intent-filter>
|
||||||
|</activity>""".ind(2)
|
|</activity>""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.core.download.DownloadService",
|
|
||||||
"",
|
|
||||||
"""
|
"""
|
||||||
|<service
|
|<service
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
| android:exported="false" />""".trimIndent().ind(2)
|
| android:exported="false" />""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
cmpList.add(Component(
|
cmpList.add(
|
||||||
"com.topjohnwu.magisk.core.JobService",
|
|
||||||
"",
|
|
||||||
"""
|
"""
|
||||||
|<service
|
|<service
|
||||||
| android:name="%s"
|
| android:name="%s"
|
||||||
| android:exported="false"
|
| android:exported="false"
|
||||||
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
|
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
|
||||||
))
|
)
|
||||||
|
|
||||||
val names = mutableListOf<String>()
|
val names = mutableListOf<String>()
|
||||||
names.addAll(c1)
|
names.addAll(c1)
|
||||||
@ -194,33 +161,38 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
names.addAll(c3.subList(0, 10))
|
names.addAll(c3.subList(0, 10))
|
||||||
names.shuffle(RANDOM)
|
names.shuffle(RANDOM)
|
||||||
|
|
||||||
val pkgNames = names.subList(0, 50)
|
val pkgNames = names
|
||||||
// Distinct by lower case to support case insensitive file systems
|
// Distinct by lower case to support case insensitive file systems
|
||||||
.distinctBy { it.toLowerCase(Locale.ROOT) }
|
.distinctBy { it.toLowerCase(Locale.ROOT) }
|
||||||
// Old Android does not support capitalized package names
|
// Old Android does not support capitalized package names
|
||||||
// Check Android 7.0.0 PackageParser#buildClassName
|
// Check Android 7.0.0 PackageParser#buildClassName
|
||||||
.map { it.decapitalize(Locale.ROOT) }
|
.map { it.decapitalize(Locale.ROOT) }
|
||||||
|
|
||||||
var idx = 0
|
|
||||||
fun isJavaKeyword(name: String) = when (name) {
|
fun isJavaKeyword(name: String) = when (name) {
|
||||||
"do", "if", "for", "int", "new", "try" -> true
|
"do", "if", "for", "int", "new", "try" -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genCmpName() : String {
|
val cmps = mutableListOf<String>()
|
||||||
var pkgName : String
|
val usedNames = mutableListOf<String>()
|
||||||
|
|
||||||
|
fun genCmpName(): String {
|
||||||
|
var pkgName: String
|
||||||
do {
|
do {
|
||||||
pkgName = pkgNames[idx++]
|
pkgName = pkgNames.random(kRANDOM)
|
||||||
} while (isJavaKeyword(pkgName))
|
} while (isJavaKeyword(pkgName))
|
||||||
|
|
||||||
var clzName : String
|
var clzName: String
|
||||||
do {
|
do {
|
||||||
clzName = names.random(kRANDOM)
|
clzName = names.random(kRANDOM)
|
||||||
} while (isJavaKeyword(clzName))
|
} while (isJavaKeyword(clzName))
|
||||||
return "${pkgName}.${clzName}"
|
val cmp = "${pkgName}.${clzName}"
|
||||||
|
usedNames.add(cmp)
|
||||||
|
return cmp
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genClass(clzName: String, type: String) {
|
fun genClass(type: String) {
|
||||||
|
val clzName = genCmpName()
|
||||||
val (pkg, name) = clzName.split('.')
|
val (pkg, name) = clzName.split('.')
|
||||||
val pkgDir = File(outDir, pkg)
|
val pkgDir = File(outDir, pkg)
|
||||||
pkgDir.mkdir()
|
pkgDir.mkdir()
|
||||||
@ -230,41 +202,20 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cmps = mutableListOf<String>()
|
// Generate 2 non redirect-able classes
|
||||||
val usedNames = mutableListOf<String>()
|
genClass("DelegateComponentFactory")
|
||||||
val maps = StringBuilder()
|
genClass("DelegateApplication")
|
||||||
|
|
||||||
for (gen in cmpList) {
|
for (gen in cmpList) {
|
||||||
val name = genCmpName()
|
val name = genCmpName()
|
||||||
usedNames.add(name)
|
cmps.add(gen.format(name))
|
||||||
maps.append("|map.put(\"$name\", \"${gen.real}\");".ind(2))
|
|
||||||
maps.append('\n')
|
|
||||||
if (gen.stub.isNotEmpty()) {
|
|
||||||
if (gen.stub != "DelegateComponentFactory") {
|
|
||||||
maps.append("|internalMap.put(\"$name\", com.topjohnwu.magisk.${gen.stub}.class);".ind(2))
|
|
||||||
maps.append('\n')
|
|
||||||
}
|
|
||||||
if (gen.genClass) {
|
|
||||||
genClass(name, gen.stub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gen.xml.isNotEmpty()) {
|
|
||||||
cmps.add(gen.xml.format(name))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shuffle the order of the components
|
// Shuffle the order of the components
|
||||||
cmps.shuffle(RANDOM)
|
cmps.shuffle(RANDOM)
|
||||||
|
|
||||||
val xml = File(srcDir, "AndroidManifest.xml").readText()
|
val xml = File(srcDir, "AndroidManifest.xml").readText()
|
||||||
val genXml = xml.format(usedNames[0], usedNames[1], cmps.joinToString("\n\n"))
|
return xml.format(usedNames[0], usedNames[1], cmps.joinToString("\n\n"))
|
||||||
|
|
||||||
// Write mapping information to code
|
|
||||||
val mapping = File(srcDir, "Mapping.java").readText().format(maps)
|
|
||||||
PrintStream(File(mainPkgDir, "Mapping.java")).use {
|
|
||||||
it.print(mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
return genXml
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genEncryptedResources(res: InputStream, outDir: File) {
|
fun genEncryptedResources(res: InputStream, outDir: File) {
|
||||||
|
@ -3,6 +3,7 @@ package com.topjohnwu.magisk;
|
|||||||
import com.topjohnwu.magisk.utils.DynamicClassLoader;
|
import com.topjohnwu.magisk.utils.DynamicClassLoader;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
// Wrap the actual classloader as we only want to resolve classname
|
// Wrap the actual classloader as we only want to resolve classname
|
||||||
// mapping when loading from platform (via LoadedApk.mClassLoader)
|
// mapping when loading from platform (via LoadedApk.mClassLoader)
|
||||||
@ -14,19 +15,24 @@ class InjectedClassLoader extends ClassLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
return super.loadClass(Mapping.get(name), resolve);
|
String clz = DynLoad.componentMap.get(name);
|
||||||
|
name = clz != null ? clz : name;
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RedirectClassLoader extends ClassLoader {
|
class RedirectClassLoader extends ClassLoader {
|
||||||
|
|
||||||
RedirectClassLoader() {
|
private final Map<String, Class<?>> mapping;
|
||||||
|
|
||||||
|
RedirectClassLoader(Map<String, Class<?>> m) {
|
||||||
super(RedirectClassLoader.class.getClassLoader());
|
super(RedirectClassLoader.class.getClassLoader());
|
||||||
|
mapping = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
Class<?> clz = Mapping.internalMap.get(name);
|
Class<?> clz = mapping.get(name);
|
||||||
return clz == null ? super.loadClass(name, resolve) : clz;
|
return clz == null ? super.loadClass(name, resolve) : clz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ import android.content.Intent;
|
|||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyProvider;
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyReceiver;
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyService;
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
public class DelegateComponentFactory extends AppComponentFactory {
|
public class DelegateComponentFactory extends AppComponentFactory {
|
||||||
|
|
||||||
@ -40,7 +44,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateActivity(DynLoad.loader, className, intent);
|
return receiver.instantiateActivity(DynLoad.loader, className, intent);
|
||||||
return create(className);
|
return create(className, DownloadActivity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -48,7 +52,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateReceiver(DynLoad.loader, className, intent);
|
return receiver.instantiateReceiver(DynLoad.loader, className, intent);
|
||||||
return create(className);
|
return create(className, DummyReceiver.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -56,7 +60,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateService(DynLoad.loader, className, intent);
|
return receiver.instantiateService(DynLoad.loader, className, intent);
|
||||||
return create(className);
|
return create(className, DummyService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -64,12 +68,17 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateProvider(DynLoad.loader, className);
|
return receiver.instantiateProvider(DynLoad.loader, className);
|
||||||
return create(className);
|
return create(className, DummyProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T create(String name)
|
private <T> T create(String name, Class<T> fallback)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException{
|
throws IllegalAccessException, InstantiationException {
|
||||||
|
try {
|
||||||
|
// noinspection unchecked
|
||||||
return (T) DynLoad.loader.loadClass(name).newInstance();
|
return (T) DynLoad.loader.loadClass(name).newInstance();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return fallback.newInstance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,14 @@ public class DownloadActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
if (DynLoad.isDynLoader()) {
|
||||||
|
// For some reason activity is created before Application.attach(),
|
||||||
|
// relaunch the activity using the same intent
|
||||||
|
finishAffinity();
|
||||||
|
startActivity(getIntent());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
themed = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault);
|
themed = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault);
|
||||||
|
|
||||||
// Only download and dynamic load full APK if hidden
|
// Only download and dynamic load full APK if hidden
|
||||||
|
@ -4,14 +4,21 @@ import static com.topjohnwu.magisk.BuildConfig.APPLICATION_ID;
|
|||||||
|
|
||||||
import android.app.AppComponentFactory;
|
import android.app.AppComponentFactory;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.app.job.JobService;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
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.content.pm.ServiceInfo;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyProvider;
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyReceiver;
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyService;
|
||||||
import com.topjohnwu.magisk.utils.APKInstall;
|
import com.topjohnwu.magisk.utils.APKInstall;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -20,6 +27,8 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import io.michaelrocks.paranoid.Obfuscate;
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
@ -28,15 +37,20 @@ import io.michaelrocks.paranoid.Obfuscate;
|
|||||||
public class DynLoad {
|
public class DynLoad {
|
||||||
|
|
||||||
// The current active classloader
|
// The current active classloader
|
||||||
static ClassLoader loader = new RedirectClassLoader();
|
static ClassLoader loader = DynLoad.class.getClassLoader();
|
||||||
static Object componentFactory;
|
static Object componentFactory;
|
||||||
|
static Map<String, String> componentMap = new HashMap<>();
|
||||||
|
|
||||||
private static boolean loadedApk = false;
|
private static boolean loadedApk = false;
|
||||||
|
|
||||||
static StubApk.Data createApkData() {
|
static StubApk.Data createApkData() {
|
||||||
var data = new StubApk.Data();
|
var data = new StubApk.Data();
|
||||||
data.setVersion(BuildConfig.STUB_VERSION);
|
data.setVersion(BuildConfig.STUB_VERSION);
|
||||||
data.setClassToComponent(Mapping.inverseMap);
|
Map<String, String> map = new HashMap<>();
|
||||||
|
for (var e : componentMap.entrySet()) {
|
||||||
|
map.put(e.getValue(), e.getKey());
|
||||||
|
}
|
||||||
|
data.setClassToComponent(map);
|
||||||
data.setRootService(DelegateRootService.class);
|
data.setRootService(DelegateRootService.class);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -135,23 +149,40 @@ public class DynLoad {
|
|||||||
if (Build.VERSION.SDK_INT < 29)
|
if (Build.VERSION.SDK_INT < 29)
|
||||||
replaceClassLoader(context);
|
replaceClassLoader(context);
|
||||||
|
|
||||||
if (!loadApk(context))
|
int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
|
||||||
|
| PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS;
|
||||||
|
|
||||||
|
final PackageInfo info;
|
||||||
|
try {
|
||||||
|
info = context.getPackageManager()
|
||||||
|
.getPackageInfo(context.getPackageName(), flags);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
// Impossible
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadApk(context)) {
|
||||||
|
loader = new RedirectClassLoader(createInternalMap(info));
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
File apk = StubApk.current(context);
|
File apk = StubApk.current(context);
|
||||||
PackageManager pm = context.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
try {
|
try {
|
||||||
var info = pm.getPackageArchiveInfo(apk.getPath(), 0).applicationInfo;
|
var pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), flags);
|
||||||
|
var appInfo = pkgInfo.applicationInfo;
|
||||||
|
|
||||||
|
updateComponentMap(info, pkgInfo);
|
||||||
|
|
||||||
// Create the receiver Application
|
// Create the receiver Application
|
||||||
var data = createApkData();
|
var data = createApkData();
|
||||||
var app = (Application) loader.loadClass(info.className)
|
var app = (Application) loader.loadClass(appInfo.className)
|
||||||
.getConstructor(Object.class)
|
.getConstructor(Object.class)
|
||||||
.newInstance(data.getObject());
|
.newInstance(data.getObject());
|
||||||
|
|
||||||
// Create the receiver component factory
|
// Create the receiver component factory
|
||||||
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
|
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
|
||||||
Object factory = loader.loadClass(info.appComponentFactory).newInstance();
|
Object factory = loader.loadClass(appInfo.appComponentFactory).newInstance();
|
||||||
var delegate = (DelegateComponentFactory) componentFactory;
|
var delegate = (DelegateComponentFactory) componentFactory;
|
||||||
delegate.receiver = (AppComponentFactory) factory;
|
delegate.receiver = (AppComponentFactory) factory;
|
||||||
}
|
}
|
||||||
@ -168,7 +199,90 @@ public class DynLoad {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDynLoader() {
|
private static Map<String, Class<?>> createInternalMap(PackageInfo info) {
|
||||||
|
Map<String, Class<?>> map = new HashMap<>();
|
||||||
|
for (var c : info.activities) {
|
||||||
|
map.put(c.name, DownloadActivity.class);
|
||||||
|
}
|
||||||
|
for (var c : info.services) {
|
||||||
|
map.put(c.name, DummyService.class);
|
||||||
|
}
|
||||||
|
for (var c : info.providers) {
|
||||||
|
map.put(c.name, DummyProvider.class);
|
||||||
|
}
|
||||||
|
for (var c : info.receivers) {
|
||||||
|
map.put(c.name, DummyReceiver.class);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateComponentMap(PackageInfo from, PackageInfo to) {
|
||||||
|
{
|
||||||
|
var src = from.activities;
|
||||||
|
var dest = to.activities;
|
||||||
|
|
||||||
|
final ActivityInfo sa;
|
||||||
|
final ActivityInfo da;
|
||||||
|
final ActivityInfo sb;
|
||||||
|
final ActivityInfo db;
|
||||||
|
if (src[0].exported) {
|
||||||
|
sa = src[0];
|
||||||
|
sb = src[1];
|
||||||
|
} else {
|
||||||
|
sa = src[1];
|
||||||
|
sb = src[0];
|
||||||
|
}
|
||||||
|
if (dest[0].exported) {
|
||||||
|
da = dest[0];
|
||||||
|
db = dest[1];
|
||||||
|
} else {
|
||||||
|
da = dest[1];
|
||||||
|
db = dest[0];
|
||||||
|
}
|
||||||
|
componentMap.put(sa.name, da.name);
|
||||||
|
componentMap.put(sb.name, db.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var src = from.services;
|
||||||
|
var dest = to.services;
|
||||||
|
|
||||||
|
final ServiceInfo sa;
|
||||||
|
final ServiceInfo da;
|
||||||
|
final ServiceInfo sb;
|
||||||
|
final ServiceInfo db;
|
||||||
|
if (JobService.PERMISSION_BIND.equals(src[0].permission)) {
|
||||||
|
sa = src[0];
|
||||||
|
sb = src[1];
|
||||||
|
} else {
|
||||||
|
sa = src[1];
|
||||||
|
sb = src[0];
|
||||||
|
}
|
||||||
|
if (JobService.PERMISSION_BIND.equals(dest[0].permission)) {
|
||||||
|
da = dest[0];
|
||||||
|
db = dest[1];
|
||||||
|
} else {
|
||||||
|
da = dest[1];
|
||||||
|
db = dest[0];
|
||||||
|
}
|
||||||
|
componentMap.put(sa.name, da.name);
|
||||||
|
componentMap.put(sb.name, db.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var src = from.receivers;
|
||||||
|
var dest = to.receivers;
|
||||||
|
componentMap.put(src[0].name, dest[0].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var src = from.providers;
|
||||||
|
var dest = to.providers;
|
||||||
|
componentMap.put(src[0].name, dest[0].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isDynLoader() {
|
||||||
return loader instanceof InjectedClassLoader;
|
return loader instanceof InjectedClassLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.topjohnwu.magisk.dummy;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public class DummyService extends Service {
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
package com.topjohnwu.magisk;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import io.michaelrocks.paranoid.Obfuscate;
|
|
||||||
|
|
||||||
@Obfuscate
|
|
||||||
public class Mapping {
|
|
||||||
|
|
||||||
private static final Map<String, String> map = new HashMap<>();
|
|
||||||
public static final Map<String, Class<?>> internalMap = new HashMap<>();
|
|
||||||
public static final Map<String, String> inverseMap = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
%s
|
|
||||||
for (Map.Entry<String, String> e : map.entrySet()) {
|
|
||||||
inverseMap.put(e.getValue(), e.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String get(String name) {
|
|
||||||
String n = map.get(name);
|
|
||||||
return n != null ? n : name;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user