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.
This commit is contained in:
topjohnwu 2019-10-17 04:47:46 -04:00
parent 40eda05a30
commit 78daa2eb62
6 changed files with 50 additions and 49 deletions

View File

@ -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 { *; }

View File

@ -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);
}

View File

@ -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)
}

View File

@ -67,7 +67,8 @@ class SettingsFragment : BasePreferenceFragment() {
val suCategory = findPreference<PreferenceCategory>("superuser")!!
val hideManager = findPreference<Preference>("hide")!!
hideManager.setOnPreferenceClickListener {
PatchAPK.hideManager(requireContext())
// TODO: Add UI to allow user to customize app name
PatchAPK.hideManager(requireContext(), "Manager")
true
}
val restoreManager = findPreference<Preference>("restore")
@ -326,4 +327,4 @@ class SettingsFragment : BasePreferenceFragment() {
.setNegativeButton(R.string.close, null)
.show()
}
}
}

View File

@ -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<Int>()
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()

View File

@ -8,7 +8,7 @@
<application
android:icon="@drawable/ic_launcher"
android:installLocation="internalOnly"
android:label="@string/app_name"
android:label="Magisk Manager"
android:supportsRtl="true">
<activity