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:
parent
40eda05a30
commit
78daa2eb62
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
@ -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 { *; }
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
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()
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user