From 010e4de4e1271e8101530f66f107e59ee1acae15 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 4 Aug 2019 23:49:09 -0700 Subject: [PATCH] Introduce DynamicClassLoader --- .../magisk/model/download/ManagerUpgrade.kt | 4 +- .../topjohnwu/magisk/ui/home/HomeFragment.kt | 5 +- .../magisk/utils/DynamicClassLoader.kt | 61 +++++++++++++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt index 26c3e50f9..67cc48b66 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt @@ -9,10 +9,10 @@ import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Restore import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Upgrade import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.ui.SplashActivity +import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.PatchAPK import com.topjohnwu.magisk.utils.RootUtils import com.topjohnwu.superuser.Shell -import dalvik.system.DexClassLoader import timber.log.Timber import java.io.File @@ -27,7 +27,7 @@ private fun RemoteFileService.patchPackage(apk: File, id: Int): File { val patched = File(apk.parent, "patched.apk") try { // Try using the new APK to patch itself - val loader = DexClassLoader(apk.path, apk.parent, null, ClassLoader.getSystemClassLoader()) + val loader = DynamicClassLoader(apk) loader.loadClass("a.a") .getMethod("patchAPK", String::class.java, String::class.java, String::class.java) .invoke(null, apk.path, patched.path, packageName) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt index 3add88815..42f0d47fb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt @@ -18,10 +18,10 @@ import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.ui.base.MagiskActivity import com.topjohnwu.magisk.ui.base.MagiskFragment import com.topjohnwu.magisk.utils.ISafetyNetHelper +import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.magisk.view.dialogs.* import com.topjohnwu.superuser.Shell -import dalvik.system.DexClassLoader import org.koin.androidx.viewmodel.ext.android.viewModel import java.io.File @@ -97,8 +97,7 @@ class HomeFragment : MagiskFragment(), private fun updateSafetyNet(dieOnError: Boolean) { try { - val loader = DexClassLoader(EXT_APK.path, EXT_APK.parent, null, - ISafetyNetHelper::class.java.classLoader) + val loader = DynamicClassLoader(EXT_APK) val clazz = loader.loadClass("com.topjohnwu.snet.Snet") val helper = clazz.getMethod("newHelper", Class::class.java, String::class.java, Activity::class.java, Any::class.java) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt b/app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt new file mode 100644 index 000000000..9810c91bd --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DynamicClassLoader.kt @@ -0,0 +1,61 @@ +package com.topjohnwu.magisk.utils + +import dalvik.system.DexClassLoader +import java.io.File +import java.io.IOException +import java.net.URL +import java.util.* + +@Suppress("FunctionName") +inline fun T.DynamicClassLoader(apk: File) = DynamicClassLoader(apk, T::class.java.classLoader) + +class DynamicClassLoader(apk: File, parent: ClassLoader?) + : DexClassLoader(apk.path, apk.parent, null, parent) { + + private val base by lazy { Any::class.java.classLoader!! } + + @Throws(ClassNotFoundException::class) + override fun loadClass(name: String, resolve: Boolean) : Class<*> + = findLoadedClass(name) ?: runCatching { + base.loadClass(name) + }.getOrElse { + runCatching { + findClass(name) + }.getOrElse { err -> + runCatching { + parent.loadClass(name) + }.getOrElse { throw err } + } + } + + override fun getResource(name: String) = base.getResource(name) + ?: findResource(name) + ?: parent?.getResource(name) + + @Throws(IOException::class) + override fun getResources(name: String): Enumeration { + val resources = mutableListOf( + base.getResources(name), + findResources(name), parent.getResources(name)) + return object : Enumeration { + override fun hasMoreElements(): Boolean { + while (true) { + if (resources.isEmpty()) + return false + if (!resources[0].hasMoreElements()) { + resources.removeAt(0) + } else { + return true + } + } + } + + override fun nextElement(): URL { + if (!hasMoreElements()) + throw NoSuchElementException() + return resources[0].nextElement() + } + } + } + +}