New hook API (#2644)
This commit is contained in:
parent
bd7a9b91b2
commit
bee7938002
|
|
@ -42,9 +42,6 @@
|
|||
getFrameworkVersion(...);
|
||||
getFrameworkVersionCode(...);
|
||||
getFrameworkPrivilege(...);
|
||||
featuredMethod(...);
|
||||
hookBefore(...);
|
||||
hookAfter(...);
|
||||
hook(...);
|
||||
deoptimize(...);
|
||||
invokeOrigin(...);
|
||||
|
|
|
|||
|
|
@ -20,20 +20,12 @@
|
|||
|
||||
package de.robv.android.xposed;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.lsposed.lspd.nativebridge.HookBridge;
|
||||
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import de.robv.android.xposed.callbacks.IXUnhook;
|
||||
import de.robv.android.xposed.callbacks.XCallback;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
|
||||
/**
|
||||
* Callback class for method hooks.
|
||||
|
|
@ -103,7 +95,7 @@ public abstract class XC_MethodHook extends XCallback {
|
|||
/**
|
||||
* Wraps information about the method call and allows to influence it.
|
||||
*/
|
||||
public static final class MethodHookParam <T extends Executable> extends XCallback.Param implements XposedInterface.BeforeHookCallback<T>, XposedInterface.AfterHookCallback<T> {
|
||||
public static final class MethodHookParam<T extends Executable> extends XCallback.Param {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
|
@ -158,11 +150,6 @@ public abstract class XC_MethodHook extends XCallback {
|
|||
return throwable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipped() {
|
||||
return returnEarly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an exception was thrown by the method.
|
||||
*/
|
||||
|
|
@ -189,62 +176,6 @@ public abstract class XC_MethodHook extends XCallback {
|
|||
throw throwable;
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public T getOrigin() {
|
||||
return (T) method;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getThis() {
|
||||
return thisObject;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <U> U getArg(int index) {
|
||||
return (U) args[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> void setArg(int index, U value) {
|
||||
args[index] = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnAndSkip(@Nullable Object returnValue) {
|
||||
setResult(returnValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void throwAndSkip(@Nullable Throwable throwable) {
|
||||
setThrowable(throwable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object invokeOrigin() throws InvocationTargetException, IllegalAccessException {
|
||||
return HookBridge.invokeOriginalMethod((Executable) method, thisObject, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <U> U getExtra(@NonNull String key) {
|
||||
return (U) extras.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> void setExtra(@NonNull String key, @Nullable U value) throws ConcurrentModificationException {
|
||||
extras.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@ import java.lang.reflect.Member;
|
|||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
|
|
@ -219,7 +221,7 @@ public final class XposedBridge {
|
|||
throw new IllegalArgumentException("callback should not be null!");
|
||||
}
|
||||
|
||||
if (!HookBridge.hookMethod((Executable) hookMethod, AdditionalHookInfo.class, callback.priority, callback)) {
|
||||
if (!HookBridge.hookMethod(false, (Executable) hookMethod, AdditionalHookInfo.class, callback.priority, callback)) {
|
||||
log("Failed to hook " + hookMethod);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -238,7 +240,7 @@ public final class XposedBridge {
|
|||
@Deprecated
|
||||
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
|
||||
if (hookMethod instanceof Executable) {
|
||||
HookBridge.unhookMethod((Executable) hookMethod, callback);
|
||||
HookBridge.unhookMethod(false, (Executable) hookMethod, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +440,7 @@ public final class XposedBridge {
|
|||
}
|
||||
}
|
||||
|
||||
Object[] callbacksSnapshot = HookBridge.callbackSnapshot(method);
|
||||
Object[] callbacksSnapshot = HookBridge.callbackSnapshot(HookerCallback.class, method);
|
||||
if (callbacksSnapshot == null || callbacksSnapshot.length == 0) {
|
||||
try {
|
||||
return HookBridge.invokeOriginalMethod(method, param.thisObject, param.args);
|
||||
|
|
@ -446,6 +448,7 @@ public final class XposedBridge {
|
|||
throw (Throwable) HookBridge.invokeOriginalMethod(getCause, ite);
|
||||
}
|
||||
}
|
||||
Queue<Object> extras = new ArrayDeque<>(callbacksSnapshot.length);
|
||||
|
||||
// call "before method" callbacks
|
||||
int beforeIdx = 0;
|
||||
|
|
@ -454,8 +457,9 @@ public final class XposedBridge {
|
|||
var cb = callbacksSnapshot[beforeIdx];
|
||||
if (HookBridge.instanceOf(cb, XC_MethodHook.class)) {
|
||||
((XC_MethodHook) cb).beforeHookedMethod(param);
|
||||
} else if (HookBridge.instanceOf(cb, XposedInterface.BeforeHooker.class)) {
|
||||
((XposedInterface.BeforeHooker<T>) cb).before(param);
|
||||
} else if (HookBridge.instanceOf(cb, HookerCallback.class)) {
|
||||
var hooker = (HookerCallback) cb;
|
||||
extras.add(hooker.beforeInvocation.invoke(null, method, param.thisObject, param.args));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
XposedBridge.log(t);
|
||||
|
|
@ -492,8 +496,9 @@ public final class XposedBridge {
|
|||
try {
|
||||
if (HookBridge.instanceOf(cb, XC_MethodHook.class)) {
|
||||
((XC_MethodHook) cb).afterHookedMethod(param);
|
||||
} else if (HookBridge.instanceOf(cb, XposedInterface.AfterHooker.class)) {
|
||||
((XposedInterface.AfterHooker<T>) cb).after(param);
|
||||
} else if (HookBridge.instanceOf(cb, HookerCallback.class)) {
|
||||
var hooker = (HookerCallback) cb;
|
||||
hooker.afterInvocation.invoke(null, extras.poll(), lastResult);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
XposedBridge.log(t);
|
||||
|
|
@ -518,4 +523,14 @@ public final class XposedBridge {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class HookerCallback {
|
||||
Method beforeInvocation;
|
||||
Method afterInvocation;
|
||||
|
||||
public HookerCallback(Method beforeInvocation, Method afterInvocation) {
|
||||
this.beforeInvocation = beforeInvocation;
|
||||
this.afterInvocation = afterInvocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,10 +55,12 @@ import java.io.InputStream;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
|
@ -66,8 +68,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.libxposed.api.XposedContext;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.XposedModule;
|
||||
import io.github.libxposed.api.XposedModuleInterface;
|
||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||
import io.github.libxposed.api.annotations.BeforeInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
import io.github.libxposed.api.errors.HookFailedError;
|
||||
import io.github.libxposed.api.utils.DexParser;
|
||||
|
||||
|
|
@ -862,41 +868,68 @@ public class LSPosedContext extends XposedContext {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object featuredMethod(String name, Object... args) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private <T, U extends Executable> MethodUnhooker<T, U> doHook(U hookMethod, int priority, T callback) {
|
||||
private <T extends Executable> MethodUnhooker<T> doHook(T hookMethod, int priority, Class<? extends Hooker> hooker) {
|
||||
if (Modifier.isAbstract(hookMethod.getModifiers())) {
|
||||
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod);
|
||||
} else if (hookMethod.getDeclaringClass().getClassLoader() == LSPosedContext.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) {
|
||||
} else if (hooker == null) {
|
||||
throw new IllegalArgumentException("hooker should not be null!");
|
||||
} else if (hooker.getAnnotation(XposedHooker.class) == null) {
|
||||
throw new IllegalArgumentException("Hooker should be annotated with @XposedHooker");
|
||||
}
|
||||
|
||||
if (HookBridge.hookMethod(hookMethod, XposedBridge.AdditionalHookInfo.class, priority, callback)) {
|
||||
Method beforeInvocation = null, afterInvocation = null;
|
||||
var modifiers = Modifier.PUBLIC | Modifier.STATIC;
|
||||
for (var method : hooker.getDeclaredMethods()) {
|
||||
if (method.getAnnotation(BeforeInvocation.class) != null) {
|
||||
if (beforeInvocation != null) {
|
||||
throw new IllegalArgumentException("More than one method annotated with @BeforeInvocation");
|
||||
}
|
||||
boolean valid;
|
||||
valid = (method.getModifiers() & modifiers) == modifiers;
|
||||
valid &= Arrays.equals(method.getParameterTypes(), new Class[]{Member.class, Object.class, Object[].class});
|
||||
valid &= method.getReturnType().equals(Hooker.class);
|
||||
if (!valid) {
|
||||
throw new IllegalArgumentException("BeforeInvocation method format is invalid");
|
||||
}
|
||||
beforeInvocation = method;
|
||||
}
|
||||
if (method.getAnnotation(AfterInvocation.class) != null) {
|
||||
if (afterInvocation != null) {
|
||||
throw new IllegalArgumentException("More than one method annotated with @AfterInvocation");
|
||||
}
|
||||
boolean valid;
|
||||
valid = (method.getModifiers() & modifiers) == modifiers;
|
||||
valid &= Arrays.equals(method.getParameterTypes(), new Class[]{Hooker.class, Object.class});
|
||||
valid &= method.getReturnType().equals(void.class);
|
||||
if (!valid) {
|
||||
throw new IllegalArgumentException("AfterInvocation method format is invalid");
|
||||
}
|
||||
afterInvocation = method;
|
||||
}
|
||||
}
|
||||
if (beforeInvocation == null) {
|
||||
throw new IllegalArgumentException("No method annotated with @BeforeInvocation");
|
||||
}
|
||||
if (afterInvocation == null) {
|
||||
throw new IllegalArgumentException("No method annotated with @AfterInvocation");
|
||||
}
|
||||
|
||||
var callback = new XposedBridge.HookerCallback(beforeInvocation, afterInvocation);
|
||||
if (HookBridge.hookMethod(true, hookMethod, XposedBridge.AdditionalHookInfo.class, priority, callback)) {
|
||||
return new MethodUnhooker<>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public U getOrigin() {
|
||||
public T getOrigin() {
|
||||
return hookMethod;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public T getHooker() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unhook() {
|
||||
HookBridge.unhookMethod(hookMethod, callback);
|
||||
HookBridge.unhookMethod(true, hookMethod, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -905,73 +938,25 @@ public class LSPosedContext extends XposedContext {
|
|||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<BeforeHooker<Method>, Method> hookBefore(@NonNull Method origin, @NonNull BeforeHooker<Method> hooker) {
|
||||
public MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<AfterHooker<Method>, Method> hookAfter(@NonNull Method origin, @NonNull AfterHooker<Method> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<Hooker<Method>, Method> hook(@NonNull Method origin, @NonNull Hooker<Method> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<BeforeHooker<Method>, Method> hookBefore(@NonNull Method origin, int priority, @NonNull BeforeHooker<Method> hooker) {
|
||||
public MethodUnhooker<Method> hook(@NonNull Method origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<AfterHooker<Method>, Method> hookAfter(@NonNull Method origin, int priority, @NonNull AfterHooker<Method> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<Hooker<Method>, Method> hook(@NonNull Method origin, int priority, @NonNull Hooker<Method> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<BeforeHooker<Constructor<T>>, Constructor<T>> hookBefore(@NonNull Constructor<T> origin, @NonNull BeforeHooker<Constructor<T>> hooker) {
|
||||
public <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<AfterHooker<Constructor<T>>, Constructor<T>> hookAfter(@NonNull Constructor<T> origin, @NonNull AfterHooker<Constructor<T>> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<Hooker<Constructor<T>>, Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Hooker<Constructor<T>> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<BeforeHooker<Constructor<T>>, Constructor<T>> hookBefore(@NonNull Constructor<T> origin, int priority, @NonNull BeforeHooker<Constructor<T>> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<AfterHooker<Constructor<T>>, Constructor<T>> hookAfter(@NonNull Constructor<T> origin, int priority, @NonNull AfterHooker<Constructor<T>> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<Hooker<Constructor<T>>, Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Hooker<Constructor<T>> hooker) {
|
||||
public <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ 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 hookMethod(boolean useModernApi, Executable hookMethod, Class<?> hooker, int priority, Object callback);
|
||||
|
||||
public static native boolean unhookMethod(Executable hookMethod, Object callback);
|
||||
public static native boolean unhookMethod(boolean useModernApi, Executable hookMethod, Object callback);
|
||||
|
||||
public static native boolean deoptimizeMethod(Executable method);
|
||||
|
||||
|
|
@ -24,5 +24,5 @@ public class HookBridge {
|
|||
@FastNative
|
||||
public static native boolean setTrusted(Object cookie);
|
||||
|
||||
public static native Object[] callbackSnapshot(Executable method);
|
||||
public static native Object[] callbackSnapshot(Class<?> hooker_callback, Executable method);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,9 +29,19 @@
|
|||
using namespace lsplant;
|
||||
|
||||
namespace {
|
||||
struct CallbackType {
|
||||
bool use_modern_api;
|
||||
union {
|
||||
jobject callback_object;
|
||||
struct {
|
||||
jmethodID before_method;
|
||||
jmethodID after_method;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct HookItem {
|
||||
std::multimap<jint, jobject, std::greater<>> callbacks {};
|
||||
std::multimap<jint, CallbackType, std::greater<>> callbacks;
|
||||
private:
|
||||
std::atomic<jobject> backup {nullptr};
|
||||
static_assert(decltype(backup)::is_always_lock_free);
|
||||
|
|
@ -57,10 +67,13 @@ std::shared_mutex hooked_lock;
|
|||
absl::flat_hash_map<jmethodID, std::unique_ptr<HookItem>> hooked_methods;
|
||||
|
||||
jmethodID invoke = nullptr;
|
||||
jmethodID callback_ctor = nullptr;
|
||||
jfieldID before_method_field = nullptr;
|
||||
jfieldID after_method_field = nullptr;
|
||||
}
|
||||
|
||||
namespace lspd {
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jobject hookMethod,
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jboolean useModernApi, jobject hookMethod,
|
||||
jclass hooker, jint priority, jobject callback) {
|
||||
bool newHook = false;
|
||||
#ifndef NDEBUG
|
||||
|
|
@ -108,11 +121,32 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jobject hookMethod,
|
|||
jobject backup = hook_item->GetBackup();
|
||||
if (!backup) return JNI_FALSE;
|
||||
JNIMonitor monitor(env, backup);
|
||||
hook_item->callbacks.emplace(std::make_pair(priority, env->NewGlobalRef(callback)));
|
||||
if (useModernApi) {
|
||||
if (before_method_field == nullptr) {
|
||||
auto callback_class = JNI_GetObjectClass(env, callback);
|
||||
callback_ctor = JNI_GetMethodID(env, callback_class, "<init>", "(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V");
|
||||
before_method_field = JNI_GetFieldID(env, callback_class, "beforeInvocation", "Ljava/lang/reflect/Method;");
|
||||
after_method_field = JNI_GetFieldID(env, callback_class, "afterInvocation", "Ljava/lang/reflect/Method;");
|
||||
}
|
||||
auto before_method = JNI_GetObjectField(env, callback, before_method_field);
|
||||
auto after_method = JNI_GetObjectField(env, callback, after_method_field);
|
||||
auto callback_type = CallbackType {
|
||||
.use_modern_api = true,
|
||||
.before_method = env->FromReflectedMethod(before_method),
|
||||
.after_method = env->FromReflectedMethod(after_method),
|
||||
};
|
||||
hook_item->callbacks.emplace(std::make_pair(priority, callback_type));
|
||||
} else {
|
||||
auto callback_type = CallbackType {
|
||||
.use_modern_api = false,
|
||||
.callback_object = env->NewGlobalRef(callback),
|
||||
};
|
||||
hook_item->callbacks.emplace(std::make_pair(priority, callback_type));
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jobject hookMethod, jobject callback) {
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jboolean useModernApi, jobject hookMethod, jobject callback) {
|
||||
auto target = env->FromReflectedMethod(hookMethod);
|
||||
HookItem * hook_item = nullptr;
|
||||
{
|
||||
|
|
@ -125,10 +159,22 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jobject hookMethod, jo
|
|||
jobject backup = hook_item->GetBackup();
|
||||
if (!backup) return JNI_FALSE;
|
||||
JNIMonitor monitor(env, backup);
|
||||
jmethodID before = nullptr;
|
||||
if (useModernApi) {
|
||||
auto before_method = JNI_GetObjectField(env, callback, before_method_field);
|
||||
before = env->FromReflectedMethod(before_method);
|
||||
}
|
||||
for (auto i = hook_item->callbacks.begin(); i != hook_item->callbacks.end(); ++i) {
|
||||
if (env->IsSameObject(i->second, callback)) {
|
||||
hook_item->callbacks.erase(i);
|
||||
return JNI_TRUE;
|
||||
if (useModernApi) {
|
||||
if (i->second.use_modern_api && before == i->second.before_method) {
|
||||
hook_item->callbacks.erase(i);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
} else {
|
||||
if (!i->second.use_modern_api && env->IsSameObject(i->second.callback_object, callback)) {
|
||||
hook_item->callbacks.erase(i);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JNI_FALSE;
|
||||
|
|
@ -153,7 +199,7 @@ LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalMethod, jobject hookMet
|
|||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jobject, HookBridge, allocateObject, jclass cls) {
|
||||
return env->AllocObject(clazz);
|
||||
return env->AllocObject(cls);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeSpecialMethod, jobject method, jcharArray shorty,
|
||||
|
|
@ -269,7 +315,7 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, setTrusted, jobject cookie) {
|
|||
return lsplant::MakeDexFileTrusted(env, cookie);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jobjectArray, HookBridge, callbackSnapshot, jobject method) {
|
||||
LSP_DEF_NATIVE_METHOD(jobjectArray, HookBridge, callbackSnapshot, jclass callback_class, jobject method) {
|
||||
auto target = env->FromReflectedMethod(method);
|
||||
HookItem *hook_item = nullptr;
|
||||
{
|
||||
|
|
@ -284,21 +330,28 @@ LSP_DEF_NATIVE_METHOD(jobjectArray, HookBridge, callbackSnapshot, jobject method
|
|||
JNIMonitor monitor(env, backup);
|
||||
auto res = env->NewObjectArray((jsize) hook_item->callbacks.size(), env->FindClass("java/lang/Object"), nullptr);
|
||||
for (jsize i = 0; auto callback: hook_item->callbacks) {
|
||||
env->SetObjectArrayElement(res, i++, env->NewLocalRef(callback.second));
|
||||
if (callback.second.use_modern_api) {
|
||||
auto before_method = JNI_ToReflectedMethod(env, clazz, callback.second.before_method, JNI_TRUE);
|
||||
auto after_method = JNI_ToReflectedMethod(env, clazz, callback.second.after_method, JNI_TRUE);
|
||||
auto callback_object = JNI_NewObject(env, callback_class, callback_ctor, before_method, after_method);
|
||||
env->SetObjectArrayElement(res, i++, env->NewLocalRef(callback_object));
|
||||
} else {
|
||||
env->SetObjectArrayElement(res, i++, env->NewLocalRef(callback.second.callback_object));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
LSP_NATIVE_METHOD(HookBridge, hookMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Class;ILjava/lang/Object;)Z"),
|
||||
LSP_NATIVE_METHOD(HookBridge, unhookMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Object;)Z"),
|
||||
LSP_NATIVE_METHOD(HookBridge, hookMethod, "(ZLjava/lang/reflect/Executable;Ljava/lang/Class;ILjava/lang/Object;)Z"),
|
||||
LSP_NATIVE_METHOD(HookBridge, unhookMethod, "(ZLjava/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, 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;"),
|
||||
LSP_NATIVE_METHOD(HookBridge, callbackSnapshot, "(Ljava/lang/Class;Ljava/lang/reflect/Executable;)[Ljava/lang/Object;"),
|
||||
};
|
||||
|
||||
void RegisterHookBridge(JNIEnv *env) {
|
||||
|
|
|
|||
|
|
@ -149,11 +149,6 @@ public class LSPModuleService extends IXposedService.Stub {
|
|||
return IXposedService.FRAMEWORK_PRIVILEGE_ROOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle featuredMethod(String name, Bundle args) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getScope() throws RemoteException {
|
||||
ensureModule();
|
||||
|
|
|
|||
Loading…
Reference in New Issue