diff --git a/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java b/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java index f8576165..9189c020 100644 --- a/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java +++ b/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java @@ -229,12 +229,6 @@ public abstract class XC_MethodHook extends XCallback { setThrowable(throwable); } - @Nullable - @Override - public Object invokeOrigin(@Nullable Object thisObject, Object[] args) throws InvocationTargetException, IllegalAccessException { - return HookBridge.invokeOriginalMethod((Executable) method, thisObject, args); - } - @Nullable @Override public Object invokeOrigin() throws InvocationTargetException, IllegalAccessException { diff --git a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java index 9e0f3983..e6b86719 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java @@ -968,14 +968,71 @@ public class LSPosedContext extends XposedContext { @Nullable @Override - public Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object[] args) throws InvocationTargetException, IllegalAccessException { + public Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object[] args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { return HookBridge.invokeOriginalMethod(method, thisObject, args); } + private static char getTypeShorty(Class type) { + if (type == int.class) { + return 'I'; + } else if (type == long.class) { + return 'J'; + } else if (type == float.class) { + return 'F'; + } else if (type == double.class) { + return 'D'; + } else if (type == boolean.class) { + return 'Z'; + } else if (type == byte.class) { + return 'B'; + } else if (type == char.class) { + return 'C'; + } else if (type == short.class) { + return 'S'; + } else if (type == void.class) { + return 'V'; + } else { + return 'L'; + } + } + + private static char[] getExecutableShorty(Executable executable) { + var parameterTypes = executable.getParameterTypes(); + var shorty = new char[parameterTypes.length + 1]; + shorty[0] = getTypeShorty(executable instanceof Method ? ((Method) executable).getReturnType() : void.class); + for (int i = 1; i < shorty.length; i++) { + shorty[i] = getTypeShorty(parameterTypes[i - 1]); + } + return shorty; + } + @Nullable @Override - public T newInstanceOrigin(@NonNull Constructor constructor, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException { - return HookBridge.invokeOriginalConstructor(constructor, constructor.getDeclaringClass(), args); + public Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { + if (Modifier.isStatic(method.getModifiers())) { + throw new IllegalArgumentException("Cannot invoke special on static method: " + method); + } + return HookBridge.invokeSpecialMethod(method, getExecutableShorty(method), method.getDeclaringClass(), thisObject, args); + } + + @NonNull + @Override + public T newInstanceOrigin(@NonNull Constructor constructor, Object... args) throws InvocationTargetException, IllegalAccessException, InstantiationException { + var obj = HookBridge.allocateObject(constructor.getDeclaringClass()); + HookBridge.invokeOriginalMethod(constructor, obj, args); + return obj; + } + + @NonNull + @Override + public U newInstanceSpecial(@NonNull Constructor constructor, @NonNull Class subClass, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { + var superClass = constructor.getDeclaringClass(); + if (!superClass.isAssignableFrom(subClass)) { + throw new IllegalArgumentException(subClass + " is not inherited from " + superClass); + } + var obj = HookBridge.allocateObject(subClass); + HookBridge.invokeSpecialMethod(constructor, getExecutableShorty(constructor), superClass, obj, args); + return obj; } @Override diff --git a/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java b/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java index f6a190f4..87d41ebc 100644 --- a/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java +++ b/core/src/main/java/org/lsposed/lspd/nativebridge/HookBridge.java @@ -13,9 +13,11 @@ public class HookBridge { public static native boolean deoptimizeMethod(Executable method); + public static native T allocateObject(Class clazz) throws InstantiationException; + public static native Object invokeOriginalMethod(Executable method, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; - public static native T invokeOriginalConstructor(Constructor method, Class clazz, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; + public static native Object invokeSpecialMethod(Executable method, char[] shorty, Class clazz, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; @FastNative public static native boolean instanceOf(Object obj, Class clazz); diff --git a/core/src/main/jni/src/jni/hook_bridge.cpp b/core/src/main/jni/src/jni/hook_bridge.cpp index fa867c3d..3167d22c 100644 --- a/core/src/main/jni/src/jni/hook_bridge.cpp +++ b/core/src/main/jni/src/jni/hook_bridge.cpp @@ -143,23 +143,113 @@ LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalMethod, jobject hookMet return env->CallObjectMethod(to_call, invoke, thiz, args); } -LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalConstructor, jobject hookConstructor, - jclass cls, jobjectArray args) { - auto target = env->FromReflectedMethod(hookConstructor); - HookItem * hook_item = nullptr; - { - std::shared_lock lk(hooked_lock); - if (auto found = hooked_methods.find(target); found != hooked_methods.end()) { - hook_item = found->second.get(); +LSP_DEF_NATIVE_METHOD(jobject, HookBridge, allocateObject, jclass cls) { + return env->AllocObject(clazz); +} + +LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeSpecialMethod, jobject method, jcharArray shorty, + jclass cls, jobject thiz, jobjectArray args) { + static auto* const get_int = env->GetMethodID(env->FindClass("java/lang/Integer"), "intValue", "()I"); + static auto* const get_double = env->GetMethodID(env->FindClass("java/lang/Double"), "doubleValue", "()D"); + static auto* const get_long = env->GetMethodID(env->FindClass("java/lang/Long"), "longValue", "()J"); + static auto* const get_float = env->GetMethodID(env->FindClass("java/lang/Float"), "floatValue", "()F"); + static auto* const get_short = env->GetMethodID(env->FindClass("java/lang/Short"), "shortValue", "()S"); + static auto* const get_byte = env->GetMethodID(env->FindClass("java/lang/Byte"), "byteValue", "()B"); + static auto* const get_char = env->GetMethodID(env->FindClass("java/lang/Character"), "charValue", "()C"); + static auto* const get_boolean = env->GetMethodID(env->FindClass("java/lang/Boolean"), "booleanValue", "()Z"); + static auto* const set_int = env->GetStaticMethodID(env->FindClass("java/lang/Integer"), "valueOf", "(I)Ljava/lang/Integer;"); + static auto* const set_double = env->GetStaticMethodID(env->FindClass("java/lang/Double"), "valueOf", "(D)Ljava/lang/Double;"); + static auto* const set_long = env->GetStaticMethodID(env->FindClass("java/lang/Long"), "valueOf", "(J)Ljava/lang/Long;"); + static auto* const set_float = env->GetStaticMethodID(env->FindClass("java/lang/Float"), "valueOf", "(F)Ljava/lang/Float;"); + static auto* const set_short = env->GetStaticMethodID(env->FindClass("java/lang/Short"), "valueOf", "(S)Ljava/lang/Short;"); + static auto* const set_byte = env->GetStaticMethodID(env->FindClass("java/lang/Byte"), "valueOf", "(B)Ljava/lang/Byte;"); + static auto* const set_char = env->GetStaticMethodID(env->FindClass("java/lang/Character"), "valueOf", "(C)Ljava/lang/Character;"); + static auto* const set_boolean = env->GetStaticMethodID(env->FindClass("java/lang/Boolean"), "valueOf", "(Z)Ljava/lang/Boolean;"); + + auto target = env->FromReflectedMethod(method); + auto param_len = env->GetArrayLength(shorty) - 1; + if (env->GetArrayLength(args) != param_len) { + env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), "args.length != parameters.length"); + return nullptr; + } + if (thiz == nullptr) { + env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), "this == null"); + return nullptr; + } + std::vector a(param_len); + auto *const shorty_char = env->GetCharArrayElements(shorty, nullptr); + for (jint i = 0; i != param_len; ++i) { + jobject element = nullptr; + switch(shorty_char[i + 1]) { + case 'I': + a[i].i = env->CallIntMethod(element = env->GetObjectArrayElement(args, i), get_int); + break; + case 'D': + a[i].d = env->CallDoubleMethod(element = env->GetObjectArrayElement(args, i), get_double); + break; + case 'J': + a[i].j = env->CallLongMethod(element = env->GetObjectArrayElement(args, i), get_long); + break; + case 'F': + a[i].f = env->CallFloatMethod(element = env->GetObjectArrayElement(args, i), get_float); + break; + case 'S': + a[i].s = env->CallShortMethod(element = env->GetObjectArrayElement(args, i), get_short); + break; + case 'B': + a[i].b = env->CallByteMethod(element = env->GetObjectArrayElement(args, i), get_byte); + break; + case 'C': + a[i].c = env->CallCharMethod(element = env->GetObjectArrayElement(args, i), get_char); + break; + case 'Z': + a[i].z = env->CallBooleanMethod(element = env->GetObjectArrayElement(args, i), get_boolean); + break; + default: + case 'L': + a[i].l = env->GetObjectArrayElement(args, i); + element = nullptr; + break; } + if (element) env->DeleteLocalRef(element); + if (env->ExceptionCheck()) return nullptr; } - jobject to_call = hookConstructor; - if (hook_item) { - std::unique_lock lk(backup_lock); - if (hook_item->backup) to_call = hook_item->backup; + jobject value = nullptr; + switch(shorty_char[0]) { + case 'I': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_int, env->CallNonvirtualIntMethodA(thiz, cls, target, a.data())); + break; + case 'D': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_double, env->CallNonvirtualDoubleMethodA(thiz, cls, target, a.data())); + break; + case 'J': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_long, env->CallNonvirtualLongMethodA(thiz, cls, target, a.data())); + break; + case 'F': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_float, env->CallNonvirtualFloatMethodA(thiz, cls, target, a.data())); + break; + case 'S': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_short, env->CallNonvirtualShortMethodA(thiz, cls, target, a.data())); + break; + case 'B': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_byte, env->CallNonvirtualByteMethodA(thiz, cls, target, a.data())); + break; + case 'C': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_char, env->CallNonvirtualCharMethodA(thiz, cls, target, a.data())); + break; + case 'Z': + value = env->CallStaticObjectMethod(jclass{nullptr}, set_boolean, env->CallNonvirtualBooleanMethodA(thiz, cls, target, a.data())); + break; + case 'L': + value = env->CallNonvirtualObjectMethodA(thiz, cls, target, a.data()); + break; + default: + case 'V': + env->CallNonvirtualVoidMethodA(thiz, cls, target, a.data()); + break; } - auto thiz = env->AllocObject(cls); - return env->CallObjectMethod(to_call, invoke, thiz, args); + env->ReleaseCharArrayElements(shorty, shorty_char, JNI_ABORT); + return value; } LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, instanceOf, jobject object, jclass expected_class) { @@ -199,7 +289,8 @@ static JNINativeMethod gMethods[] = { LSP_NATIVE_METHOD(HookBridge, unhookMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Object;)Z"), LSP_NATIVE_METHOD(HookBridge, deoptimizeMethod, "(Ljava/lang/reflect/Executable;)Z"), LSP_NATIVE_METHOD(HookBridge, invokeOriginalMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), - LSP_NATIVE_METHOD(HookBridge, invokeOriginalConstructor, "(Ljava/lang/reflect/Constructor;Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object;"), + LSP_NATIVE_METHOD(HookBridge, invokeSpecialMethod, "(Ljava/lang/reflect/Executable;[CLjava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), + LSP_NATIVE_METHOD(HookBridge, allocateObject, "(Ljava/lang/Class;)Ljava/lang/Object;"), LSP_NATIVE_METHOD(HookBridge, instanceOf, "(Ljava/lang/Object;Ljava/lang/Class;)Z"), LSP_NATIVE_METHOD(HookBridge, setTrusted, "(Ljava/lang/Object;)Z"), LSP_NATIVE_METHOD(HookBridge, callbackSnapshot, "(Ljava/lang/reflect/Executable;)[Ljava/lang/Object;"),