From 6d27eb7f64324e137de6b36842ac79bc94951974 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 31 Dec 2018 15:53:24 +0800 Subject: [PATCH] Dynamic load updated APK for patching Magisk Manager sometimes updates the code for patching the APK due to several changes. When an old manager tries to patch an updated APK using its internal methods, it is sometimes incomplete, or simply won't work at all. This commit exposes an API that can be dynamically loaded from an old app to invoke the updated patchAPK method from the downloaded new APK. --- app/src/full/java/a/a.java | 5 ++++- .../topjohnwu/magisk/utils/DownloadApp.java | 14 +++++++++++--- .../com/topjohnwu/magisk/utils/PatchAPK.java | 19 +++++++------------ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/full/java/a/a.java b/app/src/full/java/a/a.java index e2481e07e..b97ad15e2 100644 --- a/app/src/full/java/a/a.java +++ b/app/src/full/java/a/a.java @@ -1,10 +1,13 @@ package a; import com.topjohnwu.core.utils.BootSigner; +import com.topjohnwu.magisk.utils.PatchAPK; import androidx.annotation.Keep; @Keep public class a extends BootSigner { - /* stub */ + public static boolean patchAPK(String in, String out, String pkg) { + return PatchAPK.patch(in, out, pkg); + } } diff --git a/app/src/full/java/com/topjohnwu/magisk/utils/DownloadApp.java b/app/src/full/java/com/topjohnwu/magisk/utils/DownloadApp.java index f08090f30..9436f6897 100644 --- a/app/src/full/java/com/topjohnwu/magisk/utils/DownloadApp.java +++ b/app/src/full/java/com/topjohnwu/magisk/utils/DownloadApp.java @@ -19,6 +19,8 @@ import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; +import dalvik.system.DexClassLoader; + public class DownloadApp { public static void upgrade(String name) { @@ -72,10 +74,16 @@ public class DownloadApp { progress.update(); patched = new File(apk.getParent(), "patched.apk"); try { - JarMap jarMap = PatchAPK.patch(apk.getPath(), app.getPackageName()); - SignAPK.sign(jarMap, new BufferedOutputStream(new FileOutputStream(patched))); + // Try using the new APK to patch itself + ClassLoader loader = new DexClassLoader(apk.getPath(), + apk.getParent(), null, ClassLoader.getSystemClassLoader()); + loader.loadClass("a.a") + .getMethod("patchAPK", String.class, String.class, String.class) + .invoke(null, apk.getPath(), patched.getPath(), app.getPackageName()); } catch (Exception e) { - return; + e.printStackTrace(); + // Fallback to use the current implementation + PatchAPK.patch(apk.getPath(), patched.getPath(), app.getPackageName()); } } APKInstall.install(app, patched); diff --git a/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java b/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java index c8bd1d32c..17ca8d9b0 100644 --- a/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java +++ b/app/src/full/java/com/topjohnwu/magisk/utils/PatchAPK.java @@ -102,14 +102,8 @@ public class PatchAPK { File repack = new File(app.getFilesDir(), "patched.apk"); String pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length()); - try { - JarMap apk; - if ((apk = patch(app.getPackageCodePath(), pkg)) == null) - return false; - SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(repack))); - } catch (Exception e) { + if (!patch(app.getPackageCodePath(), repack.getPath(), pkg)) return false; - } // Install the application repack.setReadable(true, false); @@ -123,23 +117,24 @@ public class PatchAPK { return true; } - public static JarMap patch(String apk, String pkg) { + public static boolean patch(String in, String out, String pkg) { try { - JarMap jar = new JarMap(apk); + JarMap jar = new JarMap(in); JarEntry je = jar.getJarEntry(Const.ANDROID_MANIFEST); byte xml[] = jar.getRawData(je); if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) || !findAndPatch(xml, R.string.app_name, R.string.re_app_name)) - return null; + return false; // Write in changes jar.getOutputStream(je).write(xml); - return jar; + SignAPK.sign(jar, new BufferedOutputStream(new FileOutputStream(out))); } catch (Exception e) { e.printStackTrace(); - return null; + return false; } + return true; } public static void hideManager() {