Avoid using system methods in callback (#1830)
This commit is contained in:
parent
1554ed08a8
commit
ec5f7847e9
|
|
@ -168,7 +168,7 @@ public final class XposedBridge {
|
|||
*/
|
||||
public static void deoptimizeMethod(Member deoptimizedMethod) {
|
||||
if (!(deoptimizedMethod instanceof Executable)) {
|
||||
throw new IllegalArgumentException("Only methods and constructors can be deoptimized: " + deoptimizedMethod.toString());
|
||||
throw new IllegalArgumentException("Only methods and constructors can be deoptimized: " + deoptimizedMethod);
|
||||
} else if (Modifier.isAbstract(deoptimizedMethod.getModifiers())) {
|
||||
throw new IllegalArgumentException("Cannot deoptimize abstract methods: " + deoptimizedMethod);
|
||||
} else if (Proxy.isProxyClass(deoptimizedMethod.getDeclaringClass())) {
|
||||
|
|
@ -193,16 +193,13 @@ public final class XposedBridge {
|
|||
*/
|
||||
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
|
||||
if (!(hookMethod instanceof Executable)) {
|
||||
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
|
||||
}
|
||||
// No check interface because there may be default methods
|
||||
/*else if (hookMethod.getDeclaringClass().isInterface()) {
|
||||
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
|
||||
}*/
|
||||
else if (Modifier.isAbstract(hookMethod.getModifiers())) {
|
||||
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
|
||||
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod);
|
||||
} else if (Modifier.isAbstract(hookMethod.getModifiers())) {
|
||||
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod);
|
||||
} else if (hookMethod.getDeclaringClass().getClassLoader() == XposedBridge.class.getClassLoader()) {
|
||||
throw new IllegalArgumentException("Do not allow hooking inner methods");
|
||||
} else if (hookMethod.getDeclaringClass() == Method.class && hookMethod.getName().equals("invoke")) {
|
||||
throw new IllegalArgumentException("Cannot hook Method.invoke");
|
||||
}
|
||||
|
||||
if (callback == null) {
|
||||
|
|
@ -383,26 +380,51 @@ public final class XposedBridge {
|
|||
}
|
||||
|
||||
public static class AdditionalHookInfo {
|
||||
final Executable method;
|
||||
final Object[][] callbacks;
|
||||
private static final ClassCastException castException = new ClassCastException("Return value's type from hook callback does not match the hooked method");
|
||||
private static final Method getCause;
|
||||
private final Executable method;
|
||||
private final Object[][] callbacks;
|
||||
private final Class<?> returnType;
|
||||
private final boolean isStatic;
|
||||
|
||||
static {
|
||||
Method tmp;
|
||||
try {
|
||||
tmp = InvocationTargetException.class.getMethod("getCause");
|
||||
} catch (Throwable e) {
|
||||
tmp = null;
|
||||
}
|
||||
getCause = tmp;
|
||||
}
|
||||
|
||||
private AdditionalHookInfo(Executable method, Object[][] callbacks) {
|
||||
this.method = method;
|
||||
isStatic = Modifier.isStatic(method.getModifiers());
|
||||
if (method instanceof Method) {
|
||||
returnType = ((Method) method).getReturnType();
|
||||
} else {
|
||||
returnType = null;
|
||||
}
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
// This method is quite critical. We should try not to use system methods to avoid
|
||||
// endless recursive
|
||||
public Object callback(Object[] args) throws Throwable {
|
||||
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||
|
||||
param.method = method;
|
||||
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
if (isStatic) {
|
||||
param.thisObject = null;
|
||||
param.args = args;
|
||||
} else {
|
||||
param.thisObject = args[0];
|
||||
param.args = new Object[args.length - 1];
|
||||
System.arraycopy(args, 1, param.args, 0, args.length - 1);
|
||||
//noinspection ManualArrayCopy
|
||||
for (int i = 0; i < args.length - 1; ++i) {
|
||||
param.args[i] = args[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
Object[] callbacksSnapshot = callbacks[0];
|
||||
|
|
@ -411,7 +433,7 @@ public final class XposedBridge {
|
|||
try {
|
||||
return HookBridge.invokeOriginalMethod(method, param.thisObject, param.args);
|
||||
} catch (InvocationTargetException ite) {
|
||||
throw ite.getCause();
|
||||
throw (Throwable) HookBridge.invokeOriginalMethod(getCause, ite);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +463,7 @@ public final class XposedBridge {
|
|||
try {
|
||||
param.setResult(HookBridge.invokeOriginalMethod(method, param.thisObject, param.args));
|
||||
} catch (InvocationTargetException e) {
|
||||
param.setThrowable(e.getCause());
|
||||
param.setThrowable((Throwable) HookBridge.invokeOriginalMethod(getCause, e));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -469,10 +491,8 @@ public final class XposedBridge {
|
|||
throw param.getThrowable();
|
||||
else {
|
||||
var result = param.getResult();
|
||||
if (method instanceof Method) {
|
||||
var returnType = ((Method) method).getReturnType();
|
||||
if (!returnType.isPrimitive())
|
||||
return returnType.cast(result);
|
||||
if (returnType != null && !returnType.isPrimitive() && !HookBridge.instanceOf(result, returnType)) {
|
||||
throw castException;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,17 @@ package org.lsposed.lspd.nativebridge;
|
|||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import dalvik.annotation.optimization.FastNative;
|
||||
|
||||
public class HookBridge {
|
||||
public static native boolean hookMethod(Executable hookMethod, Class<?> hooker, int priority, Object callback);
|
||||
public static native boolean unhookMethod(Executable hookMethod, Object callback);
|
||||
|
||||
public static native boolean deoptimizeMethod(Executable method);
|
||||
|
||||
public static native Object invokeOriginalMethod(Executable method, Object thisObject, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
||||
@FastNative
|
||||
public static native Object invokeOriginalMethod(Executable method, Object thisObject, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
|
||||
|
||||
@FastNative
|
||||
public static native boolean instanceOf(Object obj, Class<?> clazz);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import android.content.res.XResources;
|
|||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import dalvik.annotation.optimization.FastNative;
|
||||
|
||||
public class ResourcesHook {
|
||||
|
||||
public static native boolean initXResourcesNative();
|
||||
|
|
@ -33,5 +35,6 @@ public class ResourcesHook {
|
|||
|
||||
public static native ClassLoader buildDummyClassLoader(ClassLoader parent, String resourceSuperClass, String typedArraySuperClass);
|
||||
|
||||
@FastNative
|
||||
public static native void rewriteXmlReferencesNative(long parserPtr, XResources origRes, Resources repRes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ std::shared_mutex hooked_lock;
|
|||
// Rehashing invalidates iterators, changes ordering between elements, and changes which buckets elements appear in, but does not invalidate pointers or references to elements.
|
||||
std::unordered_map<jmethodID, HookItem> hooked_methods;
|
||||
|
||||
jmethodID invoke = nullptr;
|
||||
jobject (*native_invoke)(JNIEnv* env, jobject javaMethod, jobject javaReceiver,
|
||||
jobjectArray javaArgs);
|
||||
}
|
||||
|
||||
namespace lspd {
|
||||
|
|
@ -166,7 +167,11 @@ LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalMethod, jobject hookMet
|
|||
if (hook_item && hook_item->backup) {
|
||||
to_call = hook_item->backup;
|
||||
}
|
||||
return env->CallObjectMethod(to_call, invoke, thiz, args);
|
||||
return native_invoke(env, to_call, thiz, args);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, instanceOf, jobject object, jclass expected_class) {
|
||||
return env->IsInstanceOf(object, expected_class);
|
||||
}
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
|
|
@ -174,13 +179,16 @@ 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, instanceOf, "(Ljava/lang/Object;Ljava/lang/Class;)Z"),
|
||||
};
|
||||
|
||||
void RegisterHookBridge(JNIEnv *env) {
|
||||
auto method = env->FindClass("java/lang/reflect/Method");
|
||||
invoke = env->GetMethodID(
|
||||
auto invoke = env->GetMethodID(
|
||||
method, "invoke",
|
||||
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
native_invoke = reinterpret_cast<decltype(native_invoke)>(
|
||||
lsplant::GetNativeFunction(env, env->ToReflectedMethod(method, invoke, false)));
|
||||
env->DeleteLocalRef(method);
|
||||
REGISTER_LSP_NATIVE_METHODS(HookBridge);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package dalvik.annotation.optimization;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface FastNative {
|
||||
}
|
||||
Loading…
Reference in New Issue