From 78daa2eb62fefde8ad7049d3224572b2819b7bbc Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 17 Oct 2019 04:47:46 -0400 Subject: [PATCH] Do not use string resources for app label This not only simplifies hiding stub APKs (no resource IDs involved), but also opens the opportunity to allow users to customize whatever app name they want after it is hidden. --- app/proguard-rules.pro | 2 +- app/src/main/java/a/a.java | 7 +- .../magisk/model/download/ManagerUpgrade.kt | 14 +--- .../magisk/ui/settings/SettingsFragment.kt | 5 +- .../com/topjohnwu/magisk/utils/PatchAPK.kt | 69 +++++++++++-------- shared/src/main/AndroidManifest.xml | 2 +- 6 files changed, 50 insertions(+), 49 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c95d10abb..10d54c59d 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -32,7 +32,7 @@ -keep,allowobfuscation class * extends com.topjohnwu.magisk.base.DelegateWorker # BootSigner --keepclassmembers class com.topjohnwu.signing.BootSigner { *; } +-keep class a.a { *; } # Strip logging -assumenosideeffects class timber.log.Timber.Tree { *; } diff --git a/app/src/main/java/a/a.java b/app/src/main/java/a/a.java index 5ccb6738b..23927a021 100644 --- a/app/src/main/java/a/a.java +++ b/app/src/main/java/a/a.java @@ -1,18 +1,21 @@ package a; -import androidx.annotation.Keep; import androidx.core.app.AppComponentFactory; import com.topjohnwu.magisk.utils.PatchAPK; import com.topjohnwu.signing.BootSigner; -@Keep public class a extends AppComponentFactory { + @Deprecated public static boolean patchAPK(String in, String out, String pkg) { return PatchAPK.patch(in, out, pkg); } + public static boolean patchAPK(String in, String out, String pkg, String label) { + return PatchAPK.patch(in, out, pkg, label); + } + public static void main(String[] args) throws Exception { BootSigner.main(args); } 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 bc8e85dff..f75a8a0c1 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 @@ -4,7 +4,6 @@ import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.cmp -import com.topjohnwu.magisk.extensions.DynamicClassLoader 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 @@ -12,7 +11,6 @@ import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.utils.PatchAPK import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell -import timber.log.Timber import java.io.File private fun RemoteFileService.patchPackage(apk: File, id: Int) { @@ -24,17 +22,7 @@ private fun RemoteFileService.patchPackage(apk: File, id: Int) { .setContentText("") } val patched = File(apk.parent, "patched.apk") - try { - // Try using the new APK to patch itself - 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) - } catch (e: Exception) { - Timber.e(e) - // Fallback to use the current implementation - PatchAPK.patch(apk.path, patched.path, packageName) - } + PatchAPK.patch(apk, patched, packageName, applicationInfo.nonLocalizedLabel.toString()) apk.delete() patched.renameTo(apk) } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index c67ce091a..9da82ee24 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -67,7 +67,8 @@ class SettingsFragment : BasePreferenceFragment() { val suCategory = findPreference("superuser")!! val hideManager = findPreference("hide")!! hideManager.setOnPreferenceClickListener { - PatchAPK.hideManager(requireContext()) + // TODO: Add UI to allow user to customize app name + PatchAPK.hideManager(requireContext(), "Manager") true } val restoreManager = findPreference("restore") @@ -326,4 +327,4 @@ class SettingsFragment : BasePreferenceFragment() { .setNegativeButton(R.string.close, null) .show() } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index f70dc291f..edd1cff66 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -3,6 +3,7 @@ package com.topjohnwu.magisk.utils import android.content.Context import android.widget.Toast import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.view.Notifications @@ -46,52 +47,38 @@ object PatchAPK { } private fun findAndPatch(xml: ByteArray, from: String, to: String): Boolean { - if (from.length != to.length) + if (to.length > from.length) return false val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer() val offList = mutableListOf() var i = 0 - while (i < buf.length - from.length) { - var match = true - for (j in 0 until from.length) { + loop@ while (i < buf.length - from.length) { + for (j in from.indices) { if (buf.get(i + j) != from[j]) { - match = false - break + ++i + continue@loop } } - if (match) { - offList.add(i) - i += from.length - } - ++i + offList.add(i) + i += from.length } if (offList.isEmpty()) return false + + val toBuf = to.toCharArray().copyOf(from.length) for (off in offList) { buf.position(off) - buf.put(to) + buf.put(toBuf) } return true } - private fun findAndPatch(xml: ByteArray, a: Int, b: Int): Boolean { - val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer() - val len = xml.size / 4 - for (i in 0 until len) { - if (buf.get(i) == a) { - buf.put(i, b) - return true - } - } - return false - } - - private fun patchAndHide(context: Context): Boolean { + private fun patchAndHide(context: Context, label: String): Boolean { // Generate a new app with random package name val repack = File(context.filesDir, "patched.apk") val pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length) - if (!patch(context.packageCodePath, repack.path, pkg)) + if (!patch(context.packageCodePath, repack.path, pkg, label)) return false // Install the application @@ -107,14 +94,15 @@ object PatchAPK { } @JvmStatic - fun patch(apk: String, out: String, pkg: String): Boolean { + @JvmOverloads + fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean { try { val jar = JarMap(apk) val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val xml = jar.getRawData(je) if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) || - !findAndPatch(xml, R.string.app_name, R.string.re_app_name)) + !findAndPatch(xml, "Magisk Manager", label)) return false // Write apk changes @@ -128,11 +116,32 @@ object PatchAPK { return true } - fun hideManager(context: Context) { + fun patch(apk: File, out: File, pkg: String, label: String): Boolean { + try { + // Try using the new APK to patch itself + val loader = DynamicClassLoader(apk) + val cls = loader.loadClass("a.a") + + for (m in cls.declaredMethods) { + val pars = m.parameterTypes + if (pars.size == 4 && pars[0] == String::class.java) { + return m.invoke(null, apk.path, out.path, pkg, label) as Boolean + } + } + throw Exception("No matching method found") + } catch (e: Exception) { + Timber.e(e) + // Fallback to use the current implementation + patch(apk.path, out.path, pkg, label) + } + return false + } + + fun hideManager(context: Context, label: String) { Completable.fromAction { val progress = Notifications.progress(context, context.getString(R.string.hide_manager_title)) Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build()) - if (!patchAndHide(context)) + if (!patchAndHide(context, label)) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG) Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID) }.subscribeK() diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml index 73a5f831e..c42a2dd7f 100644 --- a/shared/src/main/AndroidManifest.xml +++ b/shared/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@