diff --git a/.gitignore b/.gitignore
index 83911abb4..dcc31361b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ out
*.apk
/config.prop
/update.sh
+/dict.txt
# Built binaries
native/out
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 8ae2aab1d..742804a21 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,5 +1,4 @@
import org.apache.tools.ant.filters.FixCrLfFilter
-import java.io.PrintStream
plugins {
id("com.android.application")
@@ -143,32 +142,11 @@ android.applicationVariants.all {
val outSrcDir = File(buildDir, "generated/source/keydata/$name")
val outSrc = File(outSrcDir, "com/topjohnwu/magisk/signing/KeyData.java")
- fun PrintStream.newField(name: String, file: File) {
- println("public static byte[] $name() {")
- print("byte[] buf = {")
- val bytes = file.readBytes()
- print(bytes.joinToString(",") { "(byte)(${it.toInt() and 0xff})" })
- println("};")
- println("return buf;")
- println("}")
- }
-
val genSrcTask = tasks.register("generate${name.capitalize()}KeyData") {
inputs.dir(keysDir)
outputs.file(outSrc)
doLast {
- outSrc.parentFile.mkdirs()
- PrintStream(outSrc).use {
- it.println("package com.topjohnwu.magisk.signing;")
- it.println("public final class KeyData {")
-
- it.newField("testCert", File(keysDir, "testkey.x509.pem"))
- it.newField("testKey", File(keysDir, "testkey.pk8"))
- it.newField("verityCert", File(keysDir, "verity.x509.pem"))
- it.newField("verityKey", File(keysDir, "verity.pk8"))
-
- it.println("}")
- }
+ genKeyData(keysDir, outSrc)
}
}
registerJavaGeneratingTask(genSrcTask, outSrcDir)
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index df3605203..ac99c2102 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -49,6 +49,10 @@
-repackageclasses 'a'
-allowaccessmodification
+-obfuscationdictionary ../dict.txt
+-classobfuscationdictionary ../dict.txt
+-packageobfuscationdictionary ../dict.txt
+
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
diff --git a/app/shared/build.gradle.kts b/app/shared/build.gradle.kts
index 7f90192bd..ad8a82bc1 100644
--- a/app/shared/build.gradle.kts
+++ b/app/shared/build.gradle.kts
@@ -8,3 +8,7 @@ android {
consumerProguardFiles("proguard-rules.pro")
}
}
+
+dependencies {
+ api("io.michaelrocks:paranoid-core:0.3.5")
+}
diff --git a/app/shared/src/main/AndroidManifest.xml b/app/shared/src/main/AndroidManifest.xml
index 5423da11b..220750909 100644
--- a/app/shared/src/main/AndroidManifest.xml
+++ b/app/shared/src/main/AndroidManifest.xml
@@ -24,4 +24,9 @@
android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute" />
+
+
+
diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java b/app/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java
index c5daa0398..93a623a37 100644
--- a/app/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java
+++ b/app/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java
@@ -1,5 +1,7 @@
package com.topjohnwu.magisk;
+import static android.os.Build.VERSION.SDK_INT;
+
import android.content.Context;
import android.content.res.AssetManager;
@@ -7,8 +9,9 @@ import java.io.File;
import java.lang.reflect.Method;
import java.util.Map;
-import static android.os.Build.VERSION.SDK_INT;
+import io.michaelrocks.paranoid.Obfuscate;
+@Obfuscate
public class DynAPK {
// Indices of the object array
diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java b/app/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java
index f4261dd95..bafe479ad 100644
--- a/app/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java
+++ b/app/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java
@@ -19,9 +19,12 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import io.michaelrocks.paranoid.Obfuscate;
+
/**
* Modified from androidx.core.content.FileProvider
*/
+@Obfuscate
public class FileProvider extends ContentProvider {
private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/ProviderInstaller.java b/app/shared/src/main/java/com/topjohnwu/magisk/ProviderInstaller.java
index 56ed00232..c969a85ba 100644
--- a/app/shared/src/main/java/com/topjohnwu/magisk/ProviderInstaller.java
+++ b/app/shared/src/main/java/com/topjohnwu/magisk/ProviderInstaller.java
@@ -2,6 +2,9 @@ package com.topjohnwu.magisk;
import android.content.Context;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class ProviderInstaller {
public static boolean install(Context context) {
diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java b/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
index 22a87e402..d76b3a7e8 100644
--- a/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
+++ b/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
@@ -12,6 +12,9 @@ import com.topjohnwu.magisk.FileProvider;
import java.io.File;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class APKInstall {
public static Intent installIntent(Context c, File apk) {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8eb5b28e0..0d621bda2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -71,11 +71,6 @@
android:exported="false"
android:grantUriPermissions="true" />
-
-
-
{
override fun apply(project: Project) = project.applyPlugin()
private fun Project.applyPlugin() {
+ initRandom(rootProject.file("dict.txt"))
props.clear()
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
val configPath: String? by this
diff --git a/buildSrc/src/main/java/Codegen.kt b/buildSrc/src/main/java/Codegen.kt
new file mode 100644
index 000000000..d56043acf
--- /dev/null
+++ b/buildSrc/src/main/java/Codegen.kt
@@ -0,0 +1,286 @@
+
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileInputStream
+import java.io.PrintStream
+import java.security.SecureRandom
+import java.util.*
+import java.util.zip.GZIPOutputStream
+import javax.crypto.Cipher
+import javax.crypto.CipherOutputStream
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+// Set non-zero value here to fix the random seed to create reproducible builds
+const val RAND_SEED = 0
+private lateinit var RANDOM: Random
+
+private val c1 = mutableListOf()
+private val c2 = mutableListOf()
+private val c3 = mutableListOf()
+
+fun initRandom(dict: File) {
+ RANDOM = if (RAND_SEED != 0) Random(RAND_SEED.toLong()) else SecureRandom()
+ c1.clear()
+ c2.clear()
+ c3.clear()
+ for (a in chain('a'..'z', 'A'..'Z')) {
+ if (a != 'a' && a != 'A') {
+ c1.add("$a")
+ }
+ for (b in chain('a'..'z', 'A'..'Z', '0'..'9')) {
+ c2.add("$a$b")
+ for (c in chain('a'..'z', 'A'..'Z', '0'..'9')) {
+ c3.add("$a$b$c")
+ }
+ }
+ }
+ c1.shuffle(RANDOM)
+ c2.shuffle(RANDOM)
+ c3.shuffle(RANDOM)
+ PrintStream(dict).use {
+ for (c in chain(c1, c2, c3)) {
+ it.println(c)
+ }
+ }
+}
+
+private fun chain(vararg iters: Iterable) = sequence {
+ iters.forEach { it.forEach { v -> yield(v) } }
+}
+
+private fun PrintStream.byteField(name: String, bytes: ByteArray) {
+ println("public static byte[] $name() {")
+ print("byte[] buf = {")
+ print(bytes.joinToString(",") { "(byte)(${it.toInt() and 0xff})" })
+ println("};")
+ println("return buf;")
+ println("}")
+}
+
+fun genKeyData(keysDir: File, outSrc: File) {
+ outSrc.parentFile.mkdirs()
+ PrintStream(outSrc).use {
+ it.println("package com.topjohnwu.magisk.signing;")
+ it.println("public final class KeyData {")
+
+ it.byteField("testCert", File(keysDir, "testkey.x509.pem").readBytes())
+ it.byteField("testKey", File(keysDir, "testkey.pk8").readBytes())
+ it.byteField("verityCert", File(keysDir, "verity.x509.pem").readBytes())
+ it.byteField("verityKey", File(keysDir, "verity.pk8").readBytes())
+
+ it.println("}")
+ }
+}
+
+fun genStubManifest(srcDir: File, outDir: File): String {
+ class Component(
+ val real: String,
+ val stub: String,
+ val xml: String
+ )
+
+ outDir.deleteRecursively()
+
+ val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
+ mainPkgDir.mkdirs()
+
+ fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
+
+ val cmpList = mutableListOf()
+
+ cmpList.add(Component(
+ "androidx.core.app.CoreComponentFactory",
+ "DelegateComponentFactory",
+ ""
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.core.App",
+ "DelegateApplication",
+ ""
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.core.Provider",
+ "FileProvider",
+ """
+ |""".ind(2)
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.core.Receiver",
+ "dummy.DummyReceiver",
+ """
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |""".ind(2)
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.core.SplashActivity",
+ "DownloadActivity",
+ """
+ |
+ |
+ |
+ |
+ |
+ |""".ind(2)
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.ui.MainActivity",
+ "",
+ """|""".ind(2)
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.ui.surequest.SuRequestActivity",
+ "",
+ """
+ |
+ |
+ |
+ |
+ |
+ |""".ind(2)
+ ))
+
+ cmpList.add(Component(
+ "com.topjohnwu.magisk.core.download.DownloadService",
+ "",
+ """|""".ind(2)
+ ))
+
+ cmpList.add(Component(
+ "androidx.work.impl.background.systemjob.SystemJobService",
+ "",
+ """
+ |""".ind(2)
+ ))
+
+ val names = mutableListOf()
+ names.addAll(c1)
+ names.addAll(c2.subList(0, 10))
+ names.addAll(c3.subList(0, 10))
+ names.shuffle(RANDOM)
+
+ var idx = 0
+ fun genCmpName(): String {
+ val name = "${names[idx++]}.${names[idx++]}"
+ return name[0].toLowerCase() + name.substring(1)
+ }
+
+ fun genClass(clzName: String, type: String) {
+ val (pkg, name) = clzName.split('.')
+ PrintStream(File(mainPkgDir, "$name.java")).use {
+ it.println("package $pkg;")
+ it.println("public class $name extends com.topjohnwu.magisk.$type {}")
+ }
+ }
+
+ val cmps = mutableListOf()
+ val usedNames = mutableListOf()
+ val maps = StringBuilder()
+
+ for (gen in cmpList) {
+ val name = genCmpName()
+ usedNames.add(name)
+ maps.append("|map.put(\"$name\", \"${gen.real}\");".ind(2))
+ maps.append('\n')
+ if (gen.stub.isNotEmpty()) {
+ if (gen.stub != "DelegateComponentFactory") {
+ maps.append("|internalMap.put(\"$name\", com.topjohnwu.magisk.${gen.stub}.class);".ind(2))
+ maps.append('\n')
+ }
+ if (gen.stub.startsWith("Delegate")) {
+ genClass(name, gen.stub)
+ }
+ }
+ if (gen.xml.isNotEmpty()) {
+ cmps.add(gen.xml.format(name))
+ }
+ }
+
+ // Shuffle the order of the components
+ cmps.shuffle(RANDOM)
+ val xml = File(srcDir, "AndroidManifest.xml").readText()
+ val genXml = xml.format(usedNames[0], usedNames[1], cmps.joinToString("\n\n"))
+
+ // Write mapping information to code
+ val mapping = File(srcDir, "Mapping.java").readText().format(maps)
+ PrintStream(File(mainPkgDir, "Mapping.java")).use {
+ it.print(mapping)
+ }
+
+ return genXml
+}
+
+fun genEncryptedResources(res: File, outDir: File) {
+ val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
+ // Rename R.java
+ val r = File(mainPkgDir, "R.java").let {
+ val txt = it.readText()
+ it.delete()
+ txt
+ }
+ File(mainPkgDir, "A.java").writeText(r.replace("class R", "class A"))
+
+ // Generate iv and key
+ val iv = ByteArray(16)
+ val key = ByteArray(32)
+ RANDOM.nextBytes(iv)
+ RANDOM.nextBytes(key)
+
+ val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
+ cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
+ val bos = ByteArrayOutputStream()
+
+ FileInputStream(res).use {
+ // First compress, then encrypt
+ GZIPOutputStream(CipherOutputStream(bos, cipher)).use { os ->
+ it.transferTo(os)
+ }
+ }
+
+ PrintStream(File(mainPkgDir, "Bytes.java")).use {
+ it.println("package com.topjohnwu.magisk;")
+ it.println("public final class Bytes {")
+
+ it.byteField("key", key)
+ it.byteField("iv", iv)
+ it.byteField("res", bos.toByteArray())
+
+ it.println("}")
+ }
+}
diff --git a/stub/.gitignore b/stub/.gitignore
index 796b96d1c..fa72e45ca 100644
--- a/stub/.gitignore
+++ b/stub/.gitignore
@@ -1 +1,2 @@
/build
+/src/main/AndroidManifest.xml
diff --git a/stub/build.gradle.kts b/stub/build.gradle.kts
index fadb91d3f..4e0137b5f 100644
--- a/stub/build.gradle.kts
+++ b/stub/build.gradle.kts
@@ -1,7 +1,30 @@
+
+import io.michaelrocks.paranoid.plugin.ParanoidExtension
+import org.gradle.internal.os.OperatingSystem
+import java.io.OutputStream
+import java.io.PrintStream
+import java.nio.file.Paths
+
plugins {
id("com.android.application")
}
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath("io.michaelrocks:paranoid-gradle-plugin:0.3.5")
+ }
+}
+
+apply(plugin = "io.michaelrocks.paranoid")
+
+extensions.configure("paranoid") {
+ obfuscationSeed = if (RAND_SEED != 0) RAND_SEED else null
+ includeSubprojects = true
+}
+
android {
val canary = !Config.version.contains(".")
@@ -11,7 +34,7 @@ android {
defaultConfig {
applicationId = "com.topjohnwu.magisk"
versionCode = 1
- versionName = Config.version
+ versionName = "1.0"
buildConfigField("int", "STUB_VERSION", Config.stubVersion)
buildConfigField("String", "APK_URL", url?.let { "\"$it\"" } ?: "null" )
}
@@ -24,16 +47,98 @@ android {
}
}
- androidResources {
- additionalParameters("--package-id", "0x80")
- }
-
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
}
+// Make sure we have a working manifest while building
+val ensureManifest by tasks.registering {
+ val manifest = file("src/main/AndroidManifest.xml")
+ if (!manifest.exists()) {
+ PrintStream(manifest).use {
+ it.println("")
+ }
+ }
+}
+
+tasks["preBuild"]?.dependsOn(ensureManifest)
+
+android.applicationVariants.all {
+ val manifest = file("src/main/AndroidManifest.xml")
+ val outSrcDir = File(buildDir, "generated/source/obfuscate/$name")
+ val templateDir = file("template")
+ val resDir = file("res")
+
+ val androidJar = Paths.get(android.sdkDirectory.path, "platforms",
+ android.compileSdkVersion, "android.jar")
+
+ val aaptCommand = if (OperatingSystem.current().isWindows) "aapt2.exe" else "aapt2"
+ val aapt = Paths.get(android.sdkDirectory.path,
+ "build-tools", android.buildToolsVersion, aaptCommand)
+
+ val dummy = object : OutputStream() {
+ override fun write(b: Int) {}
+ override fun write(bytes: ByteArray, off: Int, len: Int) {}
+ }
+
+ val genSrcTask = tasks.register("generate${name.capitalize()}ObfuscatedSources") {
+ doLast {
+ val xml = genStubManifest(templateDir, outSrcDir)
+ PrintStream(manifest).use {
+ it.print(xml)
+ }
+
+ val compileTmp = File.createTempFile("tmp", ".zip")
+ val linkTmp = File.createTempFile("tmp", ".zip")
+ val optTmp = File.createTempFile("tmp", ".zip")
+ val stubXml = File.createTempFile("tmp", ".xml")
+ try {
+ PrintStream(stubXml).use {
+ it.println("")
+ }
+
+ exec {
+ commandLine(aapt, "compile",
+ "-o", compileTmp,
+ "--dir", resDir)
+ standardOutput = dummy
+ errorOutput = dummy
+ }
+
+ exec {
+ commandLine(aapt, "link",
+ "-o", linkTmp,
+ "-I", androidJar,
+ "--min-sdk-version", android.defaultConfig.minSdk,
+ "--target-sdk-version", android.defaultConfig.targetSdk,
+ "--manifest", stubXml,
+ "--java", outSrcDir, compileTmp)
+ standardOutput = dummy
+ errorOutput = dummy
+ }
+
+ exec {
+ commandLine(aapt, "optimize",
+ "-o", optTmp,
+ "--collapse-resource-names", linkTmp)
+ standardOutput = dummy
+ errorOutput = dummy
+ }
+
+ genEncryptedResources(optTmp, outSrcDir)
+ } finally {
+ compileTmp.delete()
+ linkTmp.delete()
+ optTmp.delete()
+ stubXml.delete()
+ }
+ }
+ }
+ registerJavaGeneratingTask(genSrcTask, outSrcDir)
+}
+
dependencies {
implementation(project(":app:shared"))
}
diff --git a/stub/proguard-rules.pro b/stub/proguard-rules.pro
index f352d3fa9..99c641428 100644
--- a/stub/proguard-rules.pro
+++ b/stub/proguard-rules.pro
@@ -20,6 +20,10 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile
+-obfuscationdictionary ../dict.txt
+-classobfuscationdictionary ../dict.txt
+-packageobfuscationdictionary ../dict.txt
+
# Excessive obfuscation
-repackageclasses
-allowaccessmodification
diff --git a/stub/src/main/res/values-ar/strings.xml b/stub/res/values-ar/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ar/strings.xml
rename to stub/res/values-ar/strings.xml
diff --git a/stub/src/main/res/values-az/strings.xml b/stub/res/values-az/strings.xml
similarity index 100%
rename from stub/src/main/res/values-az/strings.xml
rename to stub/res/values-az/strings.xml
diff --git a/stub/src/main/res/values-be/strings.xml b/stub/res/values-be/strings.xml
similarity index 100%
rename from stub/src/main/res/values-be/strings.xml
rename to stub/res/values-be/strings.xml
diff --git a/stub/src/main/res/values-bg/strings.xml b/stub/res/values-bg/strings.xml
similarity index 100%
rename from stub/src/main/res/values-bg/strings.xml
rename to stub/res/values-bg/strings.xml
diff --git a/stub/src/main/res/values-ca/strings.xml b/stub/res/values-ca/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ca/strings.xml
rename to stub/res/values-ca/strings.xml
diff --git a/stub/src/main/res/values-cs/strings.xml b/stub/res/values-cs/strings.xml
similarity index 100%
rename from stub/src/main/res/values-cs/strings.xml
rename to stub/res/values-cs/strings.xml
diff --git a/stub/src/main/res/values-de/strings.xml b/stub/res/values-de/strings.xml
similarity index 100%
rename from stub/src/main/res/values-de/strings.xml
rename to stub/res/values-de/strings.xml
diff --git a/stub/src/main/res/values-el/strings.xml b/stub/res/values-el/strings.xml
similarity index 100%
rename from stub/src/main/res/values-el/strings.xml
rename to stub/res/values-el/strings.xml
diff --git a/stub/src/main/res/values-es/strings.xml b/stub/res/values-es/strings.xml
similarity index 100%
rename from stub/src/main/res/values-es/strings.xml
rename to stub/res/values-es/strings.xml
diff --git a/stub/src/main/res/values-et/strings.xml b/stub/res/values-et/strings.xml
similarity index 100%
rename from stub/src/main/res/values-et/strings.xml
rename to stub/res/values-et/strings.xml
diff --git a/stub/src/main/res/values-fa/strings.xml b/stub/res/values-fa/strings.xml
similarity index 100%
rename from stub/src/main/res/values-fa/strings.xml
rename to stub/res/values-fa/strings.xml
diff --git a/stub/src/main/res/values-fr/strings.xml b/stub/res/values-fr/strings.xml
similarity index 100%
rename from stub/src/main/res/values-fr/strings.xml
rename to stub/res/values-fr/strings.xml
diff --git a/stub/src/main/res/values-hi/strings.xml b/stub/res/values-hi/strings.xml
similarity index 100%
rename from stub/src/main/res/values-hi/strings.xml
rename to stub/res/values-hi/strings.xml
diff --git a/stub/src/main/res/values-hr/strings.xml b/stub/res/values-hr/strings.xml
similarity index 100%
rename from stub/src/main/res/values-hr/strings.xml
rename to stub/res/values-hr/strings.xml
diff --git a/stub/src/main/res/values-in/strings.xml b/stub/res/values-in/strings.xml
similarity index 100%
rename from stub/src/main/res/values-in/strings.xml
rename to stub/res/values-in/strings.xml
diff --git a/stub/src/main/res/values-it/strings.xml b/stub/res/values-it/strings.xml
similarity index 100%
rename from stub/src/main/res/values-it/strings.xml
rename to stub/res/values-it/strings.xml
diff --git a/stub/src/main/res/values-iw/strings.xml b/stub/res/values-iw/strings.xml
similarity index 100%
rename from stub/src/main/res/values-iw/strings.xml
rename to stub/res/values-iw/strings.xml
diff --git a/stub/src/main/res/values-ja/strings.xml b/stub/res/values-ja/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ja/strings.xml
rename to stub/res/values-ja/strings.xml
diff --git a/stub/src/main/res/values-ka/strings.xml b/stub/res/values-ka/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ka/strings.xml
rename to stub/res/values-ka/strings.xml
diff --git a/stub/src/main/res/values-ko/strings.xml b/stub/res/values-ko/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ko/strings.xml
rename to stub/res/values-ko/strings.xml
diff --git a/stub/src/main/res/values-lt/strings.xml b/stub/res/values-lt/strings.xml
similarity index 100%
rename from stub/src/main/res/values-lt/strings.xml
rename to stub/res/values-lt/strings.xml
diff --git a/stub/src/main/res/values-mk/strings.xml b/stub/res/values-mk/strings.xml
similarity index 100%
rename from stub/src/main/res/values-mk/strings.xml
rename to stub/res/values-mk/strings.xml
diff --git a/stub/src/main/res/values-nb/strings.xml b/stub/res/values-nb/strings.xml
similarity index 100%
rename from stub/src/main/res/values-nb/strings.xml
rename to stub/res/values-nb/strings.xml
diff --git a/stub/src/main/res/values-nl/strings.xml b/stub/res/values-nl/strings.xml
similarity index 100%
rename from stub/src/main/res/values-nl/strings.xml
rename to stub/res/values-nl/strings.xml
diff --git a/stub/src/main/res/values-pa/strings.xml b/stub/res/values-pa/strings.xml
similarity index 100%
rename from stub/src/main/res/values-pa/strings.xml
rename to stub/res/values-pa/strings.xml
diff --git a/stub/src/main/res/values-pl/strings.xml b/stub/res/values-pl/strings.xml
similarity index 100%
rename from stub/src/main/res/values-pl/strings.xml
rename to stub/res/values-pl/strings.xml
diff --git a/stub/src/main/res/values-pt-rBR/strings.xml b/stub/res/values-pt-rBR/strings.xml
similarity index 100%
rename from stub/src/main/res/values-pt-rBR/strings.xml
rename to stub/res/values-pt-rBR/strings.xml
diff --git a/stub/src/main/res/values-pt-rPT/strings.xml b/stub/res/values-pt-rPT/strings.xml
similarity index 100%
rename from stub/src/main/res/values-pt-rPT/strings.xml
rename to stub/res/values-pt-rPT/strings.xml
diff --git a/stub/src/main/res/values-ro/strings.xml b/stub/res/values-ro/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ro/strings.xml
rename to stub/res/values-ro/strings.xml
diff --git a/stub/src/main/res/values-ru/strings.xml b/stub/res/values-ru/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ru/strings.xml
rename to stub/res/values-ru/strings.xml
diff --git a/stub/src/main/res/values-sk/strings.xml b/stub/res/values-sk/strings.xml
similarity index 100%
rename from stub/src/main/res/values-sk/strings.xml
rename to stub/res/values-sk/strings.xml
diff --git a/stub/src/main/res/values-sr/strings.xml b/stub/res/values-sr/strings.xml
similarity index 100%
rename from stub/src/main/res/values-sr/strings.xml
rename to stub/res/values-sr/strings.xml
diff --git a/stub/src/main/res/values-sv/strings.xml b/stub/res/values-sv/strings.xml
similarity index 100%
rename from stub/src/main/res/values-sv/strings.xml
rename to stub/res/values-sv/strings.xml
diff --git a/stub/src/main/res/values-ta/strings.xml b/stub/res/values-ta/strings.xml
similarity index 100%
rename from stub/src/main/res/values-ta/strings.xml
rename to stub/res/values-ta/strings.xml
diff --git a/stub/src/main/res/values-th/strings.xml b/stub/res/values-th/strings.xml
similarity index 100%
rename from stub/src/main/res/values-th/strings.xml
rename to stub/res/values-th/strings.xml
diff --git a/stub/src/main/res/values-tr/strings.xml b/stub/res/values-tr/strings.xml
similarity index 100%
rename from stub/src/main/res/values-tr/strings.xml
rename to stub/res/values-tr/strings.xml
diff --git a/stub/src/main/res/values-uk/strings.xml b/stub/res/values-uk/strings.xml
similarity index 100%
rename from stub/src/main/res/values-uk/strings.xml
rename to stub/res/values-uk/strings.xml
diff --git a/stub/src/main/res/values-vi/strings.xml b/stub/res/values-vi/strings.xml
similarity index 100%
rename from stub/src/main/res/values-vi/strings.xml
rename to stub/res/values-vi/strings.xml
diff --git a/stub/src/main/res/values-zh-rCN/strings.xml b/stub/res/values-zh-rCN/strings.xml
similarity index 100%
rename from stub/src/main/res/values-zh-rCN/strings.xml
rename to stub/res/values-zh-rCN/strings.xml
diff --git a/stub/src/main/res/values-zh-rTW/strings.xml b/stub/res/values-zh-rTW/strings.xml
similarity index 100%
rename from stub/src/main/res/values-zh-rTW/strings.xml
rename to stub/res/values-zh-rTW/strings.xml
diff --git a/stub/src/main/res/values/strings.xml b/stub/res/values/strings.xml
similarity index 100%
rename from stub/src/main/res/values/strings.xml
rename to stub/res/values/strings.xml
diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 6a0c9ce31..000000000
--- a/stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/stub/src/main/java/a/Q.java b/stub/src/main/java/a/Q.java
deleted file mode 100644
index 537e6615e..000000000
--- a/stub/src/main/java/a/Q.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package a;
-
-import com.topjohnwu.magisk.DelegateApplication;
-
-public class Q extends DelegateApplication {}
diff --git a/stub/src/main/java/a/z.java b/stub/src/main/java/a/z.java
deleted file mode 100644
index 883e9cebb..000000000
--- a/stub/src/main/java/a/z.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package a;
-
-import com.topjohnwu.magisk.DelegateComponentFactory;
-
-public class z extends DelegateComponentFactory {}
diff --git a/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java b/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java
index f1f9f637a..84d7b794f 100644
--- a/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java
+++ b/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java
@@ -7,6 +7,9 @@ import android.content.res.Configuration;
import java.lang.reflect.Method;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class DelegateApplication extends Application {
static boolean dynLoad = false;
diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
index b19609460..7206b9cee 100644
--- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
+++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
@@ -1,5 +1,14 @@
package com.topjohnwu.magisk;
+import static android.R.string.no;
+import static android.R.string.ok;
+import static android.R.string.yes;
+import static com.topjohnwu.magisk.DelegateApplication.dynLoad;
+import static com.topjohnwu.magisk.A.string.dling;
+import static com.topjohnwu.magisk.A.string.no_internet_msg;
+import static com.topjohnwu.magisk.A.string.relaunch_app;
+import static com.topjohnwu.magisk.A.string.upgrade_msg;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -16,17 +25,22 @@ import com.topjohnwu.magisk.utils.APKInstall;
import org.json.JSONException;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
-import static android.R.string.no;
-import static android.R.string.ok;
-import static android.R.string.yes;
-import static com.topjohnwu.magisk.DelegateApplication.dynLoad;
-import static com.topjohnwu.magisk.R.string.dling;
-import static com.topjohnwu.magisk.R.string.no_internet_msg;
-import static com.topjohnwu.magisk.R.string.relaunch_app;
-import static com.topjohnwu.magisk.R.string.upgrade_msg;
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class DownloadActivity extends Activity {
private static final String APP_NAME = "Magisk";
@@ -41,6 +55,9 @@ public class DownloadActivity extends Activity {
super.onCreate(savedInstanceState);
themed = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault);
+ // Inject resources
+ loadResources();
+
if (Networking.checkNetworkStatus(this)) {
if (apkLink == null) {
fetchCanary();
@@ -111,4 +128,26 @@ public class DownloadActivity extends Activity {
});
}
+ private void loadResources() {
+ File apk = new File(getCacheDir(), "res.apk");
+ try {
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
+ IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
+ cipher.init(Cipher.DECRYPT_MODE, key, iv);
+ InputStream is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher);
+ try (InputStream gzip = new GZIPInputStream(is);
+ OutputStream out = new FileOutputStream(apk)) {
+ byte[] buf = new byte[4096];
+ for (int read; (read = gzip.read(buf)) >= 0;) {
+ out.write(buf, 0, read);
+ }
+ }
+ DynAPK.addAssetPath(getResources().getAssets(), apk.getPath());
+ } catch (Exception e) {
+ // Should not happen
+ e.printStackTrace();
+ }
+ }
+
}
diff --git a/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java b/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
index eaab4d9fb..4d2d68169 100644
--- a/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
+++ b/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
@@ -17,6 +17,9 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class InjectAPK {
static Object componentFactory;
diff --git a/stub/src/main/java/com/topjohnwu/magisk/Mapping.java b/stub/src/main/java/com/topjohnwu/magisk/Mapping.java
deleted file mode 100644
index 1f4e17be3..000000000
--- a/stub/src/main/java/com/topjohnwu/magisk/Mapping.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.topjohnwu.magisk;
-
-import com.topjohnwu.magisk.dummy.DummyReceiver;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * These are just some random class names hardcoded as an example.
- * For the actual release builds, these mappings will be auto generated.
- */
-public class Mapping {
-
- private static final Map map = new HashMap<>();
- public static final Map> internalMap = new HashMap<>();
- public static final Map inverseMap;
-
- static {
- map.put("a.Q", "com.topjohnwu.magisk.core.App");
- map.put("f.u7", "com.topjohnwu.magisk.core.SplashActivity");
- map.put("fxQ.lk", "com.topjohnwu.magisk.core.Provider");
- map.put("yy.E", "com.topjohnwu.magisk.core.Receiver");
- map.put("xt.R", "com.topjohnwu.magisk.ui.MainActivity");
- map.put("lt5.a", "com.topjohnwu.magisk.ui.surequest.SuRequestActivity");
- map.put("d.s", "com.topjohnwu.magisk.core.download.DownloadService");
- map.put("w.d", "androidx.work.impl.background.systemjob.SystemJobService");
-
- internalMap.put("a.Q", DelegateApplication.class);
- internalMap.put("f.u7", DownloadActivity.class);
- internalMap.put("fxQ.lk", FileProvider.class);
- internalMap.put("yy.E", DummyReceiver.class);
-
- inverseMap = new HashMap<>(map.size());
- for (Map.Entry e : map.entrySet()) {
- inverseMap.put(e.getValue(), e.getKey());
- }
- }
-
- public static String get(String name) {
- String n = map.get(name);
- return n != null ? n : name;
- }
-}
diff --git a/stub/src/main/java/com/topjohnwu/magisk/net/Networking.java b/stub/src/main/java/com/topjohnwu/magisk/net/Networking.java
index bc93dc48a..470996ff8 100644
--- a/stub/src/main/java/com/topjohnwu/magisk/net/Networking.java
+++ b/stub/src/main/java/com/topjohnwu/magisk/net/Networking.java
@@ -10,6 +10,9 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class Networking {
private static final int READ_TIMEOUT = 15000;
diff --git a/stub/src/main/java/com/topjohnwu/magisk/net/Request.java b/stub/src/main/java/com/topjohnwu/magisk/net/Request.java
index f0373eb63..4c1c55de7 100644
--- a/stub/src/main/java/com/topjohnwu/magisk/net/Request.java
+++ b/stub/src/main/java/com/topjohnwu/magisk/net/Request.java
@@ -20,6 +20,9 @@ import java.net.HttpURLConnection;
import java.util.Scanner;
import java.util.concurrent.Executor;
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
public class Request implements Closeable {
private HttpURLConnection conn;
private Executor executor = null;
diff --git a/stub/template/AndroidManifest.xml b/stub/template/AndroidManifest.xml
new file mode 100644
index 000000000..952c391a8
--- /dev/null
+++ b/stub/template/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+%s
+
+
+
+
diff --git a/stub/template/Mapping.java b/stub/template/Mapping.java
new file mode 100644
index 000000000..0f6ddcd55
--- /dev/null
+++ b/stub/template/Mapping.java
@@ -0,0 +1,26 @@
+package com.topjohnwu.magisk;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.michaelrocks.paranoid.Obfuscate;
+
+@Obfuscate
+public class Mapping {
+
+ private static final Map map = new HashMap<>();
+ public static final Map> internalMap = new HashMap<>();
+ public static final Map inverseMap = new HashMap<>();
+
+ static {
+%s
+ for (Map.Entry e : map.entrySet()) {
+ inverseMap.put(e.getValue(), e.getKey());
+ }
+ }
+
+ public static String get(String name) {
+ String n = map.get(name);
+ return n != null ? n : name;
+ }
+}