diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java index de5988d03..1d877d21c 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java @@ -45,6 +45,8 @@ private static boolean isFileExists(String path) { public static native String getPrefsPath(String suffix); + public static native String getCachePath(String suffix); + public static native String getBaseConfigPath(); public static native String getDataPathPrefix(); diff --git a/edxp-core/src/main/cpp/main/src/config_manager.h b/edxp-core/src/main/cpp/main/src/config_manager.h index 9b3130182..ac5478b2e 100644 --- a/edxp-core/src/main/cpp/main/src/config_manager.h +++ b/edxp-core/src/main/cpp/main/src/config_manager.h @@ -68,6 +68,10 @@ namespace edxp { return misc_path_ / "framework" / suffix; } + inline static auto GetCachePath(const std::string &suffix = {}) { + return misc_path_ / "cache" / suffix; + } + inline static auto GetLibSandHookName() { if constexpr(edxp::is64) return GetFrameworkPath("lib64/libsandhook.edxp.so"); diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp index 5f0a96c9f..2bed5a4cd 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp @@ -53,6 +53,13 @@ namespace edxp { return env->NewStringUTF(result.c_str()); } + static jstring ConfigManager_getCachePath(JNI_START, jstring jSuffix) { + const char *suffix = env->GetStringUTFChars(jSuffix, JNI_FALSE); + auto result = ConfigManager::GetCachePath(suffix); + env->ReleaseStringUTFChars(jSuffix, suffix); + return env->NewStringUTF(result.c_str()); + } + static jstring ConfigManager_getBaseConfigPath(JNI_START) { auto result = ConfigManager::GetInstance()->GetBaseConfigPath(); return env->NewStringUTF(result.c_str()); @@ -80,6 +87,8 @@ namespace edxp { "(Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(ConfigManager, getPrefsPath, "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(ConfigManager, getCachePath, + "(Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(ConfigManager, getBaseConfigPath,"()Ljava/lang/String;"), NATIVE_METHOD(ConfigManager, getModulesList, "()Ljava/lang/String;"), }; diff --git a/edxp-core/template_override/customize.sh b/edxp-core/template_override/customize.sh index cbc70927b..3f1f82ae6 100644 --- a/edxp-core/template_override/customize.sh +++ b/edxp-core/template_override/customize.sh @@ -230,6 +230,9 @@ if [[ "${VARIANT}" == "SandHook" ]]; then fi set_perm_recursive /data/misc/$MISC_PATH/framework root root 0755 0644 "u:object_r:magisk_file:s0" || abortC "! ${LANG_CUST_ERR_PERM}" +mkdir -p /data/misc/$MISC_PATH/cache +set_perm /data/misc/$MISC_PATH/cache root root 0777 "u:object_r:magisk_file:s0" || abortC "! ${LANG_CUST_ERR_PERM}" + mv "${MODPATH}/system/lib/libriru_edxp.so" "${MODPATH}/system/lib/${LIB_RIRU_EDXP}" if [[ "${IS64BIT}" == true ]]; then mv "${MODPATH}/system/lib64/libriru_edxp.so" "${MODPATH}/system/lib64/${LIB_RIRU_EDXP}" diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java index 8fbf22dca..0cacd3864 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DexMakerUtils.java @@ -1,11 +1,9 @@ package com.elderdrivers.riru.edxp.yahfa.dexmaker; -import android.app.AndroidAppHelper; -import android.os.Build; -import android.text.TextUtils; - import com.elderdrivers.riru.edxp.config.ConfigManager; +import com.elderdrivers.riru.edxp.util.Utils; +import java.io.File; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; @@ -16,19 +14,14 @@ public class DexMakerUtils { - private static final boolean IN_MEMORY_DEX_ELIGIBLE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + public static boolean canCache = true; - public static boolean shouldUseInMemoryHook() { - if (!IN_MEMORY_DEX_ELIGIBLE) { - return false; - } - String packageName = AndroidAppHelper.currentPackageName(); - if (TextUtils.isEmpty(packageName)) { //default to true - DexLog.w("packageName is empty, processName=" + ConfigManager.appProcessName - + ", appDataDir=" + ConfigManager.appDataDir); - return true; + static { + File cacheDir = new File(ConfigManager.getCachePath("")); + if(!cacheDir.canRead() || !cacheDir.canWrite()) { + Utils.logW("Cache disabled"); + canCache = false; } - return !ConfigManager.shouldUseCompatMode(packageName); } public static void autoBoxIfNecessary(Code code, Local target, Local source) { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java index f82975c2b..09e0c49d4 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/DynamicBridge.java @@ -15,17 +15,11 @@ import de.robv.android.xposed.EdHooker; import de.robv.android.xposed.XposedBridge; -import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; -import static com.elderdrivers.riru.edxp.util.FileUtils.getPackageName; -import static com.elderdrivers.riru.edxp.util.ProcessUtils.getCurrentProcessName; -import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.shouldUseInMemoryHook; - public final class DynamicBridge { private static final HashMap hookedInfo = new HashMap<>(); private static final HookerDexMaker dexMaker = new HookerDexMaker(); private static final AtomicBoolean dexPathInited = new AtomicBoolean(false); - private static File dexDir; private static File dexOptDir; /** @@ -50,46 +44,16 @@ public static synchronized void hookMethod(Member hookMethod, XposedBridge.Addit DexLog.d("start to generate class for: " + hookMethod); long startTime = System.nanoTime(); try { - // for Android Oreo and later use InMemoryClassLoader - if (!shouldUseInMemoryHook()) { - setupDexCachePath(); - } dexMaker.start(hookMethod, additionalHookInfo, - hookMethod.getDeclaringClass().getClassLoader(), getDexDirPath()); + hookMethod.getDeclaringClass().getClassLoader()); hookedInfo.put(hookMethod, dexMaker.getHooker()); } catch (Exception e) { - DexLog.e("error occur when generating dex. dexDir=" + dexDir, e); + DexLog.e("error occur when generating dex.", e); } long endTime = System.nanoTime(); DexLog.d("generated class for " + hookMethod + " in " + ((endTime-startTime) * 1.e-6) + "ms"); } - private static String getDexDirPath() { - if (dexDir == null) { - return null; - } - return dexDir.getAbsolutePath(); - } - - private static void setupDexCachePath() { - // using file based DexClassLoader - if (!dexPathInited.compareAndSet(false, true)) { - return; - } - 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 - String fixedAppDataDir = getDataPathPrefix() + getPackageName(ConfigManager.appDataDir) + "/"; - dexDir = new File(fixedAppDataDir, "/cache/edhookers/" - + getCurrentProcessName(ConfigManager.appProcessName).replace(":", "_") + "/"); - dexOptDir = new File(dexDir, "oat"); - dexDir.mkdirs(); - DexLog.d(ConfigManager.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath()); - } catch (Throwable throwable) { - DexLog.e("error when init dex path", throwable); - } - } - private static boolean checkMember(Member member) { if (member instanceof Method) { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java index 201417462..caafae443 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java @@ -4,6 +4,7 @@ import android.os.Build; import android.text.TextUtils; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.core.yahfa.HookMain; import com.elderdrivers.riru.edxp.util.ProxyClassLoader; import com.elderdrivers.riru.edxp.yahfa.BuildConfig; @@ -26,9 +27,11 @@ import external.com.android.dx.Local; import external.com.android.dx.MethodId; import external.com.android.dx.TypeId; +import pxb.android.arsc.Config; import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.autoBoxIfNecessary; import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.autoUnboxIfNecessary; +import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.canCache; import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.createResultLocals; import static com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils.getObjTypeIdIfPrimitive; @@ -69,7 +72,6 @@ public class HookerDexMaker { private Class mHookClass; private Method mHookMethod; private Method mBackupMethod; - private String mDexDirPath; private EdHooker mHooker; private static TypeId[] getParameterTypeIds(Class[] parameterTypes, boolean isStatic) { @@ -101,7 +103,7 @@ private static Class[] getParameterTypes(Class[] parameterTypes, boolean i } public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, - ClassLoader appClassLoader, String dexDirPath) throws Exception { + ClassLoader appClassLoader) throws Exception { if (member instanceof Method) { Method method = (Method) member; mIsStatic = Modifier.isStatic(method.getModifiers()); @@ -132,7 +134,6 @@ public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, } mMember = member; mHookInfo = hookInfo; - mDexDirPath = dexDirPath; if (appClassLoader == null || appClassLoader.getClass().getName().equals("java.lang.BootClassLoader")) { mAppClassLoader = getClass().getClassLoader(); @@ -145,27 +146,36 @@ public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo, @TargetApi(Build.VERSION_CODES.O) private void doMake(String hookedClassName) throws Exception { - final boolean useInMemoryCl = TextUtils.isEmpty(mDexDirPath); mDexMaker = new DexMaker(); - ClassLoader loader; + ClassLoader loader = null; // 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 { + boolean usedCache = false; + if (canCache) { + try { + // className is also used as dex file name + // so it should be different from each other + String suffix = DexMakerUtils.getSha1Hex(mMember.toString()); + className = className + suffix; + String dexFileName = className + ".jar"; + File dexFile = new File(ConfigManager.getCachePath(dexFileName)); + if (!dexFile.exists()) { + // if file exists, reuse it and skip generating + DexLog.d("Generating " + dexFileName); + doGenerate(className); + loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(ConfigManager.getCachePath("")), dexFileName, false); + dexFile.setWritable(true, false); + dexFile.setReadable(true, false); + } else { + DexLog.d("Using cache " + dexFileName); + loader = mDexMaker.loadClassDirect(mAppClassLoader, new File(ConfigManager.getCachePath("")), dexFileName); + } + usedCache = true; + } catch (Throwable ignored) {} + } + if (!usedCache) { // do everything in memory + DexLog.d("Generating in memory"); if(BuildConfig.DEBUG) className = className + hookedClassName.replace(".", "/"); doGenerate(className);