Avoid using system methods in callback (#1830)

This commit is contained in:
LoveSy 2022-04-10 16:15:52 +08:00 committed by GitHub
parent 1554ed08a8
commit ec5f7847e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 72 additions and 24 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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

View File

@ -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 {
}