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 e593064b..83bc6457 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 @@ -20,10 +20,20 @@ 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.XposedInterface; /** * Callback class for method hooks. @@ -93,7 +103,7 @@ public abstract class XC_MethodHook extends XCallback { /** * Wraps information about the method call and allows to influence it. */ - public static final class MethodHookParam extends XCallback.Param { + public static final class MethodHookParam extends XCallback.Param implements XposedInterface.BeforeHookCallback, XposedInterface.AfterHookCallback { /** * @hide */ @@ -121,6 +131,8 @@ public abstract class XC_MethodHook extends XCallback { private Throwable throwable = null; public boolean returnEarly = false; + private final HashMap extras = new HashMap<>(); + /** * Returns the result of the method call. */ @@ -146,6 +158,11 @@ 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. */ @@ -172,6 +189,51 @@ 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; + } + + @Override + public void returnAndSkip(@Nullable Object returnValue) { + setResult(returnValue); + } + + @Override + public void throwAndSkip(@Nullable Throwable throwable) { + 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 U getExtra(@NonNull String key) { + return (U) extras.get(key); + } + + @Override + public void setExtra(@NonNull String key, @Nullable U value) throws ConcurrentModificationException { + extras.put(key, value); + } } /** diff --git a/core/src/main/java/de/robv/android/xposed/XposedBridge.java b/core/src/main/java/de/robv/android/xposed/XposedBridge.java index f72b4cb7..ed722fb5 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/core/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -43,6 +43,7 @@ import java.util.concurrent.CopyOnWriteArraySet; import de.robv.android.xposed.callbacks.XC_InitPackageResources; import de.robv.android.xposed.callbacks.XC_LoadPackage; import io.github.libxposed.XposedInterface; +import io.github.libxposed.XposedModuleInterface; /** * This class contains most of Xposed's central logic, such as initialization and callbacks used by @@ -395,7 +396,7 @@ public final class XposedBridge { } } - public static class AdditionalHookInfo { + public static class AdditionalHookInfo { private final Object params; private AdditionalHookInfo(Executable method) { @@ -416,7 +417,7 @@ public final class XposedBridge { // 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(); + XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam<>(); var array = ((Object[]) params); @@ -451,7 +452,12 @@ public final class XposedBridge { int beforeIdx = 0; do { try { - ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param); + var cb = callbacksSnapshot[beforeIdx]; + if (HookBridge.instanceOf(cb, XC_MethodHook.class)) { + ((XC_MethodHook) cb).beforeHookedMethod(param); + } else if (HookBridge.instanceOf(cb, XposedInterface.BeforeMethodHooker.class)) { + ((XposedInterface.BeforeMethodHooker) cb).before(param); + } } catch (Throwable t) { XposedBridge.log(t); @@ -483,8 +489,13 @@ public final class XposedBridge { Object lastResult = param.getResult(); Throwable lastThrowable = param.getThrowable(); + var cb = callbacksSnapshot[afterIdx]; try { - ((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param); + if (HookBridge.instanceOf(cb, XC_MethodHook.class)) { + ((XC_MethodHook) cb).afterHookedMethod(param); + } else if (HookBridge.instanceOf(cb, XposedInterface.AfterHookCallback.class)) { + ((XposedInterface.AfterMethodHooker) cb).after(param); + } } catch (Throwable t) { XposedBridge.log(t); 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 c4909eb5..8dbc61a4 100644 --- a/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java @@ -1,5 +1,7 @@ package org.lsposed.lspd.impl; +import static de.robv.android.xposed.callbacks.XCallback.PRIORITY_DEFAULT; + import android.app.ActivityThread; import android.app.LoadedApk; import android.content.BroadcastReceiver; @@ -49,6 +51,7 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; +import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; @@ -56,6 +59,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; import io.github.libxposed.XposedContext; @@ -754,76 +759,102 @@ public class LSPosedContext extends XposedContext { return BuildConfig.VERSION_CODE; } - // TODO + private MethodUnhooker doHook(U hookMethod, int priority, T callback) { + 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) { + throw new IllegalArgumentException("callback should not be null!"); + } + + if (HookBridge.hookMethod(hookMethod, XposedBridge.AdditionalHookInfo.class, priority, callback)) { + return new MethodUnhooker<>() { + @NonNull + @Override + public U getOrigin() { + return hookMethod; + } + + @NonNull + @Override + public T getHooker() { + return callback; + } + + @Override + public void unhook() { + HookBridge.unhookMethod(hookMethod, callback); + } + }; + } + log("Cannot hook " + hookMethod); + return null; + } + @Override public MethodUnhooker, Method> hookBefore(@NonNull Method origin, @NonNull BeforeMethodHooker hooker) { - return null; + return doHook(origin, PRIORITY_DEFAULT, hooker); } - // TODO @Override public MethodUnhooker, Method> hookAfter(@NonNull Method origin, @NonNull AfterMethodHooker hooker) { - return null; + return doHook(origin, PRIORITY_DEFAULT, hooker); } - // TODO @Override public MethodUnhooker, Method> hook(@NonNull Method origin, @NonNull MethodHooker hooker) { - return null; + return doHook(origin, PRIORITY_DEFAULT, hooker); } - // TODO @Override public MethodUnhooker, Method> hookBefore(@NonNull Method origin, int priority, @NonNull BeforeMethodHooker hooker) { - return null; + return doHook(origin, priority, hooker); } - // TODO @Override public MethodUnhooker, Method> hookAfter(@NonNull Method origin, int priority, @NonNull AfterMethodHooker hooker) { - return null; + return doHook(origin, priority, hooker); } - // TODO @Override public MethodUnhooker, Method> hook(@NonNull Method origin, int priority, @NonNull MethodHooker hooker) { - return null; + return doHook(origin, priority, hooker); } - // TODO @Override public MethodUnhooker>, Constructor> hookBefore(@NonNull Constructor origin, @NonNull BeforeMethodHooker> hooker) { - return null; + return doHook(origin, PRIORITY_DEFAULT, hooker); } - // TODO @Override public MethodUnhooker>, Constructor> hookAfter(@NonNull Constructor origin, @NonNull AfterMethodHooker> hooker) { - return null; + return doHook(origin, PRIORITY_DEFAULT, hooker); } - // TODO @Override public MethodUnhooker>, Constructor> hook(@NonNull Constructor origin, @NonNull MethodHooker> hooker) { - return null; + return doHook(origin, PRIORITY_DEFAULT, hooker); } // TODO @Override public MethodUnhooker>, Constructor> hookBefore(@NonNull Constructor origin, int priority, @NonNull BeforeMethodHooker> hooker) { - return null; + return doHook(origin, priority, hooker); } - // TODO @Override public MethodUnhooker>, Constructor> hookAfter(@NonNull Constructor origin, int priority, @NonNull AfterMethodHooker> hooker) { - return null; + return doHook(origin, priority, hooker); } - // TODO @Override public MethodUnhooker>, Constructor> hook(@NonNull Constructor origin, int priority, @NonNull MethodHooker> hooker) { - return null; + return doHook(origin, priority, hooker); } private static boolean deoptimize(@NonNull Executable method) { @@ -845,11 +876,10 @@ public class LSPosedContext extends XposedContext { return deoptimize((Executable) constructor); } - // TODO @Nullable @Override public XposedUtils getUtils() { - return null; + return new LSPosedUtils(this); } @Override diff --git a/core/src/main/java/org/lsposed/lspd/impl/LSPosedUtils.java b/core/src/main/java/org/lsposed/lspd/impl/LSPosedUtils.java new file mode 100644 index 00000000..797707f5 --- /dev/null +++ b/core/src/main/java/org/lsposed/lspd/impl/LSPosedUtils.java @@ -0,0 +1,38 @@ +package org.lsposed.lspd.impl; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import io.github.libxposed.XposedUtils; + +public class LSPosedUtils implements XposedUtils { + private final LSPosedContext context; + + LSPosedUtils(LSPosedContext context) { + this.context = context; + } + + @NonNull + @Override + public Class classByName(@NonNull String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { + throw new AbstractMethodError(); + } + + @Nullable + @Override + public Class classOrNullByName(@NonNull String className, @Nullable ClassLoader classLoader) { + throw new AbstractMethodError(); + } + + @NonNull + @Override + public Class classByBinaryName(@NonNull String binaryClassName, @Nullable ClassLoader classLoader) throws ClassNotFoundException { + throw new AbstractMethodError(); + } + + @Nullable + @Override + public Class classOrNullByBinaryName(@NonNull String binaryClassName, @Nullable ClassLoader classLoader) { + throw new AbstractMethodError(); + } +} diff --git a/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java b/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java index 6f9a05cb..d05559a8 100644 --- a/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java +++ b/libxposed/api/src/main/java/io/github/libxposed/XposedInterface.java @@ -4,6 +4,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ConcurrentModificationException; @@ -25,7 +26,7 @@ public interface XposedInterface { void throwAndSkip(@Nullable Throwable throwable); @Nullable - Object invokeOrigin(@Nullable Object thisObject, Object[] args); + Object invokeOrigin(@Nullable Object thisObject, Object[] args) throws InvocationTargetException, IllegalAccessException; void setExtra(@NonNull String key, @Nullable U value) throws ConcurrentModificationException; } @@ -53,7 +54,7 @@ public interface XposedInterface { void setThrowable(@Nullable Throwable throwable); @Nullable - Object invokeOrigin(@Nullable Object thisObject, Object[] args); + Object invokeOrigin(@Nullable Object thisObject, Object[] args) throws InvocationTargetException, IllegalAccessException; @Nullable U getExtra(@NonNull String key);