chore: initial support libxposed api 100
This commit is contained in:
@@ -206,12 +206,14 @@ android {
|
||||
)
|
||||
}
|
||||
packaging {
|
||||
resources.excludes.addAll(arrayOf(
|
||||
"META-INF/**",
|
||||
"kotlin/**",
|
||||
"**.bin",
|
||||
"kotlin-tooling-metadata.json"
|
||||
))
|
||||
// libxposed API uses META-INF/xposed
|
||||
resources.excludes.addAll(
|
||||
arrayOf(
|
||||
"kotlin/**",
|
||||
"**.bin",
|
||||
"kotlin-tooling-metadata.json"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
||||
@@ -27,7 +27,7 @@ dependencies {
|
||||
// Xposed API 89
|
||||
compileOnly(libs.xposed.api)
|
||||
// LSPosed API 100
|
||||
// compileOnly(libs.libxposed.api)
|
||||
compileOnly(projects.libs.libxposed)
|
||||
compileOnly(libs.androidx.annotation)
|
||||
implementation(projects.loader.hookapi)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package io.github.qauxv.loader.sbl.lsp100;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.XposedModule;
|
||||
import io.github.qauxv.loader.sbl.common.ModuleLoader;
|
||||
|
||||
/**
|
||||
* Entry point for libxpsoed API 100 (typically LSPosed).
|
||||
@@ -8,6 +13,57 @@ import androidx.annotation.Keep;
|
||||
* The libxpsoed API is used as ART hook implementation.
|
||||
*/
|
||||
@Keep
|
||||
public class Lsp100HookEntry {
|
||||
public class Lsp100HookEntry extends XposedModule {
|
||||
|
||||
/**
|
||||
* Instantiates a new Xposed module.
|
||||
* <p>
|
||||
* When the module is loaded into the target process, the constructor will be called.
|
||||
*
|
||||
* @param base The implementation interface provided by the framework, should not be used by the module
|
||||
* @param param Information about the process in which the module is loaded
|
||||
*/
|
||||
public Lsp100HookEntry(@NonNull XposedInterface base, @NonNull ModuleLoadedParam param) {
|
||||
super(base, param);
|
||||
mModule = param;
|
||||
Lsp100HookImpl.init(this);
|
||||
}
|
||||
|
||||
private ModuleLoadedParam mModule;
|
||||
|
||||
public static final String PACKAGE_NAME_QQ = "com.tencent.mobileqq";
|
||||
public static final String PACKAGE_NAME_QQ_INTERNATIONAL = "com.tencent.mobileqqi";
|
||||
public static final String PACKAGE_NAME_QQ_LITE = "com.tencent.qqlite";
|
||||
public static final String PACKAGE_NAME_QQ_HD = "com.tencent.minihd.qq";
|
||||
public static final String PACKAGE_NAME_TIM = "com.tencent.tim";
|
||||
|
||||
@Override
|
||||
public void onPackageLoaded(@NonNull PackageLoadedParam param) {
|
||||
String packageName = param.getPackageName();
|
||||
switch (packageName) {
|
||||
case PACKAGE_NAME_QQ:
|
||||
case PACKAGE_NAME_QQ_INTERNATIONAL:
|
||||
case PACKAGE_NAME_QQ_LITE:
|
||||
case PACKAGE_NAME_QQ_HD:
|
||||
case PACKAGE_NAME_TIM:
|
||||
// Initialize the module
|
||||
if (param.isFirstPackage()) {
|
||||
String modulePath = this.getApplicationInfo().sourceDir;
|
||||
handleLoadHostPackage(param.getClassLoader(), param.getApplicationInfo(), modulePath);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoadHostPackage(@NonNull ClassLoader cl, @NonNull ApplicationInfo ai, @NonNull String modulePath) {
|
||||
try {
|
||||
ModuleLoader.initialize(ai, cl, Lsp100HookImpl.INSTANCE, Lsp100HookImpl.INSTANCE, modulePath);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package io.github.qauxv.loader.sbl.lsp100;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.XposedModule;
|
||||
import io.github.qauxv.loader.hookapi.IHookBridge;
|
||||
import io.github.qauxv.loader.hookapi.ILoaderInfo;
|
||||
import io.github.qauxv.loader.sbl.BuildConfig;
|
||||
import io.github.qauxv.loader.sbl.common.CheckUtils;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Lsp100HookImpl implements IHookBridge, ILoaderInfo {
|
||||
|
||||
public static final Lsp100HookImpl INSTANCE = new Lsp100HookImpl();
|
||||
public static XposedModule self = null;
|
||||
|
||||
private Lsp100HookImpl() {
|
||||
}
|
||||
|
||||
public static void init(@NonNull XposedModule base) {
|
||||
self = base;
|
||||
Lsp100HookWrapper.self = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getApiLevel() {
|
||||
return XposedInterface.API;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFrameworkName() {
|
||||
return self.getFrameworkName();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFrameworkVersion() {
|
||||
return self.getFrameworkVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFrameworkVersionCode() {
|
||||
return self.getFrameworkVersionCode();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MemberUnhookHandle hookMethod(@NonNull Member member, @NonNull IMemberHookCallback callback, int priority) {
|
||||
return Lsp100HookWrapper.hookAndRegisterMethodCallback(member, callback, priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeoptimizationSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deoptimize(@NonNull Member member) {
|
||||
CheckUtils.checkNonNull(member, "member");
|
||||
if (member instanceof Method) {
|
||||
return self.deoptimize((Method) member);
|
||||
} else if (member instanceof Constructor) {
|
||||
return self.deoptimize((Constructor<?>) member);
|
||||
} else {
|
||||
throw new IllegalArgumentException("only method and constructor can be deoptimized");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object invokeOriginalMethod(@NonNull Method method, @Nullable Object thisObject, @NonNull Object[] args)
|
||||
throws NullPointerException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
CheckUtils.checkNonNull(method, "method");
|
||||
CheckUtils.checkNonNull(args, "args");
|
||||
return self.invokeOrigin(method, thisObject, args);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> T newInstanceOrigin(@NonNull Constructor<T> constructor, @NonNull Object... args)
|
||||
throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
|
||||
CheckUtils.checkNonNull(constructor, "constructor");
|
||||
CheckUtils.checkNonNull(args, "args");
|
||||
return self.newInstanceOrigin(constructor, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void invokeOriginalConstructor(@NonNull Constructor<T> ctor, @NonNull T thisObject, @NonNull Object[] args)
|
||||
throws NullPointerException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
CheckUtils.checkNonNull(ctor, "ctor");
|
||||
CheckUtils.checkNonNull(thisObject, "thisObject");
|
||||
CheckUtils.checkNonNull(args, "args");
|
||||
self.invokeOrigin(ctor, thisObject, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object queryExtension(@NonNull String key, @Nullable Object... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getEntryPointName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getLoaderVersionName() {
|
||||
return BuildConfig.VERSION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoaderVersionCode() {
|
||||
return BuildConfig.VERSION_CODE;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getMainModulePath() {
|
||||
return self.getApplicationInfo().sourceDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(@NonNull String msg) {
|
||||
self.log(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(@NonNull Throwable tr) {
|
||||
self.log(tr.toString(), tr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package io.github.qauxv.loader.sbl.lsp100;
|
||||
|
||||
import android.os.ParcelUuid;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.libxposed.api.XposedInterface;
|
||||
import io.github.libxposed.api.XposedModule;
|
||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||
import io.github.libxposed.api.annotations.BeforeInvocation;
|
||||
import io.github.libxposed.api.annotations.XposedHooker;
|
||||
import io.github.qauxv.loader.hookapi.IHookBridge;
|
||||
import io.github.qauxv.loader.sbl.common.CheckUtils;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class Lsp100HookWrapper {
|
||||
|
||||
private Lsp100HookWrapper() {
|
||||
}
|
||||
|
||||
public static XposedModule self = null;
|
||||
|
||||
private static final CallbackWrapper[] EMPTY_CALLBACKS = new CallbackWrapper[0];
|
||||
|
||||
private static final AtomicLong sNextHookId = new AtomicLong(1);
|
||||
|
||||
private static final Object sRegistryWriteLock = new Object();
|
||||
|
||||
private static final int LSP100_PRIORITY = XposedInterface.PRIORITY_LOWEST + 1;
|
||||
|
||||
public static class CallbackWrapper {
|
||||
|
||||
public final IHookBridge.IMemberHookCallback callback;
|
||||
public final long hookId = sNextHookId.getAndIncrement();
|
||||
public final int priority;
|
||||
|
||||
public CallbackWrapper(IHookBridge.IMemberHookCallback callback, int priority) {
|
||||
this.callback = callback;
|
||||
this.priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CallbackListHolder {
|
||||
|
||||
public final Object lock = new Object();
|
||||
// sorted by priority, descending
|
||||
public CallbackWrapper[] callbacks = EMPTY_CALLBACKS;
|
||||
|
||||
}
|
||||
|
||||
public static UnhookHandle hookAndRegisterMethodCallback(@NonNull Member method, @NonNull IHookBridge.IMemberHookCallback callback, int priority) {
|
||||
CheckUtils.checkNonNull(method, "method");
|
||||
CheckUtils.checkNonNull(callback, "callback");
|
||||
CallbackWrapper wrapper = new CallbackWrapper(callback, priority);
|
||||
UnhookHandle handle = new UnhookHandle(wrapper, method);
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
CallbackListHolder holder;
|
||||
synchronized (sRegistryWriteLock) {
|
||||
// 1. check if the method is already hooked
|
||||
ConcurrentHashMap<Member, CallbackListHolder> callbackList = sCallbackRegistry.get(declaringClass);
|
||||
if (callbackList == null) {
|
||||
callbackList = new ConcurrentHashMap<>();
|
||||
sCallbackRegistry.put(declaringClass, callbackList);
|
||||
}
|
||||
holder = callbackList.get(method);
|
||||
if (holder == null) {
|
||||
// 2. tell the underlying framework to hook the method
|
||||
if (method instanceof Method) {
|
||||
self.hook((Method) method, LSP100_PRIORITY, Lsp100HookAgent.class);
|
||||
} else if (method instanceof Constructor) {
|
||||
self.hook((Constructor<?>) method, LSP100_PRIORITY, Lsp100HookAgent.class);
|
||||
} else {
|
||||
throw new IllegalArgumentException("only method and constructor can be hooked, but got " + method);
|
||||
}
|
||||
// 3. create a new holder
|
||||
CallbackListHolder newHolder = new CallbackListHolder();
|
||||
callbackList.put(method, newHolder);
|
||||
holder = newHolder;
|
||||
}
|
||||
}
|
||||
// 4. add the callback to the holder
|
||||
synchronized (holder.lock) {
|
||||
// add and sort descending
|
||||
int newSize = holder.callbacks == null ? 1 : holder.callbacks.length + 1;
|
||||
CallbackWrapper[] newCallbacks = new CallbackWrapper[newSize];
|
||||
if (holder.callbacks != null) {
|
||||
int i = 0;
|
||||
for (; i < holder.callbacks.length; i++) {
|
||||
if (holder.callbacks[i].priority > priority) {
|
||||
newCallbacks[i] = holder.callbacks[i];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
newCallbacks[i] = wrapper;
|
||||
for (; i < holder.callbacks.length; i++) {
|
||||
newCallbacks[i + 1] = holder.callbacks[i];
|
||||
}
|
||||
} else {
|
||||
newCallbacks[0] = wrapper;
|
||||
}
|
||||
holder.callbacks = newCallbacks;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
public static void removeMethodCallback(@NonNull Member method, @NonNull CallbackWrapper callback) {
|
||||
CheckUtils.checkNonNull(method, "method");
|
||||
CheckUtils.checkNonNull(callback, "callback");
|
||||
// find the callback holder
|
||||
ConcurrentHashMap<Member, CallbackListHolder> callbackList = sCallbackRegistry.get(method.getDeclaringClass());
|
||||
if (callbackList == null) {
|
||||
return;
|
||||
}
|
||||
CallbackListHolder holder = callbackList.get(method);
|
||||
if (holder == null) {
|
||||
return;
|
||||
}
|
||||
// remove the callback
|
||||
synchronized (holder.lock) {
|
||||
ArrayList<CallbackWrapper> newCallbacks = new ArrayList<>();
|
||||
for (CallbackWrapper cb : holder.callbacks) {
|
||||
if (cb != callback) {
|
||||
newCallbacks.add(cb);
|
||||
}
|
||||
}
|
||||
holder.callbacks = newCallbacks.toArray(new CallbackWrapper[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isMethodCallbackRegistered(@NonNull Member method, @NonNull CallbackWrapper callback) {
|
||||
CheckUtils.checkNonNull(method, "method");
|
||||
CheckUtils.checkNonNull(callback, "callback");
|
||||
// find the callback holder
|
||||
ConcurrentHashMap<Member, CallbackListHolder> callbackList = sCallbackRegistry.get(method.getDeclaringClass());
|
||||
if (callbackList == null) {
|
||||
return false;
|
||||
}
|
||||
CallbackListHolder holder = callbackList.get(method);
|
||||
if (holder == null) {
|
||||
return false;
|
||||
}
|
||||
// read only, not need to lock
|
||||
CallbackWrapper[] callbacks = holder.callbacks;
|
||||
for (CallbackWrapper cb : callbacks) {
|
||||
if (cb == callback) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static CallbackWrapper[] copyCallbacks(@Nullable CallbackListHolder holder) {
|
||||
if (holder == null) {
|
||||
return EMPTY_CALLBACKS;
|
||||
}
|
||||
synchronized (holder.lock) {
|
||||
if (holder.callbacks != null) {
|
||||
return holder.callbacks.clone();
|
||||
}
|
||||
}
|
||||
return EMPTY_CALLBACKS;
|
||||
}
|
||||
|
||||
public static class InvocationParamWrapper implements IHookBridge.IMemberHookParam {
|
||||
|
||||
// the index of the active callback
|
||||
public int index = -1;
|
||||
public boolean isAfter = false;
|
||||
XposedInterface.BeforeHookCallback before;
|
||||
XposedInterface.AfterHookCallback after;
|
||||
// sorted by priority, descending
|
||||
public CallbackWrapper[] callbacks;
|
||||
// create on demand
|
||||
public Object[] extras;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Member getMember() {
|
||||
if (isAfter) {
|
||||
return after.getMember();
|
||||
} else {
|
||||
return before.getMember();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getThisObject() {
|
||||
if (isAfter) {
|
||||
return after.getThisObject();
|
||||
} else {
|
||||
return before.getThisObject();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
if (isAfter) {
|
||||
return after.getArgs();
|
||||
} else {
|
||||
return before.getArgs();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getResult() {
|
||||
if (isAfter) {
|
||||
return after.getResult();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(@Nullable Object result) {
|
||||
if (isAfter) {
|
||||
after.setResult(result);
|
||||
} else {
|
||||
before.returnAndSkip(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Throwable getThrowable() {
|
||||
if (isAfter) {
|
||||
return after.getThrowable();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThrowable(@NonNull Throwable throwable) {
|
||||
if (isAfter) {
|
||||
after.setThrowable(throwable);
|
||||
} else {
|
||||
before.throwAndSkip(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getExtra() {
|
||||
if (extras == null) {
|
||||
return null;
|
||||
}
|
||||
return extras[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtra(@Nullable Object extra) {
|
||||
if (extras == null) {
|
||||
// create on demand
|
||||
extras = new Object[callbacks.length];
|
||||
}
|
||||
extras[index] = extra;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Member, CallbackListHolder>> sCallbackRegistry = new ConcurrentHashMap<>();
|
||||
|
||||
@XposedHooker
|
||||
public static class Lsp100HookAgent implements XposedInterface.Hooker {
|
||||
|
||||
@BeforeInvocation
|
||||
public static InvocationParamWrapper beforeInvocation(@NonNull XposedInterface.BeforeHookCallback callback) {
|
||||
Member member = callback.getMember();
|
||||
// lookup callback list
|
||||
ConcurrentHashMap<Member, CallbackListHolder> callbackList = sCallbackRegistry.get(member.getDeclaringClass());
|
||||
if (callbackList == null) {
|
||||
return null;
|
||||
}
|
||||
CallbackListHolder holder = callbackList.get(member);
|
||||
if (holder == null) {
|
||||
return null;
|
||||
}
|
||||
// copy callbacks
|
||||
CallbackWrapper[] callbacks = copyCallbacks(holder);
|
||||
if (callbacks.length == 0) {
|
||||
return null;
|
||||
}
|
||||
// create invocation holder
|
||||
InvocationParamWrapper param = new InvocationParamWrapper();
|
||||
param.callbacks = callbacks;
|
||||
param.before = callback;
|
||||
param.isAfter = false;
|
||||
for (int i = 0; i < callbacks.length; i++) {
|
||||
param.index = i;
|
||||
try {
|
||||
callbacks[i].callback.beforeHookedMember(param);
|
||||
} catch (Throwable t) {
|
||||
self.log(t.toString(), t);
|
||||
}
|
||||
}
|
||||
param.index = -1;
|
||||
return param;
|
||||
}
|
||||
|
||||
@AfterInvocation
|
||||
public static void afterInvocation(
|
||||
@NonNull XposedInterface.AfterHookCallback callback,
|
||||
@Nullable InvocationParamWrapper param
|
||||
) {
|
||||
if (param == null) {
|
||||
throw new AssertionError("param is null");
|
||||
}
|
||||
param.isAfter = true;
|
||||
param.after = callback;
|
||||
// call in reserve order
|
||||
for (int i = param.callbacks.length - 1; i >= 0; i--) {
|
||||
param.index = i;
|
||||
try {
|
||||
param.callbacks[i].callback.afterHookedMember(param);
|
||||
} catch (Throwable t) {
|
||||
self.log(t.toString(), t);
|
||||
}
|
||||
}
|
||||
// for gc
|
||||
param.callbacks = null;
|
||||
param.extras = null;
|
||||
param.before = null;
|
||||
param.after = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnhookHandle implements IHookBridge.MemberUnhookHandle {
|
||||
|
||||
private final CallbackWrapper callback;
|
||||
private final Member method;
|
||||
|
||||
public UnhookHandle(@NonNull CallbackWrapper callback, @NonNull Member method) {
|
||||
this.callback = callback;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Member getMember() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public IHookBridge.IMemberHookCallback getCallback() {
|
||||
return callback.callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHookActive() {
|
||||
return isMethodCallbackRegistered(method, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unhook() {
|
||||
removeMethodCallback(method, callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
io.github.qauxv.loader.sbl.lsp100.Lsp100HookEntry
|
||||
@@ -0,0 +1,3 @@
|
||||
minApiVersion=51
|
||||
targetApiVersion=100
|
||||
staticScope=true
|
||||
@@ -0,0 +1 @@
|
||||
libqauxv.so
|
||||
4
loader/sbl/src/main/resources/META-INF/xposed/scope.list
Normal file
4
loader/sbl/src/main/resources/META-INF/xposed/scope.list
Normal file
@@ -0,0 +1,4 @@
|
||||
com.tencent.mobileqq
|
||||
com.tencent.tim
|
||||
com.tencent.qqlite
|
||||
com.tencent.minihd.qq
|
||||
Reference in New Issue
Block a user