Use modern hook API for internal hookers
This commit is contained in:
parent
e8e9105598
commit
fc1adeac55
|
|
@ -34,34 +34,29 @@ import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker;
|
|||
import org.lsposed.lspd.hooker.LoadedApkCtorHooker;
|
||||
import org.lsposed.lspd.hooker.OpenDexFileHooker;
|
||||
import org.lsposed.lspd.impl.LSPosedContext;
|
||||
import org.lsposed.lspd.impl.LSPosedHelper;
|
||||
import org.lsposed.lspd.service.ILSPApplicationService;
|
||||
import org.lsposed.lspd.util.Utils;
|
||||
|
||||
import dalvik.system.DexFile;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.XposedInit;
|
||||
|
||||
public class Startup {
|
||||
@SuppressWarnings("deprecation")
|
||||
private static void startBootstrapHook(boolean isSystem) {
|
||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||
XposedHelpers.findAndHookMethod(Thread.class, "dispatchUncaughtException",
|
||||
Throwable.class, new CrashDumpHooker());
|
||||
LSPosedHelper.hookMethod(CrashDumpHooker.class, Thread.class, "dispatchUncaughtException", Throwable.class);
|
||||
if (isSystem) {
|
||||
XposedBridge.hookAllMethods(ZygoteInit.class,
|
||||
"handleSystemServerProcess", new HandleSystemServerProcessHooker());
|
||||
LSPosedHelper.hookAllMethods(HandleSystemServerProcessHooker.class, ZygoteInit.class, "handleSystemServerProcess");
|
||||
} else {
|
||||
var hooker = new OpenDexFileHooker();
|
||||
XposedBridge.hookAllMethods(DexFile.class, "openDexFile", hooker);
|
||||
XposedBridge.hookAllMethods(DexFile.class, "openInMemoryDexFile", hooker);
|
||||
XposedBridge.hookAllMethods(DexFile.class, "openInMemoryDexFiles", hooker);
|
||||
LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, "openDexFile");
|
||||
LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, "openInMemoryDexFile");
|
||||
LSPosedHelper.hookAllMethods(OpenDexFileHooker.class, DexFile.class, "openInMemoryDexFiles");
|
||||
}
|
||||
XposedHelpers.findAndHookConstructor(LoadedApk.class,
|
||||
LSPosedHelper.hookConstructor(LoadedApkCtorHooker.class, LoadedApk.class,
|
||||
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
|
||||
ClassLoader.class, boolean.class, boolean.class, boolean.class,
|
||||
new LoadedApkCtorHooker());
|
||||
XposedBridge.hookAllMethods(ActivityThread.class, "attach", new AttachHooker());
|
||||
ClassLoader.class, boolean.class, boolean.class, boolean.class);
|
||||
LSPosedHelper.hookAllMethods(AttachHooker.class, ActivityThread.class, "attach");
|
||||
}
|
||||
|
||||
public static void bootstrapXposed() {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
package org.lsposed.lspd.hooker;
|
||||
|
||||
import static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
|
||||
import org.lsposed.lspd.impl.LSPosedContext;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedInit;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
|
||||
public class AttachHooker extends XC_MethodHook {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {
|
||||
XposedInit.loadModules((ActivityThread) param.thisObject);
|
||||
@XposedHooker
|
||||
public class AttachHooker implements XposedInterface.Hooker {
|
||||
|
||||
@AfterInvocation
|
||||
public static void afterHookedMethod(XposedInterface.AfterHookCallback callback) {
|
||||
XposedInit.loadModules((ActivityThread) callback.getThisObject());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,20 @@ package org.lsposed.lspd.hooker;
|
|||
|
||||
import android.util.Log;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import org.lsposed.lspd.impl.LSPosedBridge;
|
||||
|
||||
public class CrashDumpHooker extends XC_MethodHook {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam<?> param) {
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.annotations.BeforeInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
|
||||
@XposedHooker
|
||||
public class CrashDumpHooker implements XposedInterface.Hooker {
|
||||
|
||||
@BeforeInvocation
|
||||
public static void beforeHookedMethod(XposedInterface.BeforeHookCallback callback) {
|
||||
try {
|
||||
var e = (Throwable) param.args[0];
|
||||
XposedBridge.log("Crash unexpectedly: " + Log.getStackTraceString(e));
|
||||
var e = (Throwable) callback.getArgs()[0];
|
||||
LSPosedBridge.log("Crash unexpectedly: " + Log.getStackTraceString(e));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,32 +20,35 @@
|
|||
|
||||
package org.lsposed.lspd.hooker;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
|
||||
import org.lsposed.lspd.impl.LSPosedHelper;
|
||||
import org.lsposed.lspd.util.Hookers;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
|
||||
// system_server initialization
|
||||
public class HandleSystemServerProcessHooker extends XC_MethodHook {
|
||||
@XposedHooker
|
||||
public class HandleSystemServerProcessHooker implements XposedInterface.Hooker {
|
||||
|
||||
public static volatile ClassLoader systemServerCL;
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam<?> param) {
|
||||
@SuppressLint("PrivateApi")
|
||||
@AfterInvocation
|
||||
public static void afterHookedMethod() {
|
||||
Hookers.logD("ZygoteInit#handleSystemServerProcess() starts");
|
||||
try {
|
||||
// get system_server classLoader
|
||||
systemServerCL = Thread.currentThread().getContextClassLoader();
|
||||
// deopt methods in SYSTEMSERVERCLASSPATH
|
||||
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
||||
XposedBridge.hookAllMethods(
|
||||
XposedHelpers.findClass("com.android.server.SystemServer", systemServerCL),
|
||||
"startBootstrapServices", new StartBootstrapServicesHooker());
|
||||
var clazz = Class.forName("com.android.server.SystemServer", false, systemServerCL);
|
||||
LSPosedHelper.hookAllMethods(StartBootstrapServicesHooker.class, clazz, "startBootstrapServices");
|
||||
} catch (Throwable t) {
|
||||
Hookers.logE("error when hooking systemMain", t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,19 +26,23 @@ import android.util.Log;
|
|||
|
||||
import org.lsposed.lspd.util.Hookers;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.XposedInit;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
|
||||
// when a package is loaded for an existing process, trigger the callbacks as well
|
||||
public class LoadedApkCtorHooker extends XC_MethodHook {
|
||||
@XposedHooker
|
||||
public class LoadedApkCtorHooker implements XposedInterface.Hooker {
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam<?> param) {
|
||||
@AfterInvocation
|
||||
public static void afterHookedMethod(XposedInterface.AfterHookCallback callback) {
|
||||
Hookers.logD("LoadedApk#<init> starts");
|
||||
|
||||
try {
|
||||
LoadedApk loadedApk = (LoadedApk) param.thisObject;
|
||||
LoadedApk loadedApk = (LoadedApk) callback.getThisObject();
|
||||
assert loadedApk != null;
|
||||
String packageName = loadedApk.getPackageName();
|
||||
Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir");
|
||||
Hookers.logD("LoadedApk#<init> ends: " + mAppDir);
|
||||
|
|
|
|||
|
|
@ -2,26 +2,30 @@ package org.lsposed.lspd.hooker;
|
|||
|
||||
import android.os.Build;
|
||||
|
||||
import org.lsposed.lspd.impl.LSPosedBridge;
|
||||
import org.lsposed.lspd.nativebridge.HookBridge;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
|
||||
public class OpenDexFileHooker extends XC_MethodHook {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {
|
||||
@XposedHooker
|
||||
public class OpenDexFileHooker implements XposedInterface.Hooker {
|
||||
|
||||
@AfterInvocation
|
||||
public static void afterHookedMethod(XposedInterface.AfterHookCallback callback) {
|
||||
ClassLoader classLoader = null;
|
||||
for (var arg : param.args) {
|
||||
for (var arg : callback.getArgs()) {
|
||||
if (arg instanceof ClassLoader) {
|
||||
classLoader = (ClassLoader) arg;
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P && classLoader == null) {
|
||||
classLoader = XposedHelpers.class.getClassLoader();
|
||||
classLoader = LSPosedBridge.class.getClassLoader();
|
||||
}
|
||||
while (classLoader != null) {
|
||||
if (classLoader == XposedHelpers.class.getClassLoader()) {
|
||||
HookBridge.setTrusted(param.getResult());
|
||||
if (classLoader == LSPosedBridge.class.getClassLoader()) {
|
||||
HookBridge.setTrusted(callback.getResult());
|
||||
return;
|
||||
} else {
|
||||
classLoader = classLoader.getParent();
|
||||
|
|
|
|||
|
|
@ -27,16 +27,19 @@ import androidx.annotation.NonNull;
|
|||
import org.lsposed.lspd.impl.LSPosedContext;
|
||||
import org.lsposed.lspd.util.Hookers;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedInit;
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.XposedModuleInterface;
|
||||
import io.github.libxposed.api.annotations.BeforeInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
|
||||
public class StartBootstrapServicesHooker extends XC_MethodHook {
|
||||
@XposedHooker
|
||||
public class StartBootstrapServicesHooker implements XposedInterface.Hooker {
|
||||
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam<?> param) {
|
||||
@BeforeInvocation
|
||||
public static void beforeHookedMethod() {
|
||||
logD("SystemServer#startBootstrapServices() starts");
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package org.lsposed.lspd.impl;
|
|||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.lsposed.lspd.nativebridge.HookBridge;
|
||||
|
||||
import java.lang.reflect.Executable;
|
||||
|
|
@ -10,6 +12,11 @@ import java.lang.reflect.Method;
|
|||
import java.lang.reflect.Modifier;
|
||||
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
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;
|
||||
|
||||
public class LSPosedBridge {
|
||||
|
||||
|
|
@ -30,12 +37,19 @@ public class LSPosedBridge {
|
|||
}
|
||||
|
||||
public static class HookerCallback {
|
||||
Method beforeInvocation;
|
||||
Method afterInvocation;
|
||||
@NonNull
|
||||
final Method beforeInvocation;
|
||||
@NonNull
|
||||
final Method afterInvocation;
|
||||
|
||||
public HookerCallback(Method beforeInvocation, Method afterInvocation) {
|
||||
final int beforeParams;
|
||||
final int afterParams;
|
||||
|
||||
public HookerCallback(@NonNull Method beforeInvocation, @NonNull Method afterInvocation) {
|
||||
this.beforeInvocation = beforeInvocation;
|
||||
this.afterInvocation = afterInvocation;
|
||||
this.beforeParams = beforeInvocation.getParameterCount();
|
||||
this.afterParams = afterInvocation.getParameterCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +125,11 @@ public class LSPosedBridge {
|
|||
for (beforeIdx = 0; beforeIdx < modernSnapshot.length; beforeIdx++) {
|
||||
try {
|
||||
var hooker = (HookerCallback) modernSnapshot[beforeIdx];
|
||||
if (hooker.beforeParams == 0) {
|
||||
ctxArray[beforeIdx] = hooker.beforeInvocation.invoke(null);
|
||||
} else {
|
||||
ctxArray[beforeIdx] = hooker.beforeInvocation.invoke(null, callback);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LSPosedBridge.log(t);
|
||||
|
||||
|
|
@ -150,12 +168,13 @@ public class LSPosedBridge {
|
|||
Object lastResult = callback.getResult();
|
||||
Throwable lastThrowable = callback.getThrowable();
|
||||
var hooker = (HookerCallback) modernSnapshot[afterIdx];
|
||||
var context = ctxArray[afterIdx];
|
||||
try {
|
||||
if (context == null) {
|
||||
if (hooker.afterParams == 0) {
|
||||
hooker.afterInvocation.invoke(null);
|
||||
} else if (hooker.afterParams == 1) {
|
||||
hooker.afterInvocation.invoke(null, callback);
|
||||
} else {
|
||||
hooker.afterInvocation.invoke(null, callback, context);
|
||||
hooker.afterInvocation.invoke(null, callback, ctxArray[afterIdx]);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LSPosedBridge.log(t);
|
||||
|
|
@ -186,4 +205,95 @@ public class LSPosedBridge {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dummyCallback() {
|
||||
}
|
||||
|
||||
public static <T extends Executable> XposedInterface.MethodUnhooker<T>
|
||||
doHook(T hookMethod, int priority, Class<? extends XposedInterface.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");
|
||||
} 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");
|
||||
}
|
||||
|
||||
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 = (method.getModifiers() & modifiers) == modifiers;
|
||||
var params = method.getParameterTypes();
|
||||
if (params.length == 1) {
|
||||
valid &= params[0].equals(XposedInterface.BeforeHookCallback.class);
|
||||
} else if (params.length != 0) {
|
||||
valid = false;
|
||||
}
|
||||
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 = (method.getModifiers() & modifiers) == modifiers;
|
||||
valid &= method.getReturnType().equals(void.class);
|
||||
var params = method.getParameterTypes();
|
||||
if (params.length == 1 || params.length == 2) {
|
||||
valid &= params[0].equals(XposedInterface.AfterHookCallback.class);
|
||||
} else if (params.length != 0) {
|
||||
valid = false;
|
||||
}
|
||||
if (!valid) {
|
||||
throw new IllegalArgumentException("AfterInvocation method format is invalid");
|
||||
}
|
||||
afterInvocation = method;
|
||||
}
|
||||
}
|
||||
if (beforeInvocation == null && afterInvocation == null) {
|
||||
throw new IllegalArgumentException("No method annotated with @BeforeInvocation or @AfterInvocation");
|
||||
}
|
||||
try {
|
||||
if (beforeInvocation == null) {
|
||||
beforeInvocation = LSPosedBridge.class.getMethod("dummyCallback");
|
||||
} else if (afterInvocation == null) {
|
||||
afterInvocation = LSPosedBridge.class.getMethod("dummyCallback");
|
||||
} else {
|
||||
var ret = beforeInvocation.getReturnType();
|
||||
var params = afterInvocation.getParameterTypes();
|
||||
if (ret != void.class && params.length == 2 && !ret.equals(params[1])) {
|
||||
throw new IllegalArgumentException("BeforeInvocation and AfterInvocation method format is invalid");
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new HookFailedError(e);
|
||||
}
|
||||
|
||||
var callback = new LSPosedBridge.HookerCallback(beforeInvocation, afterInvocation);
|
||||
if (HookBridge.hookMethod(true, hookMethod, LSPosedBridge.NativeHooker.class, priority, callback)) {
|
||||
return new XposedInterface.MethodUnhooker<>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public T getOrigin() {
|
||||
return hookMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unhook() {
|
||||
HookBridge.unhookMethod(true, hookMethod, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
throw new HookFailedError("Cannot hook " + hookMethod);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ 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;
|
||||
|
|
@ -39,10 +38,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
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.errors.XposedFrameworkError;
|
||||
import io.github.libxposed.api.utils.DexParser;
|
||||
|
||||
|
|
@ -169,104 +164,28 @@ public class LSPosedContext implements XposedInterface {
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
} 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");
|
||||
}
|
||||
|
||||
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[]{BeforeHookCallback.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 &= 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");
|
||||
}
|
||||
boolean valid;
|
||||
var ctx = beforeInvocation.getReturnType();
|
||||
if (ctx == void.class) {
|
||||
valid = Arrays.equals(afterInvocation.getParameterTypes(), new Class[]{AfterHookCallback.class});
|
||||
} else {
|
||||
valid = Arrays.equals(afterInvocation.getParameterTypes(), new Class[]{AfterHookCallback.class, ctx});
|
||||
}
|
||||
if (!valid) {
|
||||
throw new IllegalArgumentException("AfterInvocation method format is invalid");
|
||||
}
|
||||
|
||||
var callback = new LSPosedBridge.HookerCallback(beforeInvocation, afterInvocation);
|
||||
if (HookBridge.hookMethod(true, hookMethod, LSPosedBridge.NativeHooker.class, priority, callback)) {
|
||||
return new MethodUnhooker<>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public T getOrigin() {
|
||||
return hookMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unhook() {
|
||||
HookBridge.unhookMethod(true, hookMethod, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
throw new HookFailedError("Cannot hook " + hookMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
return LSPosedBridge.doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodUnhooker<Method> hook(@NonNull Method origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
return LSPosedBridge.doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
return LSPosedBridge.doHook(origin, PRIORITY_DEFAULT, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
return doHook(origin, priority, hooker);
|
||||
return LSPosedBridge.doHook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
private static boolean doDeoptimize(@NonNull Executable method) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package org.lsposed.lspd.impl;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.errors.HookFailedError;
|
||||
|
||||
public class LSPosedHelper {
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static <T> XposedInterface.MethodUnhooker<Method>
|
||||
hookMethod(Class<? extends XposedInterface.Hooker> hooker, Class<T> clazz, String methodName, Class<?>... parameterTypes) {
|
||||
try {
|
||||
var method = clazz.getDeclaredMethod(methodName, parameterTypes);
|
||||
return LSPosedBridge.doHook(method, XposedInterface.PRIORITY_DEFAULT, hooker);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new HookFailedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static <T> Set<XposedInterface.MethodUnhooker<Method>>
|
||||
hookAllMethods(Class<? extends XposedInterface.Hooker> hooker, Class<T> clazz, String methodName) {
|
||||
var unhooks = new HashSet<XposedInterface.MethodUnhooker<Method>>();
|
||||
for (var method : clazz.getDeclaredMethods()) {
|
||||
if (method.getName().equals(methodName)) {
|
||||
unhooks.add(LSPosedBridge.doHook(method, XposedInterface.PRIORITY_DEFAULT, hooker));
|
||||
}
|
||||
}
|
||||
return unhooks;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public static <T> XposedInterface.MethodUnhooker<Constructor<T>>
|
||||
hookConstructor(Class<? extends XposedInterface.Hooker> hooker, Class<T> clazz, Class<?>... parameterTypes) {
|
||||
try {
|
||||
var constructor = clazz.getDeclaredConstructor(parameterTypes);
|
||||
return LSPosedBridge.doHook(constructor, XposedInterface.PRIORITY_DEFAULT, hooker);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new HookFailedError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue