Use native containers (#1910)

* Pack parameters

* Use native containers

Co-authored-by: LoveSy <shana@zju.edu.cn>
This commit is contained in:
南宫雪珊 2022-05-03 20:35:45 +08:00 committed by GitHub
parent 6fbffd23b7
commit c63fb7af37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 78 deletions

View File

@ -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) {

View File

@ -18,4 +18,6 @@ public class HookBridge {
@FastNative
public static native boolean setTrusted(Object cookie);
public static native Object[] callbackSnapshot(Executable method);
}

View File

@ -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,34 +96,15 @@ 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,
jclass hooker, jint priority, jobject callback) {
@ -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;");