Wait to hook static methods once their trampolines are fixed up
This commit is contained in:
parent
e7cdc25ab1
commit
18bd0a8c0f
|
|
@ -1,9 +1,16 @@
|
||||||
package com.elderdrivers.riru.edxp.art;
|
package com.elderdrivers.riru.edxp.art;
|
||||||
|
|
||||||
|
import com.elderdrivers.riru.common.KeepAll;
|
||||||
|
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
|
|
||||||
public class ClassLinker {
|
import de.robv.android.xposed.XposedBridge;
|
||||||
|
|
||||||
|
public class ClassLinker implements KeepAll {
|
||||||
|
|
||||||
public static native void setEntryPointsToInterpreter(Member method);
|
public static native void setEntryPointsToInterpreter(Member method);
|
||||||
|
|
||||||
|
public static void onPostFixupStaticTrampolines(Class clazz) {
|
||||||
|
XposedBridge.hookPendingMethod(clazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.elderdrivers.riru.edxp.util;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
|
||||||
|
public class ClassUtils {
|
||||||
|
|
||||||
|
public enum ClassStatus {
|
||||||
|
kNotReady(0), // Zero-initialized Class object starts in this state.
|
||||||
|
kRetired(1), // Retired, should not be used. Use the newly cloned one instead.
|
||||||
|
kErrorResolved(2),
|
||||||
|
kErrorUnresolved(3),
|
||||||
|
kIdx(4), // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
|
||||||
|
kLoaded(5), // DEX idx values resolved.
|
||||||
|
kResolving(6), // Just cloned from temporary class object.
|
||||||
|
kResolved(7), // Part of linking.
|
||||||
|
kVerifying(8), // In the process of being verified.
|
||||||
|
kRetryVerificationAtRuntime(9), // Compile time verification failed, retry at runtime.
|
||||||
|
kVerifyingAtRuntime(10), // Retrying verification at runtime.
|
||||||
|
kVerified(11), // Logically part of linking; done pre-init.
|
||||||
|
kSuperclassValidated(12), // Superclass validation part of init done.
|
||||||
|
kInitializing(13), // Class init in progress.
|
||||||
|
kInitialized(14); // Ready to go.
|
||||||
|
|
||||||
|
private final int status;
|
||||||
|
|
||||||
|
ClassStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ClassStatus withValue(int value) {
|
||||||
|
for (ClassStatus status : ClassStatus.values()) {
|
||||||
|
if (status.status == value) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kNotReady;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum class ClassStatus : uint8_t {
|
||||||
|
* kNotReady = 0, // Zero-initialized Class object starts in this state.
|
||||||
|
* kRetired = 1, // Retired, should not be used. Use the newly cloned one instead.
|
||||||
|
* kErrorResolved = 2,
|
||||||
|
* kErrorUnresolved = 3,
|
||||||
|
* kIdx = 4, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
|
||||||
|
* kLoaded = 5, // DEX idx values resolved.
|
||||||
|
* kResolving = 6, // Just cloned from temporary class object.
|
||||||
|
* kResolved = 7, // Part of linking.
|
||||||
|
* kVerifying = 8, // In the process of being verified.
|
||||||
|
* kRetryVerificationAtRuntime = 9, // Compile time verification failed, retry at runtime.
|
||||||
|
* kVerifyingAtRuntime = 10, // Retrying verification at runtime.
|
||||||
|
* kVerified = 11, // Logically part of linking; done pre-init.
|
||||||
|
* kSuperclassValidated = 12, // Superclass validation part of init done.
|
||||||
|
* kInitializing = 13, // Class init in progress.
|
||||||
|
* kInitialized = 14, // Ready to go.
|
||||||
|
* kLast = kInitialized
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
public static ClassStatus getClassStatus(Class clazz) {
|
||||||
|
if (clazz == null) {
|
||||||
|
return ClassStatus.kNotReady;
|
||||||
|
}
|
||||||
|
int status = XposedHelpers.getIntField(clazz, "status");
|
||||||
|
return ClassStatus.withValue((int) (Integer.toUnsignedLong(status) >> (32 - 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isInitialized(Class clazz) {
|
||||||
|
return getClassStatus(clazz) == ClassStatus.kInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014-2015 Marvin Wißfeld
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.elderdrivers.riru.edxp.util;
|
||||||
|
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public final class Unsafe {
|
||||||
|
private static final String TAG = "Unsafe";
|
||||||
|
|
||||||
|
private static Object unsafe;
|
||||||
|
private static Class unsafeClass;
|
||||||
|
|
||||||
|
private static Method arrayBaseOffsetMethod,
|
||||||
|
arrayIndexScaleMethod,
|
||||||
|
getIntMethod,
|
||||||
|
getLongMethod;
|
||||||
|
|
||||||
|
private volatile static boolean supported = false;
|
||||||
|
|
||||||
|
private static Class objectArrayClass = Object[].class;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
theUnsafe.setAccessible(true);
|
||||||
|
unsafe = theUnsafe.get(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
final Field theUnsafe = unsafeClass.getDeclaredField("THE_ONE");
|
||||||
|
theUnsafe.setAccessible(true);
|
||||||
|
unsafe = theUnsafe.get(null);
|
||||||
|
} catch (Exception e2) {
|
||||||
|
Log.w(TAG, "Unsafe not found o.O");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unsafe != null) {
|
||||||
|
try {
|
||||||
|
arrayBaseOffsetMethod = unsafeClass.getDeclaredMethod("arrayBaseOffset", Class.class);
|
||||||
|
arrayIndexScaleMethod = unsafeClass.getDeclaredMethod("arrayIndexScale", Class.class);
|
||||||
|
getIntMethod = unsafeClass.getDeclaredMethod("getInt", Object.class, long.class);
|
||||||
|
getLongMethod = unsafeClass.getDeclaredMethod("getLong", Object.class, long.class);
|
||||||
|
supported = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean support() {
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Unsafe() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static int arrayBaseOffset(Class cls) {
|
||||||
|
try {
|
||||||
|
return (int) arrayBaseOffsetMethod.invoke(unsafe, cls);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static int arrayIndexScale(Class cls) {
|
||||||
|
try {
|
||||||
|
return (int) arrayIndexScaleMethod.invoke(unsafe, cls);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static int getInt(Object array, long offset) {
|
||||||
|
try {
|
||||||
|
return (int) getIntMethod.invoke(unsafe, array, offset);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static long getLong(Object array, long offset) {
|
||||||
|
try {
|
||||||
|
return (long) getLongMethod.invoke(unsafe, array, offset);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getObjectAddress(Object obj) {
|
||||||
|
try {
|
||||||
|
Object[] array = new Object[]{obj};
|
||||||
|
if (arrayIndexScale(objectArrayClass) == 8) {
|
||||||
|
return getLong(array, arrayBaseOffset(objectArrayClass));
|
||||||
|
} else {
|
||||||
|
return 0xffffffffL & getInt(array, arrayBaseOffset(objectArrayClass));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Utils.logE("get object address error", e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
#include <JNIHelper.h>
|
#include <JNIHelper.h>
|
||||||
#include <base/object.h>
|
#include <base/object.h>
|
||||||
|
#include "runtime.h"
|
||||||
|
#include "jni_env_ext.h"
|
||||||
|
#include "edxp_context.h"
|
||||||
|
|
||||||
namespace art {
|
namespace art {
|
||||||
|
|
||||||
|
|
@ -24,6 +27,11 @@ namespace art {
|
||||||
return ConstructorBackup(thiz, intern_table);
|
return ConstructorBackup(thiz, intern_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CREATE_HOOK_STUB_ENTRIES(void, FixupStaticTrampolines, void *thiz, void *clazz_ptr) {
|
||||||
|
FixupStaticTrampolinesBackup(thiz, clazz_ptr);
|
||||||
|
edxp::Context::GetInstance()->CallOnPostFixupStaticTrampolines(clazz_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClassLinker(void *thiz) : HookedObject(thiz) {}
|
ClassLinker(void *thiz) : HookedObject(thiz) {}
|
||||||
|
|
||||||
|
|
@ -35,6 +43,8 @@ namespace art {
|
||||||
HOOK_FUNC(Constructor, "_ZN3art11ClassLinkerC2EPNS_11InternTableE");
|
HOOK_FUNC(Constructor, "_ZN3art11ClassLinkerC2EPNS_11InternTableE");
|
||||||
RETRIEVE_FUNC_SYMBOL(SetEntryPointsToInterpreter,
|
RETRIEVE_FUNC_SYMBOL(SetEntryPointsToInterpreter,
|
||||||
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE");
|
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE");
|
||||||
|
|
||||||
|
HOOK_FUNC(FixupStaticTrampolines, "_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE");
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void SetEntryPointsToInterpreter(void *art_method) const {
|
ALWAYS_INLINE void SetEntryPointsToInterpreter(void *art_method) const {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "base/object.h"
|
||||||
|
|
||||||
|
namespace art {
|
||||||
|
|
||||||
|
class JNIEnvExt : edxp::HookedObject {
|
||||||
|
|
||||||
|
private:
|
||||||
|
CREATE_FUNC_SYMBOL_ENTRY(jobject, NewLocalRef, void *env, void *mirror_ptr) {
|
||||||
|
return NewLocalRefSym(env, mirror_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
JNIEnvExt(void *thiz) : HookedObject(thiz) {}
|
||||||
|
|
||||||
|
static void Setup(void *handle, HookFunType hook_func) {
|
||||||
|
RETRIEVE_FUNC_SYMBOL(NewLocalRef, "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE");
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject NewLocalRefer(void *mirror_ptr) {
|
||||||
|
return NewLocalRef(thiz_, mirror_ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <base/object.h>
|
#include <base/object.h>
|
||||||
|
#include <config_manager.h>
|
||||||
|
|
||||||
namespace art {
|
namespace art {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ namespace edxp {
|
||||||
"/system/framework/eddalvikdx.jar:"
|
"/system/framework/eddalvikdx.jar:"
|
||||||
"/system/framework/eddexmaker.jar";
|
"/system/framework/eddexmaker.jar";
|
||||||
static constexpr const char *kEntryClassName = "com.elderdrivers.riru.edxp.core.Main";
|
static constexpr const char *kEntryClassName = "com.elderdrivers.riru.edxp.core.Main";
|
||||||
|
static constexpr const char *kClassLinkerClassName = "com.elderdrivers.riru.edxp.art.ClassLinker";
|
||||||
static constexpr const char *kSandHookClassName = "com.swift.sandhook.SandHook";
|
static constexpr const char *kSandHookClassName = "com.swift.sandhook.SandHook";
|
||||||
static constexpr const char *kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall";
|
static constexpr const char *kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
namespace edxp {
|
namespace edxp {
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,10 @@
|
||||||
#include <jni/art_class_linker.h>
|
#include <jni/art_class_linker.h>
|
||||||
#include <jni/art_heap.h>
|
#include <jni/art_heap.h>
|
||||||
#include <jni/edxp_yahfa.h>
|
#include <jni/edxp_yahfa.h>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <native_hook.h>
|
|
||||||
#include <jni/framework_zygote.h>
|
#include <jni/framework_zygote.h>
|
||||||
#include <jni/edxp_resources_hook.h>
|
#include <jni/edxp_resources_hook.h>
|
||||||
#include <dl_util.h>
|
#include <dl_util.h>
|
||||||
|
#include <art/runtime/jni_env_ext.h>
|
||||||
#include "edxp_context.h"
|
#include "edxp_context.h"
|
||||||
#include "config_manager.h"
|
#include "config_manager.h"
|
||||||
|
|
||||||
|
|
@ -33,10 +32,21 @@ namespace edxp {
|
||||||
return inject_class_loader_;
|
return inject_class_loader_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Context::CallOnPostFixupStaticTrampolines(void *class_ptr) {
|
||||||
|
if (post_fixup_static_mid_ != nullptr) {
|
||||||
|
JNIEnv *env;
|
||||||
|
vm_->GetEnv((void **) (&env), JNI_VERSION_1_4);
|
||||||
|
art::JNIEnvExt env_ext(env);
|
||||||
|
jobject clazz = env_ext.NewLocalRefer(class_ptr);
|
||||||
|
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Context::LoadDexAndInit(JNIEnv *env, const char *dex_path) {
|
void Context::LoadDexAndInit(JNIEnv *env, const char *dex_path) {
|
||||||
if (LIKELY(initialized_)) {
|
if (LIKELY(initialized_)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
||||||
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
||||||
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
||||||
|
|
@ -55,6 +65,15 @@ namespace edxp {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
inject_class_loader_ = env->NewGlobalRef(my_cl);
|
inject_class_loader_ = env->NewGlobalRef(my_cl);
|
||||||
|
|
||||||
|
// initialize pending methods related
|
||||||
|
env->GetJavaVM(&vm_);
|
||||||
|
class_linker_class_ = (jclass) env->NewGlobalRef(
|
||||||
|
FindClassFromLoader(env, kClassLinkerClassName));
|
||||||
|
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
||||||
|
"onPostFixupStaticTrampolines",
|
||||||
|
"(Ljava/lang/Class;)V");
|
||||||
|
|
||||||
entry_class_ = (jclass) (env->NewGlobalRef(
|
entry_class_ = (jclass) (env->NewGlobalRef(
|
||||||
FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName)));
|
FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName)));
|
||||||
|
|
||||||
|
|
@ -131,6 +150,10 @@ namespace edxp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE JavaVM *Context::GetJavaVM() const {
|
||||||
|
return vm_;
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void Context::SetAppDataDir(jstring app_data_dir) {
|
ALWAYS_INLINE void Context::SetAppDataDir(jstring app_data_dir) {
|
||||||
app_data_dir_ = app_data_dir;
|
app_data_dir_ = app_data_dir;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace edxp {
|
namespace edxp {
|
||||||
|
|
||||||
|
|
@ -14,10 +15,14 @@ namespace edxp {
|
||||||
|
|
||||||
jobject GetCurrentClassLoader() const;
|
jobject GetCurrentClassLoader() const;
|
||||||
|
|
||||||
|
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
||||||
|
|
||||||
void PrepareJavaEnv(JNIEnv *env);
|
void PrepareJavaEnv(JNIEnv *env);
|
||||||
|
|
||||||
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const;
|
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const;
|
||||||
|
|
||||||
|
JavaVM *GetJavaVM() const;
|
||||||
|
|
||||||
void SetAppDataDir(jstring app_data_dir);
|
void SetAppDataDir(jstring app_data_dir);
|
||||||
|
|
||||||
void SetNiceName(jstring nice_name);
|
void SetNiceName(jstring nice_name);
|
||||||
|
|
@ -51,6 +56,9 @@ namespace edxp {
|
||||||
jclass entry_class_ = nullptr;
|
jclass entry_class_ = nullptr;
|
||||||
jstring app_data_dir_ = nullptr;
|
jstring app_data_dir_ = nullptr;
|
||||||
jstring nice_name_ = nullptr;
|
jstring nice_name_ = nullptr;
|
||||||
|
JavaVM *vm_ = nullptr;
|
||||||
|
jclass class_linker_class_ = nullptr;
|
||||||
|
jmethodID post_fixup_static_mid_ = nullptr;
|
||||||
|
|
||||||
Context() {}
|
Context() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace edxp {
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterArtClassLinker(JNIEnv *env) {
|
void RegisterArtClassLinker(JNIEnv *env) {
|
||||||
REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.art.ClassLinker");
|
REGISTER_EDXP_NATIVE_METHODS(kClassLinkerClassName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include <config_manager.h>
|
#include <config_manager.h>
|
||||||
#include <art/runtime/runtime.h>
|
#include <art/runtime/runtime.h>
|
||||||
#include <dl_util.h>
|
#include <dl_util.h>
|
||||||
|
#include <art/runtime/jni_env_ext.h>
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
|
|
@ -55,6 +56,7 @@ namespace edxp {
|
||||||
art::gc::Heap::Setup(art_handle.Get(), hook_func);
|
art::gc::Heap::Setup(art_handle.Get(), hook_func);
|
||||||
art::ClassLinker::Setup(art_handle.Get(), hook_func);
|
art::ClassLinker::Setup(art_handle.Get(), hook_func);
|
||||||
art::mirror::Class::Setup(art_handle.Get(), hook_func);
|
art::mirror::Class::Setup(art_handle.Get(), hook_func);
|
||||||
|
art::JNIEnvExt::Setup(art_handle.Get(), hook_func);
|
||||||
LOGI("Inline hooks installed");
|
LOGI("Inline hooks installed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||||
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
import com.elderdrivers.riru.edxp.util.ClassUtils;
|
||||||
|
import com.elderdrivers.riru.edxp.util.Utils;
|
||||||
|
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -246,15 +248,19 @@ public class DexMakerUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Member findMethodNative(Member hookMethod) {
|
public static Member findMethodNative(Member hookMethod) {
|
||||||
MethodInfo methodInfo = new MethodInfo(hookMethod);
|
if (shouldDelayHook(hookMethod)) {
|
||||||
Class declaringClass = methodInfo.getClassForSure();
|
Utils.logD("solo: " + hookMethod + " hooking delayed.");
|
||||||
Member reflectMethod = (Member) HookMain.findMethod(
|
return null;
|
||||||
declaringClass, methodInfo.methodName, methodInfo.methodSig);
|
|
||||||
if (reflectMethod == null) {
|
|
||||||
DexLog.e("method not found: name="
|
|
||||||
+ methodInfo.methodName + ", sig=" + methodInfo.methodSig);
|
|
||||||
reflectMethod = hookMethod;
|
|
||||||
}
|
}
|
||||||
return reflectMethod;
|
return hookMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldDelayHook(Member hookMethod) {
|
||||||
|
if (hookMethod == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Class declaringClass = hookMethod.getDeclaringClass();
|
||||||
|
return Modifier.isStatic(hookMethod.getModifiers())
|
||||||
|
&& !ClassUtils.isInitialized(declaringClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,6 @@ public class MethodInfo {
|
||||||
methodSig = builder.toString();
|
methodSig = builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class getClassForSure() {
|
|
||||||
try {
|
|
||||||
// TODO does initialize make sense?
|
|
||||||
return Class.forName(className, true, classLoader);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
DexLog.e("error when getClassForSure", throwable);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getDescStr(Class clazz) {
|
public static String getDescStr(Class clazz) {
|
||||||
if (clazz.equals(boolean.class)) {
|
if (clazz.equals(boolean.class)) {
|
||||||
return "Z";
|
return "Z";
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import dalvik.system.InMemoryDexClassLoader;
|
import dalvik.system.InMemoryDexClassLoader;
|
||||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
|
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
|
||||||
|
|
@ -231,12 +232,32 @@ public final class XposedBridge {
|
||||||
|
|
||||||
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
|
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
|
||||||
Member reflectMethod = EdXpConfigGlobal.getHookProvider().findMethodNative(hookMethod);
|
Member reflectMethod = EdXpConfigGlobal.getHookProvider().findMethodNative(hookMethod);
|
||||||
hookMethodNative(reflectMethod, declaringClass, slot, additionalInfo);
|
if (reflectMethod != null) {
|
||||||
|
hookMethodNative(reflectMethod, declaringClass, slot, additionalInfo);
|
||||||
|
} else {
|
||||||
|
synchronized (pendingLock) {
|
||||||
|
sPendingHookMethods.put(hookMethod, additionalInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback.new Unhook(hookMethod);
|
return callback.new Unhook(hookMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>
|
||||||
|
sPendingHookMethods = new ConcurrentHashMap<>();
|
||||||
|
private static final Object pendingLock = new Object();
|
||||||
|
|
||||||
|
public static void hookPendingMethod(Class clazz) {
|
||||||
|
synchronized (pendingLock) {
|
||||||
|
for (Member member : sPendingHookMethods.keySet()) {
|
||||||
|
if (member.getDeclaringClass().equals(clazz)) {
|
||||||
|
hookMethodNative(member, clazz, 0, sPendingHookMethods.get(member));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the callback for a hooked method/constructor.
|
* Removes the callback for a hooked method/constructor.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue