Implement hook APIs
This commit is contained in:
parent
06c65a5a61
commit
c77617c3e1
|
|
@ -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 <T> extends XCallback.Param implements XposedInterface.BeforeHookCallback<T>, XposedInterface.AfterHookCallback<T> {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
|
@ -121,6 +131,8 @@ public abstract class XC_MethodHook extends XCallback {
|
|||
private Throwable throwable = null;
|
||||
public boolean returnEarly = false;
|
||||
|
||||
private final HashMap<String, Object> 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> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<T> {
|
||||
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<T> 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<T>) 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<T>) cb).after(param);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
XposedBridge.log(t);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <T, U extends Executable> MethodUnhooker<T, U> 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<BeforeMethodHooker<Method>, Method> hookBefore(@NonNull Method origin, @NonNull BeforeMethodHooker<Method> hooker) {
|
||||
return null;
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public MethodUnhooker<AfterMethodHooker<Method>, Method> hookAfter(@NonNull Method origin, @NonNull AfterMethodHooker<Method> hooker) {
|
||||
return null;
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public MethodUnhooker<MethodHooker<Method>, Method> hook(@NonNull Method origin, @NonNull MethodHooker<Method> hooker) {
|
||||
return null;
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public MethodUnhooker<BeforeMethodHooker<Method>, Method> hookBefore(@NonNull Method origin, int priority, @NonNull BeforeMethodHooker<Method> hooker) {
|
||||
return null;
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public MethodUnhooker<AfterMethodHooker<Method>, Method> hookAfter(@NonNull Method origin, int priority, @NonNull AfterMethodHooker<Method> hooker) {
|
||||
return null;
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public MethodUnhooker<MethodHooker<Method>, Method> hook(@NonNull Method origin, int priority, @NonNull MethodHooker<Method> hooker) {
|
||||
return null;
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public <T> MethodUnhooker<BeforeMethodHooker<Constructor<T>>, Constructor<T>> hookBefore(@NonNull Constructor<T> origin, @NonNull BeforeMethodHooker<Constructor<T>> hooker) {
|
||||
return null;
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public <T> MethodUnhooker<AfterMethodHooker<Constructor<T>>, Constructor<T>> hookAfter(@NonNull Constructor<T> origin, @NonNull AfterMethodHooker<Constructor<T>> hooker) {
|
||||
return null;
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public <T> MethodUnhooker<MethodHooker<Constructor<T>>, Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull MethodHooker<Constructor<T>> hooker) {
|
||||
return null;
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public <T> MethodUnhooker<BeforeMethodHooker<Constructor<T>>, Constructor<T>> hookBefore(@NonNull Constructor<T> origin, int priority, @NonNull BeforeMethodHooker<Constructor<T>> hooker) {
|
||||
return null;
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public <T> MethodUnhooker<AfterMethodHooker<Constructor<T>>, Constructor<T>> hookAfter(@NonNull Constructor<T> origin, int priority, @NonNull AfterMethodHooker<Constructor<T>> hooker) {
|
||||
return null;
|
||||
return doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public <T> MethodUnhooker<MethodHooker<Constructor<T>>, Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull MethodHooker<Constructor<T>> 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
|
||||
|
|
|
|||
|
|
@ -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 <T> Class<T> classByName(@NonNull String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> Class<T> classOrNullByName(@NonNull String className, @Nullable ClassLoader classLoader) {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Class<T> classByBinaryName(@NonNull String binaryClassName, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> Class<T> classOrNullByBinaryName(@NonNull String binaryClassName, @Nullable ClassLoader classLoader) {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
<U> 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> U getExtra(@NonNull String key);
|
||||
|
|
|
|||
Loading…
Reference in New Issue