Use native containers (#1910)
* Pack parameters * Use native containers Co-authored-by: LoveSy <shana@zju.edu.cn>
This commit is contained in:
parent
6fbffd23b7
commit
c63fb7af37
|
|
@ -82,6 +82,20 @@ public final class XposedBridge {
|
|||
|
||||
public static volatile ClassLoader dummyClassLoader = null;
|
||||
|
||||
private static final ClassCastException castException = new ClassCastException("Return value's type from hook callback does not match the hooked method");
|
||||
|
||||
private static final Method getCause;
|
||||
|
||||
static {
|
||||
Method tmp;
|
||||
try {
|
||||
tmp = InvocationTargetException.class.getMethod("getCause");
|
||||
} catch (Throwable e) {
|
||||
tmp = null;
|
||||
}
|
||||
getCause = tmp;
|
||||
}
|
||||
|
||||
public static void initXResources() {
|
||||
if (dummyClassLoader != null) {
|
||||
return;
|
||||
|
|
@ -381,32 +395,21 @@ public final class XposedBridge {
|
|||
}
|
||||
|
||||
public static class AdditionalHookInfo {
|
||||
private static final ClassCastException castException = new ClassCastException("Return value's type from hook callback does not match the hooked method");
|
||||
private static final Method getCause;
|
||||
private final Executable method;
|
||||
private final Object[][] callbacks;
|
||||
private final Class<?> returnType;
|
||||
private final boolean isStatic;
|
||||
private final Object params;
|
||||
|
||||
static {
|
||||
Method tmp;
|
||||
try {
|
||||
tmp = InvocationTargetException.class.getMethod("getCause");
|
||||
} catch (Throwable e) {
|
||||
tmp = null;
|
||||
}
|
||||
getCause = tmp;
|
||||
}
|
||||
|
||||
private AdditionalHookInfo(Executable method, Object[][] callbacks) {
|
||||
this.method = method;
|
||||
isStatic = Modifier.isStatic(method.getModifiers());
|
||||
private AdditionalHookInfo(Executable method) {
|
||||
var isStatic = Modifier.isStatic(method.getModifiers());
|
||||
Object returnType;
|
||||
if (method instanceof Method) {
|
||||
returnType = ((Method) method).getReturnType();
|
||||
} else {
|
||||
returnType = null;
|
||||
}
|
||||
this.callbacks = callbacks;
|
||||
params = new Object[] {
|
||||
method,
|
||||
returnType,
|
||||
isStatic,
|
||||
};
|
||||
}
|
||||
|
||||
// This method is quite critical. We should try not to use system methods to avoid
|
||||
|
|
@ -414,6 +417,12 @@ public final class XposedBridge {
|
|||
public Object callback(Object[] args) throws Throwable {
|
||||
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||
|
||||
var array = ((Object[]) params);
|
||||
|
||||
var method = (Executable) array[0];
|
||||
var returnType = (Class<?>) array[1];
|
||||
var isStatic = (Boolean) array[2];
|
||||
|
||||
param.method = method;
|
||||
|
||||
if (isStatic) {
|
||||
|
|
@ -428,9 +437,8 @@ public final class XposedBridge {
|
|||
}
|
||||
}
|
||||
|
||||
Object[] callbacksSnapshot = callbacks[0];
|
||||
final int callbacksLength = callbacksSnapshot.length;
|
||||
if (callbacksLength == 0) {
|
||||
Object[] callbacksSnapshot = HookBridge.callbackSnapshot(method);
|
||||
if (callbacksSnapshot == null || callbacksSnapshot.length == 0) {
|
||||
try {
|
||||
return HookBridge.invokeOriginalMethod(method, param.thisObject, param.args);
|
||||
} catch (InvocationTargetException ite) {
|
||||
|
|
@ -457,7 +465,7 @@ public final class XposedBridge {
|
|||
beforeIdx++;
|
||||
break;
|
||||
}
|
||||
} while (++beforeIdx < callbacksLength);
|
||||
} while (++beforeIdx < callbacksSnapshot.length);
|
||||
|
||||
// call original method if not requested otherwise
|
||||
if (!param.returnEarly) {
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ public class HookBridge {
|
|||
|
||||
@FastNative
|
||||
public static native boolean setTrusted(Object cookie);
|
||||
|
||||
public static native Object[] callbackSnapshot(Executable method);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ namespace {
|
|||
|
||||
struct HookItem {
|
||||
jobject backup {nullptr};
|
||||
jobjectArray callbacks {nullptr};
|
||||
std::multiset<jint, std::greater<>> priorities {};
|
||||
std::multimap<jint, jobject, std::greater<>> callbacks {};
|
||||
};
|
||||
|
||||
std::shared_mutex hooked_lock;
|
||||
|
|
@ -71,40 +70,19 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jobject hookMethod,
|
|||
if (!hook_item) {
|
||||
std::unique_lock lk(hooked_lock);
|
||||
hook_item = &hooked_methods[target];
|
||||
if (!hook_item->callbacks) {
|
||||
newHook = true;
|
||||
hook_item->callbacks = (jobjectArray) env->NewGlobalRef(
|
||||
env->NewObjectArray(1, env->FindClass("[Ljava/lang/Object;"),
|
||||
env->NewObjectArray(0, JNI_FindClass(env, "java/lang/Object"), nullptr)));
|
||||
}
|
||||
}
|
||||
if (newHook) {
|
||||
auto init = env->GetMethodID(hooker, "<init>", "(Ljava/lang/reflect/Executable;[[Ljava/lang/Object;)V");
|
||||
auto init = env->GetMethodID(hooker, "<init>", "(Ljava/lang/reflect/Executable;)V");
|
||||
auto callback_method = env->ToReflectedMethod(hooker, env->GetMethodID(hooker, "callback",
|
||||
"([Ljava/lang/Object;)Ljava/lang/Object;"),
|
||||
false);
|
||||
auto hooker_object = env->NewObject(hooker, init, hookMethod, hook_item->callbacks);
|
||||
auto hooker_object = env->NewObject(hooker, init, hookMethod);
|
||||
hook_item->backup = lsplant::Hook(env, hookMethod, hooker_object, callback_method);
|
||||
env->DeleteLocalRef(hooker_object);
|
||||
}
|
||||
env->MonitorEnter(hook_item->callbacks);
|
||||
auto insert_point = hook_item->priorities.emplace(priority);
|
||||
auto old_array = (jobjectArray) env->GetObjectArrayElement(hook_item->callbacks, 0);
|
||||
auto new_array = env->NewObjectArray(static_cast<jint>(hook_item->priorities.size()), env->FindClass("java/lang/Object"), nullptr);
|
||||
for (auto [i, current, passed] = std::make_tuple(0, hook_item->priorities.begin(), false); current != hook_item->priorities.end(); ++current, ++i) {
|
||||
if (current == insert_point) {
|
||||
env->SetObjectArrayElement(new_array, i, callback);
|
||||
passed = true;
|
||||
} else {
|
||||
auto element = env->GetObjectArrayElement(old_array, i - passed);
|
||||
env->SetObjectArrayElement(new_array, i, element);
|
||||
env->DeleteLocalRef(element);
|
||||
}
|
||||
}
|
||||
env->SetObjectArrayElement(hook_item->callbacks, 0, new_array);
|
||||
env->DeleteLocalRef(old_array);
|
||||
env->DeleteLocalRef(new_array);
|
||||
env->MonitorExit(hook_item->callbacks);
|
||||
JNIMonitor monitor(env, hook_item->backup);
|
||||
hook_item->callbacks.emplace(std::make_pair(priority, env->NewGlobalRef(callback)));
|
||||
return hook_item->backup ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -118,33 +96,14 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jobject hookMethod, jo
|
|||
}
|
||||
}
|
||||
if (!hook_item) return JNI_FALSE;
|
||||
JNIMonitor monitor(env, hook_item->callbacks);
|
||||
auto old_array = (jobjectArray) env->GetObjectArrayElement(hook_item->callbacks, 0);
|
||||
if (hook_item->priorities.empty()) return JNI_FALSE;
|
||||
auto new_array = env->NewObjectArray(static_cast<jint>(hook_item->priorities.size() - 1), env->FindClass("java/lang/Object"), nullptr);
|
||||
auto to_remove = hook_item->priorities.end();
|
||||
for (auto [i, current, passed] = std::make_tuple(0, hook_item->priorities.begin(), false); current != hook_item->priorities.end(); ++current, ++i) {
|
||||
auto element = env->GetObjectArrayElement(old_array, i);
|
||||
if (env->IsSameObject(element, callback)) {
|
||||
to_remove = current;
|
||||
passed = true;
|
||||
} else {
|
||||
if (i - passed >= hook_item->priorities.size() - 1) {
|
||||
JNIMonitor monitor(env, hook_item->backup);
|
||||
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;
|
||||
}
|
||||
}
|
||||
return JNI_FALSE;
|
||||
}
|
||||
env->SetObjectArrayElement(new_array, i - passed, element);
|
||||
}
|
||||
env->DeleteLocalRef(element);
|
||||
}
|
||||
bool removed = false;
|
||||
if (to_remove != hook_item->priorities.end()) {
|
||||
hook_item->priorities.erase(to_remove);
|
||||
env->SetObjectArrayElement(hook_item->callbacks, 0, new_array);
|
||||
removed = true;
|
||||
}
|
||||
env->DeleteLocalRef(old_array);
|
||||
env->DeleteLocalRef(new_array);
|
||||
return removed ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, deoptimizeMethod, jobject hookMethod,
|
||||
|
|
@ -177,6 +136,24 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, setTrusted, jobject cookie) {
|
|||
return lsplant::MakeDexFileTrusted(env, cookie);
|
||||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jobjectArray, HookBridge, callbackSnapshot, jobject method) {
|
||||
auto target = env->FromReflectedMethod(method);
|
||||
HookItem *hook_item = nullptr;
|
||||
{
|
||||
std::shared_lock lk(hooked_lock);
|
||||
if (auto found = hooked_methods.find(target); found != hooked_methods.end()) {
|
||||
hook_item = &found->second;
|
||||
}
|
||||
}
|
||||
if (!hook_item) return nullptr;
|
||||
JNIMonitor monitor(env, hook_item->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));
|
||||
}
|
||||
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"),
|
||||
|
|
@ -184,10 +161,11 @@ static JNINativeMethod gMethods[] = {
|
|||
LSP_NATIVE_METHOD(HookBridge, invokeOriginalMethod, "(Ljava/lang/reflect/Executable;Ljava/lang/Object;[Ljava/lang/Object;)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;"),
|
||||
};
|
||||
|
||||
void RegisterHookBridge(JNIEnv *env) {
|
||||
auto method = env->FindClass("java/lang/reflect/Method");
|
||||
jclass method = env->FindClass("java/lang/reflect/Method");
|
||||
invoke = env->GetMethodID(
|
||||
method, "invoke",
|
||||
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
|
|
|
|||
Loading…
Reference in New Issue