mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-27 12:05:30 +00:00
Fix stub app loading on older Android versions
This commit is contained in:
parent
a3381da7ed
commit
9016e6727d
@ -9,10 +9,8 @@ import dalvik.system.BaseDexClassLoader;
|
|||||||
|
|
||||||
public class DynamicClassLoader extends BaseDexClassLoader {
|
public class DynamicClassLoader extends BaseDexClassLoader {
|
||||||
|
|
||||||
private static final ClassLoader base = Object.class.getClassLoader();
|
|
||||||
|
|
||||||
public DynamicClassLoader(File apk) {
|
public DynamicClassLoader(File apk) {
|
||||||
this(apk, base);
|
this(apk, getSystemClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicClassLoader(File apk, ClassLoader parent) {
|
public DynamicClassLoader(File apk, ClassLoader parent) {
|
||||||
@ -29,7 +27,7 @@ public class DynamicClassLoader extends BaseDexClassLoader {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Then check boot classpath
|
// Then check boot classpath
|
||||||
return base.loadClass(name);
|
return getSystemClassLoader().loadClass(name);
|
||||||
} catch (ClassNotFoundException ignored) {
|
} catch (ClassNotFoundException ignored) {
|
||||||
try {
|
try {
|
||||||
// Next try current dex
|
// Next try current dex
|
||||||
@ -47,7 +45,7 @@ public class DynamicClassLoader extends BaseDexClassLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getResource(String name) {
|
public URL getResource(String name) {
|
||||||
URL resource = base.getResource(name);
|
URL resource = getSystemClassLoader().getResource(name);
|
||||||
if (resource != null)
|
if (resource != null)
|
||||||
return resource;
|
return resource;
|
||||||
resource = findResource(name);
|
resource = findResource(name);
|
||||||
@ -59,7 +57,7 @@ public class DynamicClassLoader extends BaseDexClassLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enumeration<URL> getResources(String name) throws IOException {
|
public Enumeration<URL> getResources(String name) throws IOException {
|
||||||
return new CompoundEnumeration<>(base.getResources(name),
|
return new CompoundEnumeration<>(getSystemClassLoader().getResources(name),
|
||||||
findResources(name), getParent().getResources(name));
|
findResources(name), getParent().getResources(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ package com.topjohnwu.magisk.core
|
|||||||
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.AssetManager
|
import android.content.res.AssetManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
@ -13,16 +14,12 @@ import com.topjohnwu.magisk.R
|
|||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.di.AppContext
|
import com.topjohnwu.magisk.core.di.AppContext
|
||||||
import com.topjohnwu.magisk.core.utils.syncLocale
|
import com.topjohnwu.magisk.core.utils.syncLocale
|
||||||
|
import com.topjohnwu.magisk.ktx.unwrap
|
||||||
|
|
||||||
lateinit var AppApkPath: String
|
lateinit var AppApkPath: String
|
||||||
|
|
||||||
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
||||||
|
|
||||||
fun Context.patch(): Context {
|
|
||||||
resources.patch()
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Resources.patch(): Resources {
|
fun Resources.patch(): Resources {
|
||||||
if (isRunningAsStub)
|
if (isRunningAsStub)
|
||||||
addAssetPath(AppApkPath)
|
addAssetPath(AppApkPath)
|
||||||
@ -30,6 +27,21 @@ fun Resources.patch(): Resources {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.patch(): Context {
|
||||||
|
unwrap().resources.patch()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapping is only necessary for ContextThemeWrapper to support configuration overrides
|
||||||
|
fun Context.wrap(): Context {
|
||||||
|
patch()
|
||||||
|
return object : ContextWrapper(this) {
|
||||||
|
override fun createConfigurationContext(config: Configuration): Context {
|
||||||
|
return super.createConfigurationContext(config).wrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createNewResources(): Resources {
|
fun createNewResources(): Resources {
|
||||||
val asset = AssetManager::class.java.newInstance()
|
val asset = AssetManager::class.java.newInstance()
|
||||||
val config = Configuration(AppContext.resources.configuration)
|
val config = Configuration(AppContext.resources.configuration)
|
||||||
|
@ -5,7 +5,6 @@ import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -18,9 +17,9 @@ import androidx.annotation.WorkerThread
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.core.patch
|
|
||||||
import com.topjohnwu.magisk.core.utils.RequestInstall
|
import com.topjohnwu.magisk.core.utils.RequestInstall
|
||||||
import com.topjohnwu.magisk.core.utils.UninstallPackage
|
import com.topjohnwu.magisk.core.utils.UninstallPackage
|
||||||
|
import com.topjohnwu.magisk.core.wrap
|
||||||
import com.topjohnwu.magisk.ktx.reflectField
|
import com.topjohnwu.magisk.ktx.reflectField
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
@ -56,11 +55,7 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
super.attachBaseContext(base.patch())
|
super.attachBaseContext(base.wrap())
|
||||||
}
|
|
||||||
|
|
||||||
override fun createConfigurationContext(config: Configuration): Context {
|
|
||||||
return super.createConfigurationContext(config).patch()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -12,12 +12,12 @@ import org.gradle.api.tasks.Sync
|
|||||||
import org.gradle.kotlin.dsl.filter
|
import org.gradle.kotlin.dsl.filter
|
||||||
import org.gradle.kotlin.dsl.named
|
import org.gradle.kotlin.dsl.named
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
||||||
import java.io.*
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.Deflater
|
import java.util.zip.*
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import java.util.zip.ZipOutputStream
|
|
||||||
|
|
||||||
private fun Project.androidBase(configure: Action<BaseExtension>) =
|
private fun Project.androidBase(configure: Action<BaseExtension>) =
|
||||||
extensions.configure("android", configure)
|
extensions.configure("android", configure)
|
||||||
@ -219,22 +219,23 @@ fun Project.setupStub() {
|
|||||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||||
}
|
}
|
||||||
|
|
||||||
val buffer = ByteArrayOutputStream(apk.length().toInt())
|
val buffer = ByteArrayOutputStream()
|
||||||
val newApk = ZipOutputStream(FileOutputStream(apk))
|
apkTmp.inputStream().use {
|
||||||
ZipFile(apkTmp).use {
|
object : GZIPOutputStream(buffer) {
|
||||||
newApk.use { new ->
|
init {
|
||||||
new.setLevel(Deflater.BEST_COMPRESSION)
|
def.setLevel(Deflater.BEST_COMPRESSION)
|
||||||
new.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
|
||||||
it.getInputStream(it.getEntry("AndroidManifest.xml")).transferTo(new)
|
|
||||||
new.closeEntry()
|
|
||||||
new.finish()
|
|
||||||
}
|
}
|
||||||
ZipOutputStream(buffer).use { arsc ->
|
}.use { o ->
|
||||||
arsc.setLevel(Deflater.BEST_COMPRESSION)
|
it.transferTo(o)
|
||||||
arsc.putNextEntry(ZipEntry("resources.arsc"))
|
}
|
||||||
it.getInputStream(it.getEntry("resources.arsc")).transferTo(arsc)
|
}
|
||||||
arsc.closeEntry()
|
ZipFile(apkTmp).use { o ->
|
||||||
arsc.finish()
|
ZipOutputStream(apk.outputStream()).use { n ->
|
||||||
|
n.setLevel(Deflater.BEST_COMPRESSION)
|
||||||
|
n.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||||
|
o.getInputStream(o.getEntry("AndroidManifest.xml")).transferTo(n)
|
||||||
|
n.closeEntry()
|
||||||
|
n.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apkTmp.delete()
|
apkTmp.delete()
|
||||||
|
@ -131,7 +131,7 @@ class StubClassLoader extends ClassLoader {
|
|||||||
class DelegateClassLoader extends ClassLoader {
|
class DelegateClassLoader extends ClassLoader {
|
||||||
|
|
||||||
DelegateClassLoader() {
|
DelegateClassLoader() {
|
||||||
super(null);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -34,6 +34,7 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
import javax.crypto.CipherInputStream;
|
||||||
@ -95,6 +96,12 @@ public class DownloadActivity extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
super.finish();
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
private void error(Throwable e) {
|
private void error(Throwable e) {
|
||||||
Log.e(getClass().getSimpleName(), "", e);
|
Log.e(getClass().getSimpleName(), "", e);
|
||||||
finish();
|
finish();
|
||||||
@ -154,7 +161,8 @@ public class DownloadActivity extends Activity {
|
|||||||
SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
|
SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
|
||||||
IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
|
IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
||||||
var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher);
|
var is = new GZIPInputStream(new CipherInputStream(
|
||||||
|
new ByteArrayInputStream(Bytes.res()), cipher));
|
||||||
try (is; out) {
|
try (is; out) {
|
||||||
APKInstall.transfer(is, out);
|
APKInstall.transfer(is, out);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user