From ab1a213161f90ec7ac604df47434201170b92b9a Mon Sep 17 00:00:00 2001 From: 327135569 Date: Sat, 24 Apr 2021 11:29:36 +0800 Subject: [PATCH] add loader's loader --- appstub/.gitignore | 1 + appstub/build.gradle | 62 +++++++++++ appstub/consumer-rules.pro | 0 appstub/proguard-rules.pro | 21 ++++ appstub/src/main/AndroidManifest.xml | 5 + .../lspatch/appstub/LSPApplicationSub.java | 102 ++++++++++++++++++ settings.gradle | 1 + 7 files changed, 192 insertions(+) create mode 100644 appstub/.gitignore create mode 100644 appstub/build.gradle create mode 100644 appstub/consumer-rules.pro create mode 100644 appstub/proguard-rules.pro create mode 100644 appstub/src/main/AndroidManifest.xml create mode 100644 appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationSub.java diff --git a/appstub/.gitignore b/appstub/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/appstub/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/appstub/build.gradle b/appstub/build.gradle new file mode 100644 index 0000000..f5228c0 --- /dev/null +++ b/appstub/build.gradle @@ -0,0 +1,62 @@ +apply plugin: 'com.android.application' + +android { + compileSdk 30 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdk rootProject.ext.androidMinSdkVersion + targetSdk rootProject.ext.androidTargetSdkVersion + versionCode 1 + versionName "1.0" + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + applicationVariants.all { variant -> + def buildType = variant.name.capitalize() + def variantLowered = variant.name.toLowerCase() + + variant.outputs.all { + outputFileName = "${variant.getFlavorName()}-${variant.versionName}.apk" + } + + task "copyDex$buildType"(type: Copy) { + dependsOn("assemble$buildType") + def dexFilePath = "$buildDir/intermediates/dex/${variantLowered}/mergeDex${buildType}/classes.dex" + from dexFilePath + rename "(.*).dex", "classes-${version_name}.dex" + into "$rootProject.projectDir/out/list-dex" + } + + task "copySo$buildType"(type: Copy) { + dependsOn("assemble$buildType") + from "$buildDir/intermediates/merged_native_libs/${variantLowered}/out/lib" + into "$rootProject.projectDir/out/list-so" + } + + task "copy$buildType"() { + dependsOn("copySo$buildType") + dependsOn("copyDex$buildType") + + doLast { + System.out.println("Dex and so files has been copy to ${rootProject.projectDir}${File.separator}out") + } + } + } +} + +dependencies { + implementation project(':share') + compileOnly project(":hiddenapi-stubs") +} \ No newline at end of file diff --git a/appstub/consumer-rules.pro b/appstub/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/appstub/proguard-rules.pro b/appstub/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/appstub/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/appstub/src/main/AndroidManifest.xml b/appstub/src/main/AndroidManifest.xml new file mode 100644 index 0000000..53b1155 --- /dev/null +++ b/appstub/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationSub.java b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationSub.java new file mode 100644 index 0000000..9cb096a --- /dev/null +++ b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationSub.java @@ -0,0 +1,102 @@ +package org.lsposed.lspatch.appstub; + +import android.app.ActivityThread; +import android.app.Application; +import android.content.Context; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; + +import dalvik.system.InMemoryDexClassLoader; + +public class LSPApplicationSub extends Application { + final static String TAG = LSPApplicationSub.class.getSimpleName(); + + static Object realLSPApplication = null; + + static { + // load real lsp loader from asset + Context context = createAppContext(); + if (context == null) { + Log.e(TAG, "create context err"); + } + else { + try { + InputStream inputStream = context.getAssets().open("lsploader.dex"); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[16384]; + + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + InMemoryDexClassLoader inMemoryDexClassLoader = new InMemoryDexClassLoader(ByteBuffer.wrap(buffer.toByteArray()), LSPApplicationSub.class.getClassLoader()); + Class lspa = inMemoryDexClassLoader.loadClass("org.lsposed.lspatch.appstub.LSPApplication"); + realLSPApplication = lspa.newInstance(); + } + catch (Exception e) { + throw new IllegalStateException("wtf", e); + } + } + } + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + + if (realLSPApplication != null) { + try { + realLSPApplication.getClass().getMethod("attachBaseContext", Context.class).invoke(realLSPApplication, base); + } + catch (Exception e) { + throw new IllegalStateException("wtf", e); + } + } + } + + // copy from app project + public static Context createAppContext() { + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); + currentActivityThreadMethod.setAccessible(true); + + Object activityThreadObj = currentActivityThreadMethod.invoke(null); + + Field boundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); + boundApplicationField.setAccessible(true); + Object mBoundApplication = boundApplicationField.get(activityThreadObj); // AppBindData + if (mBoundApplication == null) { + Log.e(TAG, "mBoundApplication null"); + return null; + } + Field infoField = mBoundApplication.getClass().getDeclaredField("info"); // info + infoField.setAccessible(true); + Object loadedApkObj = infoField.get(mBoundApplication); // LoadedApk + if (loadedApkObj == null) { + Log.e(TAG, "loadedApkObj null"); + return null; + } + Class contextImplClass = Class.forName("android.app.ContextImpl"); + Method createAppContextMethod = contextImplClass.getDeclaredMethod("createAppContext", activityThreadClass, loadedApkObj.getClass()); + createAppContextMethod.setAccessible(true); + + Object context = createAppContextMethod.invoke(null, (ActivityThread) activityThreadObj, loadedApkObj); + + if (context instanceof Context) { + return (Context) context; + } + } + catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/settings.gradle b/settings.gradle index dd198e0..5e96534 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,3 +13,4 @@ project(':manager-service').projectDir = new File('mmp/manager-service') include ':patch' include ':axmlprinter' include ':share' +include ':appstub'