From e938e717b05eda93466ea53159a097dab1847e37 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 31 Jan 2020 03:37:39 +0800 Subject: [PATCH] Refactor PatchAPK code --- app/src/main/java/a/a.java | 10 -- .../magisk/core/download/ManagerUpgrade.kt | 2 +- .../com/topjohnwu/magisk/core/utils/Keygen.kt | 21 +--- .../topjohnwu/magisk/core/utils/PatchAPK.kt | 114 +++++++----------- 4 files changed, 48 insertions(+), 99 deletions(-) diff --git a/app/src/main/java/a/a.java b/app/src/main/java/a/a.java index 7537dc297..6cc1df4d4 100644 --- a/app/src/main/java/a/a.java +++ b/app/src/main/java/a/a.java @@ -1,19 +1,9 @@ package a; -import com.topjohnwu.magisk.core.utils.PatchAPK; import com.topjohnwu.signing.BootSigner; public class a { - @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/core/download/ManagerUpgrade.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt index 8a8ba9ecd..b91057638 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt @@ -27,7 +27,7 @@ private fun RemoteFileService.patch(apk: File, id: Int) { .setContentText("") } val patched = File(apk.parent, "patched.apk") - PatchAPK.patch(apk, patched, packageName, applicationInfo.nonLocalizedLabel.toString()) + PatchAPK.patch(apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel) apk.delete() patched.renameTo(apk) } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt index 993d1fc06..3920c76eb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt @@ -1,21 +1,17 @@ package com.topjohnwu.magisk.core.utils +import android.content.Context import android.content.pm.PackageManager import android.util.Base64 import android.util.Base64OutputStream import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.utils.PatchAPK.ALPHANUM -import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.signing.CryptoUtils.readCertificate import com.topjohnwu.signing.CryptoUtils.readPrivateKey -import com.topjohnwu.superuser.internal.InternalUtils import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder -import org.koin.core.context.GlobalContext -import org.koin.core.context.startKoin -import timber.log.Timber import java.io.ByteArrayOutputStream import java.math.BigInteger import java.security.KeyPairGenerator @@ -33,7 +29,7 @@ private interface CertKeyProvider { } @Suppress("DEPRECATION") -class Keygen: CertKeyProvider { +class Keygen(context: Context) : CertKeyProvider { companion object { private const val ALIAS = "magisk" @@ -70,9 +66,6 @@ class Keygen: CertKeyProvider { } init { - // This object could possibly be accessed from an external app - // Get context from reflection into Android's framework - val context = InternalUtils.getContext() val pm = context.packageManager val info = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES) val sig = info.signatures[0] @@ -104,14 +97,6 @@ class Keygen: CertKeyProvider { } private fun init(): KeyStore { - GlobalContext.getOrNull() ?: { - // Invoked externally, do some basic initialization - startKoin { - modules(koinModules) - } - Timber.plant(Timber.DebugTree()) - }() - val raw = Config.keyStoreRaw val ks = KeyStore.getInstance("PKCS12") if (raw.isEmpty()) { @@ -135,7 +120,7 @@ class Keygen: CertKeyProvider { val dname = X500Name("CN=${randomString()}") val builder = JcaX509v3CertificateBuilder(dname, BigInteger(160, Random()), start.time, end.time, dname, kp.public) - val signer = JcaContentSignerBuilder("SHA256WithRSA").build(kp.private) + val signer = JcaContentSignerBuilder("SHA1WithRSA").build(kp.private) val cert = JcaX509CertificateConverter().getCertificate(builder.build(signer)) // Store them into keystore diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/PatchAPK.kt index 4a9ba8f47..cca10a290 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/PatchAPK.kt @@ -3,7 +3,6 @@ package com.topjohnwu.magisk.core.utils import android.content.Context import android.os.Build.VERSION.SDK_INT import android.widget.Toast -import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Const @@ -11,14 +10,13 @@ import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.view.Notifications import com.topjohnwu.magisk.data.network.GithubRawServices -import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.signing.JarMap import com.topjohnwu.signing.SignAPK import com.topjohnwu.superuser.Shell -import io.reactivex.Completable +import io.reactivex.Single import timber.log.Timber import java.io.File import java.io.FileOutputStream @@ -28,12 +26,13 @@ import java.security.SecureRandom object PatchAPK { - private const val LOWERALPHA = "abcdefghijklmnopqrstuvwxyz" - private val UPPERALPHA = LOWERALPHA.toUpperCase() - private val ALPHA = LOWERALPHA + UPPERALPHA + private const val ALPHA = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" private const val DIGITS = "0123456789" - val ALPHANUM = ALPHA + DIGITS - private val ALPHANUMDOTS = "$ALPHANUM............" + const val ALPHANUM = ALPHA + DIGITS + private const val ALPHANUMDOTS = "$ALPHANUM............" + + private const val APP_ID = "com.topjohnwu.magisk" + private const val APP_NAME = "Magisk Manager" private fun genPackageName(prefix: String, length: Int): CharSequence { val builder = StringBuilder(length) @@ -81,18 +80,40 @@ object PatchAPK { return true } + fun patch(apk: String, out: String, pkg: CharSequence, label: CharSequence): Boolean { + try { + val jar = JarMap.open(apk) + val je = jar.getJarEntry(Const.ANDROID_MANIFEST) + val xml = jar.getRawData(je) + + if (!findAndPatch(xml, APP_ID, pkg) || + !findAndPatch(xml, APP_NAME, label)) + return false + + // Write apk changes + jar.getOutputStream(je).write(xml) + val keys = Keygen(get()) + SignAPK.sign(keys.cert, keys.key, jar, FileOutputStream(out).buffered()) + } catch (e: Exception) { + Timber.e(e) + return false + } + + return true + } + private fun patchAndHide(context: Context, label: String): Boolean { - val src = if (!isRunningAsStub && SDK_INT >= 28 && - Info.env.magiskVersionCode >= Const.Version.PROVIDER_CONNECT) { - // If not running as stub, and we are compatible with stub, use stub + val dlStub = !isRunningAsStub && SDK_INT >= 28 && + Info.env.magiskVersionCode >= Const.Version.PROVIDER_CONNECT + val src = if (dlStub) { val stub = File(context.cacheDir, "stub.apk") val svc = get() - runCatching { + try { svc.fetchFile(Info.remote.stub.link).blockingGet().byteStream().use { it.writeTo(stub) } - }.onFailure { - Timber.e(it) + } catch (e: Exception) { + Timber.e(e) return false } stub.path @@ -102,7 +123,7 @@ object PatchAPK { // Generate a new random package name and signature val repack = File(context.cacheDir, "patched.apk") - val pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length) + val pkg = genPackageName("com.", APP_ID.length) Config.keyStoreRaw = "" if (!patch(src, repack.path, pkg, label)) @@ -115,67 +136,20 @@ object PatchAPK { Config.suManager = pkg.toString() Config.export() - Shell.su("pm uninstall ${BuildConfig.APPLICATION_ID}").submit() + Shell.su("pm uninstall $APP_ID").submit() return true } - @JvmStatic - @JvmOverloads - fun patch(apk: String, out: String, pkg: CharSequence, label: String = "Manager"): Boolean { - try { - val jar = JarMap.open(apk) - val je = jar.getJarEntry(Const.ANDROID_MANIFEST) - val xml = jar.getRawData(je) - - if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) || - !findAndPatch(xml, "Magisk Manager", label)) - return false - - // Write apk changes - jar.getOutputStream(je).write(xml) - val keys = Keygen() - SignAPK.sign(keys.cert, keys.key, jar, FileOutputStream(out).buffered()) - } catch (e: Exception) { - Timber.e(e) - return false - } - - return true - } - - fun patch(apk: File, out: File, pkg: CharSequence, label: String): Boolean { - try { - if (apk.length() < 1 shl 18) { - // APK is smaller than 256K, must be stub - return patch(apk.path, out.path, pkg, label) - } - - // 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 - return patch(apk.path, out.path, pkg, label) - } - } - 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, label)) + val progress = Notifications.progress(context, context.getString(R.string.hide_manager_title)) + Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build()) + Single.fromCallable { + patchAndHide(context, label) + }.subscribeK { + if (!it) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG) Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID) - }.subscribeK() + } } }