From 30e7dfd3d8963b4ffeb023c2db75deb41324309e Mon Sep 17 00:00:00 2001 From: solohsu Date: Wed, 6 Mar 2019 17:51:03 +0800 Subject: [PATCH] Wait GC to complete before each hook --- .../com/elderdrivers/riru/xposed/Main.java | 2 + .../riru/xposed/core/HookMain.java | 12 ++++- Core/jni/main/java_hook/java_hook.cpp | 11 +++- Core/jni/main/native_hook/native_hook.cpp | 50 ++++++++++++++++--- Core/jni/main/native_hook/native_hook.h | 2 + 5 files changed, 67 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 2674b3d0..0b27948f 100644 --- a/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java +++ b/Bridge/src/main/java/com/elderdrivers/riru/xposed/Main.java @@ -129,4 +129,6 @@ public class Main implements KeepAll { public static native long suspendAllThreads(); public static native void resumeAllThreads(long obj); + + public static native int waitForGcToComplete(long thread); } 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 846427f3..7faa9186 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; @@ -11,6 +12,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.Set; +import de.robv.android.xposed.XposedHelpers; + import static com.elderdrivers.riru.xposed.Main.backupAndHookNative; import static com.elderdrivers.riru.xposed.Main.findMethodNative; @@ -111,7 +114,14 @@ public class HookMain { if (backup != null) { HookMethodResolver.resolveMethod(hook, backup); } - Runtime.getRuntime().gc(); + // make sure GC completed before hook + Thread currentThread = Thread.currentThread(); + int lastGcType = Main.waitForGcToComplete( + XposedHelpers.getLongField(currentThread, "nativePeer")); + if (lastGcType < 0) { + Utils.logW("waitForGcToComplete failed, using fallback"); + Runtime.getRuntime().gc(); + } if (!backupAndHookNative(target, hook, backup)) { throw new RuntimeException("Failed to hook " + target + " with " + hook); } diff --git a/Core/jni/main/java_hook/java_hook.cpp b/Core/jni/main/java_hook/java_hook.cpp index 94747a4e..c24ca1e5 100644 --- a/Core/jni/main/java_hook/java_hook.cpp +++ b/Core/jni/main/java_hook/java_hook.cpp @@ -52,6 +52,12 @@ void resumeAllThreads(JNIEnv *, jclass, jlong obj) { resumeAll(reinterpret_cast(obj)); } +int waitForGcToComplete(JNIEnv *, jclass, jlong thread) { + // if waitGc succeeded, it should return one of enum collector::GcType + int gcType = waitGc(0, reinterpret_cast(thread)); + return gcType; +} + static JNINativeMethod hookMethods[] = { { "init", @@ -99,6 +105,9 @@ static JNINativeMethod hookMethods[] = { }, { "resumeAllThreads", "(J)V", (void *) resumeAllThreads + }, + { + "waitForGcToComplete", "(J)I", (void *) waitForGcToComplete } }; @@ -137,7 +146,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, 13); + env->RegisterNatives(entry_class, hookMethods, 14); 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 4b9e3c7e..21da8699 100644 --- a/Core/jni/main/native_hook/native_hook.cpp +++ b/Core/jni/main/native_hook/native_hook.cpp @@ -28,6 +28,8 @@ static void *(*instrCstBackup)(void *instru) = nullptr; void (*deoptMethod)(void *, void *) = nullptr; +static void (*heapPreForkBackup)(void *) = nullptr; + bool my_runtimeInit(void *runtime, void *mapAddr) { if (!runtimeInitBackup) { LOGE("runtimeInitBackup is null"); @@ -212,17 +214,49 @@ void hookRuntime(int api_level, void *artHandle, void (*hookFun)(void *, void *, } } -void (*suspendAll)(ScopedSuspendAll*, const char*, bool) = nullptr; -void (*resumeAll)(ScopedSuspendAll*) = nullptr; +void (*suspendAll)(ScopedSuspendAll *, const char *, bool) = nullptr; + +void (*resumeAll)(ScopedSuspendAll *) = nullptr; + +int (*waitGcInternal)(void *, int, void *) = nullptr; + +void *heap_ = nullptr; + +int waitGc(int gcCause, void *thread) { + if (!heap_) { + LOGE("heap_ is null"); + return -1; + } + return (*waitGcInternal)(heap_, gcCause, thread); +} + +static void myHeapPreFork(void *heap) { + heap_ = heap; + (*heapPreForkBackup)(heap); +} void getSuspendSyms(int api_level, void *artHandle, void (*hookFun)(void *, void *, void **)) { - if (api_level < ANDROID_N) { - return; + if (api_level >= ANDROID_LOLLIPOP) { + waitGcInternal = reinterpret_cast(dlsym(artHandle, + "_ZN3art2gc4Heap19WaitForGcToCompleteENS0_7GcCauseEPNS_6ThreadE")); + void *heapPreFork = dlsym(artHandle, "_ZN3art2gc4Heap13PreZygoteForkEv"); + if (!heapPreFork) { + LOGE("can't find heapPreFork: %s", dlerror()); + } else { + // a chance to get pointer of the heap + (*hookFun)(heapPreFork, reinterpret_cast(myHeapPreFork), + reinterpret_cast(&heapPreForkBackup)); + LOGI("heapPreFork hooked."); + } + } + + if (api_level >= ANDROID_N) { + suspendAll = reinterpret_cast(dlsym( + artHandle, + "_ZN3art16ScopedSuspendAllC2EPKcb")); + resumeAll = reinterpret_cast(dlsym(artHandle, + "_ZN3art16ScopedSuspendAllD2Ev")); } - suspendAll = reinterpret_cast(dlsym(artHandle, - "_ZN3art16ScopedSuspendAllC2EPKcb")); - resumeAll = reinterpret_cast(dlsym(artHandle, - "_ZN3art16ScopedSuspendAllD2Ev")); } void install_inline_hooks() { diff --git a/Core/jni/main/native_hook/native_hook.h b/Core/jni/main/native_hook/native_hook.h index eedb5e94..0a79f2c2 100644 --- a/Core/jni/main/native_hook/native_hook.h +++ b/Core/jni/main/native_hook/native_hook.h @@ -26,6 +26,8 @@ extern void (*suspendAll)(ScopedSuspendAll *, const char *, bool); extern void (*resumeAll)(ScopedSuspendAll *); +extern int waitGc(int, void *); + void install_inline_hooks(); void deoptimize_method(JNIEnv *env, jclass clazz, jobject method);