From 2ac5eb19c967f37e16f293cb8141d56dd74c30af Mon Sep 17 00:00:00 2001 From: solohsu Date: Thu, 28 Feb 2019 18:05:01 +0800 Subject: [PATCH 1/4] Suspend all threads when doing hook stuffs to prevent crashes caused by GC --- .../com/elderdrivers/riru/xposed/Main.java | 4 ++ .../riru/xposed/core/HookMain.java | 10 ++++- Core/jni/main/java_hook/java_hook.cpp | 38 +++++++++++++++---- Core/jni/main/native_hook/native_hook.cpp | 14 +++++++ Core/jni/main/native_hook/native_hook.h | 7 ++++ 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java index e8eeda96..504a308d 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java @@ -125,4 +125,8 @@ public class Main implements KeepAll { public static native void reopenFilesAfterForkNative(); public static native void deoptMethodNative(Object object); + + public static native long suspendAllThreads(); + + public static native void resumeAllThreads(long obj); } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java index 21ad22f1..89413aed 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/core/HookMain.java @@ -1,5 +1,6 @@ package com.elderdrivers.riru.xposed.core; +import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.entry.hooker.OnePlusWorkAroundHooker; import com.elderdrivers.riru.xposed.util.Utils; @@ -111,8 +112,13 @@ public class HookMain { if (backup != null) { HookMethodResolver.resolveMethod(hook, backup); } - if (!backupAndHookNative(target, hook, backup)) { - throw new RuntimeException("Failed to hook " + target + " with " + hook); + long obj = Main.suspendAllThreads(); + try { + if (!backupAndHookNative(target, hook, backup)) { + throw new RuntimeException("Failed to hook " + target + " with " + hook); + } + } finally { + Main.resumeAllThreads(obj); } } diff --git a/Core/jni/main/java_hook/java_hook.cpp b/Core/jni/main/java_hook/java_hook.cpp index 2baf2a03..76f086f6 100644 --- a/Core/jni/main/java_hook/java_hook.cpp +++ b/Core/jni/main/java_hook/java_hook.cpp @@ -20,13 +20,13 @@ jobject gInjectDexClassLoader; static bool isInited = false; -static FileDescriptorTable* gClosedFdTable = nullptr; +static FileDescriptorTable *gClosedFdTable = nullptr; -void closeFilesBeforeForkNative(JNIEnv*, jclass) { +void closeFilesBeforeForkNative(JNIEnv *, jclass) { gClosedFdTable = FileDescriptorTable::Create(); } -void reopenFilesAfterForkNative(JNIEnv*, jclass) { +void reopenFilesAfterForkNative(JNIEnv *, jclass) { if (!gClosedFdTable) { LOGE("gClosedFdTable is null when reopening files"); return; @@ -36,6 +36,22 @@ void reopenFilesAfterForkNative(JNIEnv*, jclass) { gClosedFdTable = nullptr; } +jlong suspendAllThreads(JNIEnv *, jclass) { + if (!suspendAll) { + return 0; + } + ScopedSuspendAll *suspendAllObj = (ScopedSuspendAll *) malloc(sizeof(ScopedSuspendAll)); + suspendAll(suspendAllObj, "edxp_stop_gc", false); + return reinterpret_cast(suspendAllObj); +} + +void resumeAllThreads(JNIEnv *, jclass, jlong obj) { + if (!resumeAll) { + return; + } + resumeAll(reinterpret_cast(obj)); +} + static JNINativeMethod hookMethods[] = { { "init", @@ -58,16 +74,22 @@ static JNINativeMethod hookMethods[] = { (void *) Java_lab_galaxy_yahfa_HookMain_ensureMethodCached }, { - "getInstallerPkgName", "()Ljava/lang/String;", (void *)get_installer_pkg_name + "getInstallerPkgName", "()Ljava/lang/String;", (void *) get_installer_pkg_name }, { - "closeFilesBeforeForkNative", "()V", (void *)closeFilesBeforeForkNative + "closeFilesBeforeForkNative", "()V", (void *) closeFilesBeforeForkNative }, { - "reopenFilesAfterForkNative", "()V", (void *)reopenFilesAfterForkNative + "reopenFilesAfterForkNative", "()V", (void *) reopenFilesAfterForkNative }, { - "deoptMethodNative", "(Ljava/lang/Object;)V", (void *)deoptimize_method + "deoptMethodNative", "(Ljava/lang/Object;)V", (void *) deoptimize_method + }, + { + "suspendAllThreads", "()J", (void *) suspendAllThreads + }, + { + "resumeAllThreads", "(J)V", (void *) resumeAllThreads } }; @@ -106,7 +128,7 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) { jclass entry_class = findClassFromLoader(env, myClassLoader, ENTRY_CLASS_NAME); if (NULL != entry_class) { LOGD("HookEntry Class %p", entry_class); - env->RegisterNatives(entry_class, hookMethods, 8); + env->RegisterNatives(entry_class, hookMethods, 10); isInited = true; LOGD("RegisterNatives succeed for HookEntry."); } else { diff --git a/Core/jni/main/native_hook/native_hook.cpp b/Core/jni/main/native_hook/native_hook.cpp index 3bb3fb11..4b9e3c7e 100644 --- a/Core/jni/main/native_hook/native_hook.cpp +++ b/Core/jni/main/native_hook/native_hook.cpp @@ -212,6 +212,19 @@ void hookRuntime(int api_level, void *artHandle, void (*hookFun)(void *, void *, } } +void (*suspendAll)(ScopedSuspendAll*, const char*, bool) = nullptr; +void (*resumeAll)(ScopedSuspendAll*) = nullptr; + +void getSuspendSyms(int api_level, void *artHandle, void (*hookFun)(void *, void *, void **)) { + if (api_level < ANDROID_N) { + return; + } + suspendAll = reinterpret_cast(dlsym(artHandle, + "_ZN3art16ScopedSuspendAllC2EPKcb")); + resumeAll = reinterpret_cast(dlsym(artHandle, + "_ZN3art16ScopedSuspendAllD2Ev")); +} + void install_inline_hooks() { if (inlineHooksInstalled) { LOGI("inline hooks installed, skip"); @@ -243,6 +256,7 @@ void install_inline_hooks() { } hookRuntime(api_level, artHandle, hookFun); hookInstrumentation(api_level, artHandle, hookFun); + getSuspendSyms(api_level, artHandle, hookFun); hookIsInSamePackage(api_level, artHandle, hookFun); if (disableHiddenAPIPolicyImpl(api_level, artHandle, hookFun)) { LOGI("disableHiddenAPIPolicyImpl done."); diff --git a/Core/jni/main/native_hook/native_hook.h b/Core/jni/main/native_hook/native_hook.h index e9843289..eedb5e94 100644 --- a/Core/jni/main/native_hook/native_hook.h +++ b/Core/jni/main/native_hook/native_hook.h @@ -19,6 +19,13 @@ static constexpr const char *kLibWhalePath = "/system/lib/libwhale.so"; static ret (*old_##func)(__VA_ARGS__); \ static ret new_##func(__VA_ARGS__) +class ScopedSuspendAll { +}; + +extern void (*suspendAll)(ScopedSuspendAll *, const char *, bool); + +extern void (*resumeAll)(ScopedSuspendAll *); + void install_inline_hooks(); void deoptimize_method(JNIEnv *env, jclass clazz, jobject method); From 37bf64a1ee4a17bf4d62c24a574ce48cabaddadc Mon Sep 17 00:00:00 2001 From: solohsu Date: Thu, 28 Feb 2019 20:08:03 +0800 Subject: [PATCH 2/4] Use native methods instead of parameters passing --- .../com/elderdrivers/riru/xposed/Main.java | 44 +++++++++---------- .../proxy/yahfa/BlackWhiteListProxy.java | 14 +++--- .../riru/xposed/proxy/yahfa/NormalProxy.java | 11 ++--- Core/jni/main/inject/framework_hook.cpp | 16 +++---- Core/jni/main/java_hook/java_hook.cpp | 8 +++- 5 files changed, 48 insertions(+), 45 deletions(-) diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java index 504a308d..b90b4c2d 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java @@ -34,36 +34,32 @@ public class Main implements KeepAll { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, boolean isBlackWhiteListMode, - boolean isDynamicModulesMode) { + String appDataDir) { if (BuildConfig.DEBUG) { forkAndSpecializePramsStr = String.format( "Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)", uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), mountExternal, seInfo, niceName, Arrays.toString(fdsToClose), - Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir, - isDynamicModulesMode); + Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir); } - if (isBlackWhiteListMode) { + if (isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, - instructionSet, appDataDir, isDynamicModulesMode); + instructionSet, appDataDir); } else { NormalProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, - appDataDir, isDynamicModulesMode); + appDataDir); } } - public static void forkAndSpecializePost(int pid, String appDataDir, - boolean isBlackWhiteListMode, - boolean isDynamicModulesMode) { + public static void forkAndSpecializePost(int pid, String appDataDir) { if (pid == 0) { Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid()); - if (isBlackWhiteListMode) { - BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, isDynamicModulesMode); + if (isBlackWhiteListEnabled()) { + BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir); } else { - NormalProxy.forkAndSpecializePost(pid, appDataDir, isDynamicModulesMode); + NormalProxy.forkAndSpecializePost(pid, appDataDir); } } else { // in zygote process, res is child zygote pid @@ -72,30 +68,28 @@ public class Main implements KeepAll { } public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities, - boolean isBlackWhiteListMode, boolean isDynamicModulesMode) { + long permittedCapabilities, long effectiveCapabilities) { if (BuildConfig.DEBUG) { forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)", uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), permittedCapabilities, effectiveCapabilities); } - if (isBlackWhiteListMode) { + if (isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, - permittedCapabilities, effectiveCapabilities, isDynamicModulesMode); + permittedCapabilities, effectiveCapabilities); } else { NormalProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, - permittedCapabilities, effectiveCapabilities, isDynamicModulesMode); + permittedCapabilities, effectiveCapabilities); } } - public static void forkSystemServerPost(int pid, boolean isBlackWhiteListMode, - boolean isDynamicModulesMode) { + public static void forkSystemServerPost(int pid) { if (pid == 0) { Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid()); - if (isBlackWhiteListMode) { - BlackWhiteListProxy.forkSystemServerPost(pid, isDynamicModulesMode); + if (isBlackWhiteListEnabled()) { + BlackWhiteListProxy.forkSystemServerPost(pid); } else { - NormalProxy.forkSystemServerPost(pid, isDynamicModulesMode); + NormalProxy.forkSystemServerPost(pid); } } else { // in zygote process, res is child zygote pid @@ -118,6 +112,10 @@ public class Main implements KeepAll { public static native String getInstallerPkgName(); + public static native boolean isBlackWhiteListEnabled(); + + public static native boolean isDynamicModulesEnabled(); + // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff public static native void closeFilesBeforeForkNative(); diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java index b246c648..f975373d 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java @@ -12,7 +12,8 @@ public class BlackWhiteListProxy { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, boolean isDynamicModulesMode) { + String appDataDir) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); ConfigManager.setDynamicModulesMode(isDynamicModulesMode); if (!isDynamicModulesMode) { Router.loadModulesSafely(); @@ -20,8 +21,8 @@ public class BlackWhiteListProxy { } } - public static void forkAndSpecializePost(int pid, String appDataDir, - boolean isDynamicModulesMode) { + public static void forkAndSpecializePost(int pid, String appDataDir) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { Main.reopenFilesAfterForkNative(); } @@ -34,8 +35,8 @@ public class BlackWhiteListProxy { public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, - long effectiveCapabilities, - boolean isDynamicModulesMode) { + long effectiveCapabilities) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); ConfigManager.setDynamicModulesMode(isDynamicModulesMode); if (!isDynamicModulesMode) { Router.loadModulesSafely(); @@ -43,7 +44,8 @@ public class BlackWhiteListProxy { } } - public static void forkSystemServerPost(int pid, boolean isDynamicModulesMode) { + public static void forkSystemServerPost(int pid) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { Main.reopenFilesAfterForkNative(); } diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java index e967d633..462e9db2 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java @@ -13,7 +13,8 @@ public class NormalProxy { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, - String appDataDir, boolean isDynamicModulesMode) { + String appDataDir) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); Main.appDataDir = appDataDir; ConfigManager.setDynamicModulesMode(isDynamicModulesMode); Router.prepare(false); @@ -24,7 +25,7 @@ public class NormalProxy { Main.closeFilesBeforeForkNative(); } - public static void forkAndSpecializePost(int pid, String appDataDir, boolean isDynamicModulesMode) { + public static void forkAndSpecializePost(int pid, String appDataDir) { // TODO consider processes without forkAndSpecializePost called Main.reopenFilesAfterForkNative(); Router.onEnterChildProcess(); @@ -34,8 +35,8 @@ public class NormalProxy { } public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, - long permittedCapabilities, long effectiveCapabilities, - boolean isDynamicModulesMode) { + long permittedCapabilities, long effectiveCapabilities) { + final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); Main.appDataDir = getDataPathPrefix() + "android"; ConfigManager.setDynamicModulesMode(isDynamicModulesMode); Router.prepare(true); @@ -50,7 +51,7 @@ public class NormalProxy { Main.closeFilesBeforeForkNative(); } - public static void forkSystemServerPost(int pid, boolean isDynamicModulesMode) { + public static void forkSystemServerPost(int pid) { // in system_server process Main.reopenFilesAfterForkNative(); Router.onEnterChildProcess(); diff --git a/Core/jni/main/inject/framework_hook.cpp b/Core/jni/main/inject/framework_hook.cpp index fa927a39..4309ac98 100644 --- a/Core/jni/main/inject/framework_hook.cpp +++ b/Core/jni/main/inject/framework_hook.cpp @@ -50,9 +50,8 @@ void onNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid } prepareJavaEnv(env); // jump to java code - findAndCall(env, "forkSystemServerPre", "(II[II[[IJJZZ)V", uid, gid, gids, runtime_flags, - rlimits, permittedCapabilities, effectiveCapabilities, - is_black_white_list_mode, is_dynamic_modules_mode); + findAndCall(env, "forkSystemServerPre", "(II[II[[IJJ)V", uid, gid, gids, runtime_flags, + rlimits, permittedCapabilities, effectiveCapabilities); } @@ -63,8 +62,7 @@ int onNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { } prepareJavaEnv(env); // only do work in child since findAndCall would print log - findAndCall(env, "forkSystemServerPost", "(IZZ)V", res, - is_black_white_list_enabled(), is_dynamic_modules_enabled()); + findAndCall(env, "forkSystemServerPost", "(I)V", res); } else { // in zygote process, res is child zygote pid // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 @@ -94,11 +92,10 @@ void onNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, } prepareJavaEnv(env); findAndCall(env, "forkAndSpecializePre", - "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;ZZ)V", + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)V", uid, gid, gids, runtime_flags, rlimits, _mount_external, se_info, se_name, fdsToClose, fdsToIgnore, - is_child_zygote, instructionSet, appDataDir, - is_black_white_list_mode, is_dynamic_modules_mode); + is_child_zygote, instructionSet, appDataDir); } int onNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { @@ -107,8 +104,7 @@ int onNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { return 0; } prepareJavaEnv(env); - findAndCall(env, "forkAndSpecializePost", "(ILjava/lang/String;ZZ)V", res, sAppDataDir, - is_black_white_list_enabled(), is_dynamic_modules_enabled()); + findAndCall(env, "forkAndSpecializePost", "(ILjava/lang/String;)V", res, sAppDataDir); } else { // in zygote process, res is child zygote pid // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 diff --git a/Core/jni/main/java_hook/java_hook.cpp b/Core/jni/main/java_hook/java_hook.cpp index 76f086f6..749b50a1 100644 --- a/Core/jni/main/java_hook/java_hook.cpp +++ b/Core/jni/main/java_hook/java_hook.cpp @@ -73,6 +73,12 @@ static JNINativeMethod hookMethods[] = { "(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V", (void *) Java_lab_galaxy_yahfa_HookMain_ensureMethodCached }, + { + "isBlackWhiteListEnabled", "()Z", (void *) is_black_white_list_enabled + }, + { + "isDynamicModulesEnabled", "()Z", (void *) is_dynamic_modules_enabled + }, { "getInstallerPkgName", "()Ljava/lang/String;", (void *) get_installer_pkg_name }, @@ -128,7 +134,7 @@ void loadDexAndInit(JNIEnv *env, const char *dexPath) { jclass entry_class = findClassFromLoader(env, myClassLoader, ENTRY_CLASS_NAME); if (NULL != entry_class) { LOGD("HookEntry Class %p", entry_class); - env->RegisterNatives(entry_class, hookMethods, 10); + env->RegisterNatives(entry_class, hookMethods, 12); isInited = true; LOGD("RegisterNatives succeed for HookEntry."); } else { From 441106915f377618b627704c31fdf1c8d36cbd46 Mon Sep 17 00:00:00 2001 From: solohsu Date: Thu, 28 Feb 2019 21:39:30 +0800 Subject: [PATCH 3/4] Deoptimize methods in framework to make their callees hookable --- .../riru/xposed/entry/Router.java | 6 ++- .../xposed/entry/hooker/SystemMainHooker.java | 3 ++ .../proxy/yahfa/BlackWhiteListProxy.java | 14 ++++++ .../riru/xposed/proxy/yahfa/NormalProxy.java | 3 ++ .../xposed/util/InlinedMethodCallers.java | 49 +++++++++++++++++++ .../xposed/util/PrebuiltMethodsDeopter.java | 39 +++++++++++++++ .../android/xposed/callbacks/XCallback.java | 11 +++++ 7 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java create mode 100644 Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java index 73ef995f..7d358fb5 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/Router.java @@ -2,15 +2,19 @@ package com.elderdrivers.riru.xposed.entry; import android.text.TextUtils; -import com.elderdrivers.riru.xposed.config.InstallerChooser; +import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.core.HookMain; import com.elderdrivers.riru.xposed.entry.bootstrap.AppBootstrapHookInfo; import com.elderdrivers.riru.xposed.entry.bootstrap.SysBootstrapHookInfo; import com.elderdrivers.riru.xposed.entry.bootstrap.SysInnerHookInfo; import com.elderdrivers.riru.xposed.entry.hooker.SystemMainHooker; +import com.elderdrivers.riru.xposed.util.InlinedMethodCallers; import com.elderdrivers.riru.xposed.util.Utils; +import java.util.Arrays; + import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; public class Router { diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java index 11f81373..1f51c3d2 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/entry/hooker/SystemMainHooker.java @@ -4,6 +4,7 @@ import android.app.ActivityThread; import com.elderdrivers.riru.common.KeepMembers; import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; import de.robv.android.xposed.XposedBridge; @@ -30,6 +31,8 @@ public class SystemMainHooker implements KeepMembers { try { // get system_server classLoader systemServerCL = Thread.currentThread().getContextClassLoader(); + // deopt methods in SYSTEMSERVERCLASSPATH + PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL); Router.startSystemServerHook(); } catch (Throwable t) { logE("error when hooking systemMain", t); diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java index f975373d..d361b38a 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/BlackWhiteListProxy.java @@ -3,6 +3,7 @@ package com.elderdrivers.riru.xposed.proxy.yahfa; import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.config.ConfigManager; import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix; @@ -13,8 +14,11 @@ public class BlackWhiteListProxy { String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { + // always enter here, make sure secondary zygote's modules is loaded only once + // when isDynamicModulesMode is not on final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote if (!isDynamicModulesMode) { Router.loadModulesSafely(); Main.closeFilesBeforeForkNative(); @@ -22,6 +26,8 @@ public class BlackWhiteListProxy { } public static void forkAndSpecializePost(int pid, String appDataDir) { + // when this process is in white list or not in black list + // installBootstrapHooks -> loadModules if needed final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { Main.reopenFilesAfterForkNative(); @@ -36,8 +42,14 @@ public class BlackWhiteListProxy { public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { + // we always enter here whether black/white list is on or not final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote + // we never install bootstrap hooks here in black/white list mode + // because installed hooks would be propagated to all child processes of main zygote + // hence we cannot install hooks for processes like com.android.phone process who are + // not from forkAndSpecialize as a side effect if (!isDynamicModulesMode) { Router.loadModulesSafely(); Main.closeFilesBeforeForkNative(); @@ -45,6 +57,8 @@ public class BlackWhiteListProxy { } public static void forkSystemServerPost(int pid) { + // should only here when system_server is in white list or not in black list + // installBootstrapHooks -> loadModules if needed final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { Main.reopenFilesAfterForkNative(); diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java index 462e9db2..20988054 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/proxy/yahfa/NormalProxy.java @@ -4,6 +4,7 @@ import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.config.ConfigManager; import com.elderdrivers.riru.xposed.dexmaker.DynamicBridge; import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; import static com.elderdrivers.riru.xposed.util.FileUtils.getDataPathPrefix; @@ -17,6 +18,7 @@ public class NormalProxy { final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); Main.appDataDir = appDataDir; ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote Router.prepare(false); // install bootstrap hooks for secondary zygote Router.installBootstrapHooks(false); @@ -40,6 +42,7 @@ public class NormalProxy { Main.appDataDir = getDataPathPrefix() + "android"; ConfigManager.setDynamicModulesMode(isDynamicModulesMode); Router.prepare(true); + PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote // install bootstrap hooks for main zygote as early as possible // in case we miss some processes not forked via forkAndSpecialize // for instance com.android.phone diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java new file mode 100644 index 00000000..eb8a29cc --- /dev/null +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/InlinedMethodCallers.java @@ -0,0 +1,49 @@ +package com.elderdrivers.riru.xposed.util; + +import java.util.HashMap; + +/** + * Providing a whitelist of methods which are the callers of the target methods we want to hook. + * Because the target methods are inlined into the callers, we deoptimize the callers to + * run in intercept mode to make target methods hookable. + *

+ * Only for methods which are included in pre-compiled framework codes. + * TODO recompile system apps and priv-apps since their original dex files are available + */ +public class InlinedMethodCallers { + + public static final String KEY_BOOT_IMAGE = "boot_image"; + public static final String KEY_SYSTEM_SERVER = "system_server"; + + /** + * Key should be {@link #KEY_BOOT_IMAGE}, {@link #KEY_SYSTEM_SERVER}, or a package name + * of system apps or priv-apps i.e. com.android.systemui + */ + private static final HashMap CALLERS = new HashMap<>(); + + /** + * format for each row: {className, methodName, methodSig} + */ + private static final String[][] BOOT_IMAGE = { + // callers of Application#attach(Context) + {"android.app.Instrumentation", "newApplication", "(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Context;)Landroid/app/Application;"} + }; + + private static final String[][] SYSTEM_SERVER = {}; + + private static final String[][] SYSTEM_UI = {}; + + static { + CALLERS.put(KEY_BOOT_IMAGE, BOOT_IMAGE); + CALLERS.put(KEY_SYSTEM_SERVER, SYSTEM_SERVER); + CALLERS.put("com.android.systemui", SYSTEM_UI); + } + + public static HashMap getAll() { + return CALLERS; + } + + public static String[][] get(String where) { + return CALLERS.get(where); + } +} diff --git a/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java new file mode 100644 index 00000000..24c00581 --- /dev/null +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/util/PrebuiltMethodsDeopter.java @@ -0,0 +1,39 @@ +package com.elderdrivers.riru.xposed.util; + +import com.elderdrivers.riru.xposed.Main; + +import java.util.Arrays; + +import de.robv.android.xposed.XposedHelpers; + +import static com.elderdrivers.riru.xposed.util.InlinedMethodCallers.KEY_BOOT_IMAGE; +import static com.elderdrivers.riru.xposed.util.InlinedMethodCallers.KEY_SYSTEM_SERVER; + +public class PrebuiltMethodsDeopter { + + public static void deoptMethods(String where, ClassLoader cl) { + String[][] callers = InlinedMethodCallers.get(where); + if (callers == null) { + return; + } + for (String[] caller : callers) { + try { + Object method = Main.findMethodNative( + XposedHelpers.findClass(caller[0], cl), caller[1], caller[2]); + if (method != null) { + Main.deoptMethodNative(method); + } + } catch (Throwable throwable) { + Utils.logE("error when deopting method: " + Arrays.toString(caller), throwable); + } + } + } + + public static void deoptBootMethods() { + deoptMethods(KEY_BOOT_IMAGE, null); + } + + public static void deoptSystemServerMethods(ClassLoader sysCL) { + deoptMethods(KEY_SYSTEM_SERVER, sysCL); + } +} diff --git a/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java b/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java index 56930fb4..4de87157 100644 --- a/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java +++ b/Bridge/src/main/java/de/robv/android/xposed/callbacks/XCallback.java @@ -2,6 +2,9 @@ package de.robv.android.xposed.callbacks; import android.os.Bundle; +import com.elderdrivers.riru.xposed.entry.Router; +import com.elderdrivers.riru.xposed.util.PrebuiltMethodsDeopter; + import java.io.Serializable; import de.robv.android.xposed.XposedBridge; @@ -98,6 +101,14 @@ public abstract class XCallback implements Comparable { /** @hide */ public static void callAll(Param param) { + + if (param instanceof XC_LoadPackage.LoadPackageParam) { + // deopt methods in system apps or priv-apps, this would be not necessary + // only if we found out how to recompile their apks + XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param; + PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader); + } + if (param.callbacks == null) throw new IllegalStateException("This object was not created for use with callAll"); From f74cf04361364801d3edb7651d5c4f4e8b308973 Mon Sep 17 00:00:00 2001 From: solohsu Date: Thu, 28 Feb 2019 23:56:28 +0800 Subject: [PATCH 4/4] Fix EdXposed Magisk module can't be loaded on some devices --- Core/template_override/common/post-fs-data.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/template_override/common/post-fs-data.sh b/Core/template_override/common/post-fs-data.sh index 0dd4bef7..c4d2630d 100644 --- a/Core/template_override/common/post-fs-data.sh +++ b/Core/template_override/common/post-fs-data.sh @@ -17,7 +17,7 @@ supolicy --live "allow system_server system_server process {execmem}" supolicy --live "allow coredomain coredomain process {execmem}" # read configs set in our app -supolicy --live "allow {zygote system_server} app_data_file * *" +supolicy --live "allow coredomain app_data_file * *" supolicy --live "attradd {system_app platform_app} mlstrustedsubject" # read module apk file in zygote