Implement invoke special and new instance special
This commit is contained in:
parent
54483ab89b
commit
4444825e3e
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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> T newInstanceOrigin(@NonNull Constructor<T> 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> T newInstanceOrigin(@NonNull Constructor<T> constructor, Object... args) throws InvocationTargetException, IllegalAccessException, InstantiationException {
|
||||
var obj = HookBridge.allocateObject(constructor.getDeclaringClass());
|
||||
HookBridge.invokeOriginalMethod(constructor, obj, args);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T, U extends T> U newInstanceSpecial(@NonNull Constructor<T> constructor, @NonNull Class<U> 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
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ public class HookBridge {
|
|||
|
||||
public static native boolean deoptimizeMethod(Executable method);
|
||||
|
||||
public static native <T> T allocateObject(Class<T> clazz) throws InstantiationException;
|
||||
|
||||
public static native Object invokeOriginalMethod(Executable method, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
public static native <T> T invokeOriginalConstructor(Constructor<T> method, Class<T> clazz, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
||||
public static native <T> Object invokeSpecialMethod(Executable method, char[] shorty, Class<T> clazz, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
@FastNative
|
||||
public static native boolean instanceOf(Object obj, Class<?> clazz);
|
||||
|
|
|
|||
|
|
@ -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<jvalue> 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;"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue