mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-02 20:41:51 +00:00
Open source fully obfuscated stub
This commit is contained in:
1
stub/.gitignore
vendored
1
stub/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/build
|
||||
/src/main/AndroidManifest.xml
|
||||
|
||||
@@ -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<ParanoidExtension>("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("<manifest package=\"com.topjohnwu.magisk\"/>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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("<manifest package=\"com.topjohnwu.magisk\"/>")
|
||||
}
|
||||
|
||||
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"))
|
||||
}
|
||||
|
||||
4
stub/proguard-rules.pro
vendored
4
stub/proguard-rules.pro
vendored
@@ -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
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.topjohnwu.magisk">
|
||||
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
|
||||
<application
|
||||
android:name="a.Q"
|
||||
android:appComponentFactory="a.z"
|
||||
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon,UnusedAttribute">
|
||||
|
||||
<!-- Splash -->
|
||||
<activity
|
||||
android:name="f.u7"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Main -->
|
||||
<activity android:name="xt.R" />
|
||||
|
||||
<!-- Superuser -->
|
||||
<activity
|
||||
android:name="lt5.a"
|
||||
android:directBootAware="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false"
|
||||
tools:ignore="AppLinkUrlError">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Receiver -->
|
||||
<receiver
|
||||
android:name="yy.E"
|
||||
android:directBootAware="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||
<action android:name="android.intent.action.UID_REMOVED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- DownloadService -->
|
||||
<service android:name="d.s" />
|
||||
|
||||
<!-- FileProvider -->
|
||||
<provider
|
||||
android:name="fxQ.lk"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:directBootAware="true"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true" />
|
||||
|
||||
<!-- WorkManager -->
|
||||
<service
|
||||
android:name="w.d"
|
||||
android:directBootAware="false"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<!-- Hardcode GMS version -->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="12451000" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,5 +0,0 @@
|
||||
package a;
|
||||
|
||||
import com.topjohnwu.magisk.DelegateApplication;
|
||||
|
||||
public class Q extends DelegateApplication {}
|
||||
@@ -1,5 +0,0 @@
|
||||
package a;
|
||||
|
||||
import com.topjohnwu.magisk.DelegateComponentFactory;
|
||||
|
||||
public class z extends DelegateComponentFactory {}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String, String> map = new HashMap<>();
|
||||
public static final Map<String, Class<?>> internalMap = new HashMap<>();
|
||||
public static final Map<String, String> 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<String, String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
20
stub/template/AndroidManifest.xml
Normal file
20
stub/template/AndroidManifest.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.topjohnwu.magisk">
|
||||
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
|
||||
<application
|
||||
android:appComponentFactory="%s"
|
||||
android:name="%s"
|
||||
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon,UnusedAttribute">
|
||||
|
||||
%s
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
26
stub/template/Mapping.java
Normal file
26
stub/template/Mapping.java
Normal file
@@ -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<String, String> map = new HashMap<>();
|
||||
public static final Map<String, Class<?>> internalMap = new HashMap<>();
|
||||
public static final Map<String, String> inverseMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
%s
|
||||
for (Map.Entry<String, String> 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user