diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DexMakerUtils.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DexMakerUtils.java index 47eebdcd..9f99619f 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DexMakerUtils.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DexMakerUtils.java @@ -7,6 +7,7 @@ import android.text.TextUtils; import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.config.ConfigManager; +import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; @@ -226,4 +227,20 @@ public class DexMakerUtils { TypeId boxTypeId; code.returnValue(resultLocals.get(returnType)); } + + public static String getSha1Hex(String text) { + final MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + byte[] result = digest.digest(text.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte b : result) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (Exception e) { + DexLog.e("error hashing target method: " + text, e); + } + return ""; + } } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DynamicBridge.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DynamicBridge.java index f9aca719..22039ee3 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DynamicBridge.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/DynamicBridge.java @@ -1,7 +1,6 @@ package com.elderdrivers.riru.xposed.dexmaker; import com.elderdrivers.riru.xposed.Main; -import com.elderdrivers.riru.xposed.util.FileUtils; import java.io.File; import java.lang.reflect.Constructor; @@ -72,8 +71,6 @@ public final class DynamicBridge { if (!dexPathInited.compareAndSet(false, true)) { return; } - // delete previous compiled dex to prevent potential crashing - // TODO find a way to reuse them in consideration of performance try { // we always choose to use device encrypted storage data on android N and later // in case some app is installing hooks before phone is unlocked @@ -83,10 +80,6 @@ public final class DynamicBridge { dexOptDir = new File(dexDir, "oat"); dexDir.mkdirs(); DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath()); - try { - FileUtils.delete(dexOptDir); - } catch (Throwable throwable) { - } } catch (Throwable throwable) { DexLog.e("error when init dex path", throwable); } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/HookerDexMaker.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/HookerDexMaker.java index 7b2bcb60..722545dc 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/HookerDexMaker.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/dexmaker/HookerDexMaker.java @@ -42,7 +42,7 @@ public class HookerDexMaker { public static final String METHOD_NAME_SETUP = "setup"; public static final TypeId objArrayTypeId = TypeId.get(Object[].class); private static final String CLASS_DESC_PREFIX = "L"; - private static final String CLASS_NAME_PREFIX = "EdHooker"; + private static final String CLASS_NAME_PREFIX = "ed_"; private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; private static final String FIELD_NAME_METHOD = "method"; private static final String PARAMS_FIELD_NAME_METHOD = "method"; @@ -183,26 +183,30 @@ public class HookerDexMaker { @TargetApi(Build.VERSION_CODES.O) private void doMake() throws Exception { + final boolean useInMemoryCl = TextUtils.isEmpty(mDexDirPath); mDexMaker = new DexMaker(); - // Generate a Hooker class. - String className = CLASS_NAME_PREFIX + sClassNameSuffix.getAndIncrement(); - String classDesc = CLASS_DESC_PREFIX + className + ";"; - mHookerTypeId = TypeId.get(classDesc); - mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT); - generateFields(); - generateSetupMethod(); - generateBackupMethod(); - generateHookMethod(); - generateCallBackupMethod(); - ClassLoader loader; - if (TextUtils.isEmpty(mDexDirPath)) { - // in memory dex classloader + // Generate a Hooker class. + String className = CLASS_NAME_PREFIX; + if (!useInMemoryCl) { + // if not using InMemoryDexClassLoader, className is also used as dex file name + // so it should be different from each other + String suffix = DexMakerUtils.getSha1Hex(mMember.toString()); + if (TextUtils.isEmpty(suffix)) { // just in case + suffix = String.valueOf(sClassNameSuffix.getAndIncrement()); + } + className = className + suffix; + if (!new File(mDexDirPath, className).exists()) { + // if file exists, reuse it and skip generating + doGenerate(className); + } + // load dex file from disk + loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), className); + } else { + // do everything in memory + doGenerate(className); byte[] dexBytes = mDexMaker.generate(); loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader); - } else { - // Create the dex file and load it. - loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath)); } mHookClass = loader.loadClass(className); @@ -216,6 +220,17 @@ public class HookerDexMaker { HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod); } + private void doGenerate(String className) { + String classDesc = CLASS_DESC_PREFIX + className + ";"; + mHookerTypeId = TypeId.get(classDesc); + mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT); + generateFields(); + generateSetupMethod(); + generateBackupMethod(); + generateHookMethod(); + generateCallBackupMethod(); + } + public Method getHookMethod() { return mHookMethod; } diff --git a/Core/build.gradle b/Core/build.gradle index 95f3a3e0..f28d6ac5 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -1,7 +1,7 @@ import org.gradle.internal.os.OperatingSystem; apply plugin: 'com.android.library' -version "v0.3.1.3_beta-SNAPSHOT" +version "v0.3.1.4_beta-SNAPSHOT" extensions["module_name"] = "EdXposed" android { compileSdkVersion 28 diff --git a/Core/template_override/common/util_functions.sh b/Core/template_override/common/util_functions.sh index f5dc0267..67276981 100644 --- a/Core/template_override/common/util_functions.sh +++ b/Core/template_override/common/util_functions.sh @@ -1,6 +1,6 @@ #!/system/bin/sh -EDXP_VERSION="0.3.1.3_beta-SNAPSHOT (3130)" +EDXP_VERSION="0.3.1.4_beta-SNAPSHOT (3140)" ANDROID_SDK=`getprop ro.build.version.sdk` BUILD_DESC=`getprop ro.build.description` PRODUCT=`getprop ro.build.product` diff --git a/Core/template_override/config.sh b/Core/template_override/config.sh index c5547e6f..a0170b1d 100644 --- a/Core/template_override/config.sh +++ b/Core/template_override/config.sh @@ -41,7 +41,7 @@ LATESTARTSERVICE=false print_modname() { ui_print "************************************" - ui_print " Riru - Ed Xposed v0.3.1.3 " + ui_print " Riru - Ed Xposed v0.3.1.4 " ui_print "************************************" } diff --git a/Core/template_override/module.prop b/Core/template_override/module.prop index 2dc811c3..6e139f04 100644 --- a/Core/template_override/module.prop +++ b/Core/template_override/module.prop @@ -1,7 +1,7 @@ id=riru_edxposed name=Riru - Ed Xposed -version=v0.3.1.3_beta-SNAPSHOT -versionCode=3130 +version=v0.3.1.4_beta-SNAPSHOT +versionCode=3140 author=solohsu & MlgmXyysd description=Magisk version of Xposed. Require Riru - Core installed. minMagisk=17000 diff --git a/Core/template_override/riru_module.prop b/Core/template_override/riru_module.prop index d492fd8a..92d58ef7 100644 --- a/Core/template_override/riru_module.prop +++ b/Core/template_override/riru_module.prop @@ -1,5 +1,5 @@ name=Ed Xposed -version=v0.3.1.3_beta-SNAPSHOT -versionCode=3130 +version=v0.3.1.4_beta-SNAPSHOT +versionCode=3140 author=solohsu & MlgmXyysd description=Magisk version of Xposed. Require Riru - Core installed. diff --git a/Core/template_override/system/framework/edconfig.dex b/Core/template_override/system/framework/edconfig.dex index ab51e4ed..b7f9d96d 100644 --- a/Core/template_override/system/framework/edconfig.dex +++ b/Core/template_override/system/framework/edconfig.dex @@ -1,4 +1,4 @@ -version=90.0-0.3.1.3-beta-SNAPSHOT +version=90.0-0.3.1.4-beta-SNAPSHOT arch=arm64 minsdk=23 maxsdk=28 diff --git a/Core/template_override/system/framework/eddexmaker.dex b/Core/template_override/system/framework/eddexmaker.dex index 1410d305..6d194a63 100644 Binary files a/Core/template_override/system/framework/eddexmaker.dex and b/Core/template_override/system/framework/eddexmaker.dex differ diff --git a/dexmaker/src/main/java/external/com/android/dx/DexMaker.java b/dexmaker/src/main/java/external/com/android/dx/DexMaker.java index 411418f8..3f151489 100644 --- a/dexmaker/src/main/java/external/com/android/dx/DexMaker.java +++ b/dexmaker/src/main/java/external/com/android/dx/DexMaker.java @@ -471,6 +471,10 @@ public final class DexMaker { } } + public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException { + return generateAndLoad(parent, dexCache, null); + } + /** * Generates a dex file and loads its types into the current process. * @@ -496,8 +500,9 @@ public final class DexMaker { * @param dexCache the destination directory where generated and optimized * dex files will be written. If null, this class will try to guess the * application's private data dir. + * @param fileName the name of dex file */ - public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException { + public ClassLoader generateAndLoad(ClassLoader parent, File dexCache, String fileName) throws IOException { if (dexCache == null) { String property = System.getProperty("dexmaker.dexcache"); if (property != null) { @@ -511,7 +516,9 @@ public final class DexMaker { } } - File result = new File(dexCache, generateFileName()); + if (fileName == null || fileName.isEmpty()) + fileName = generateFileName(); + File result = new File(dexCache, fileName); // Check that the file exists. If it does, return a DexClassLoader and skip all // the dex bytecode generation. if (result.exists()) {