mirror of
https://github.com/LSPosed/LSPlant.git
synced 2025-05-04 20:42:02 +08:00
Fix hooking proxy method
This commit is contained in:
parent
3f513a43d1
commit
bde8b5ed16
@ -85,6 +85,9 @@ jmethodID load_class = nullptr;
|
||||
jmethodID set_accessible = nullptr;
|
||||
jclass executable = nullptr;
|
||||
|
||||
// for proxy method
|
||||
jmethodID method_get_parameter_types = nullptr;
|
||||
jmethodID method_get_return_type = nullptr;
|
||||
// for old platform
|
||||
jclass path_class_loader = nullptr;
|
||||
jmethodID path_class_loader_init = nullptr;
|
||||
@ -92,7 +95,7 @@ jmethodID path_class_loader_init = nullptr;
|
||||
constexpr auto kInternalMethods = std::make_tuple(
|
||||
&method_get_name, &method_get_declaring_class, &class_get_name, &class_get_class_loader,
|
||||
&class_get_declared_constructors, &in_memory_class_loader_init, &load_class, &set_accessible,
|
||||
&path_class_loader_init);
|
||||
&method_get_parameter_types, &method_get_return_type, &path_class_loader_init);
|
||||
|
||||
std::string generated_class_name;
|
||||
std::string generated_source_name;
|
||||
@ -139,7 +142,19 @@ bool InitJNI(JNIEnv *env) {
|
||||
if (method_get_declaring_class =
|
||||
JNI_GetMethodID(env, executable, "getDeclaringClass", "()Ljava/lang/Class;");
|
||||
!method_get_declaring_class) {
|
||||
LOGE("Failed to find getName method");
|
||||
LOGE("Failed to find getDeclaringClass method");
|
||||
return false;
|
||||
}
|
||||
if (method_get_parameter_types =
|
||||
JNI_GetMethodID(env, executable, "getParameterTypes", "()[Ljava/lang/Class;");
|
||||
!method_get_parameter_types) {
|
||||
LOGE("Failed to find getParameterTypes method");
|
||||
return false;
|
||||
}
|
||||
if (method_get_return_type =
|
||||
JNI_GetMethodID(env, JNI_FindClass(env, "java/lang/reflect/Method"), "getReturnType", "()Ljava/lang/Class;");
|
||||
!method_get_return_type) {
|
||||
LOGE("Failed to find getReturnType method");
|
||||
return false;
|
||||
}
|
||||
auto clazz = JNI_FindClass(env, "java/lang/Class");
|
||||
@ -530,6 +545,68 @@ bool DoUnHook(ArtMethod *target, ArtMethod *backup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetProxyMethodShorty(JNIEnv *env, jobject proxy_method) {
|
||||
const auto return_type = JNI_CallObjectMethod(env, proxy_method, method_get_return_type);
|
||||
const auto parameter_types =
|
||||
JNI_Cast<jobjectArray>(JNI_CallObjectMethod(env, proxy_method, method_get_parameter_types));
|
||||
auto integer_class = JNI_FindClass(env, "java/lang/Integer");
|
||||
auto long_class = JNI_FindClass(env, "java/lang/Long");
|
||||
auto float_class = JNI_FindClass(env, "java/lang/Float");
|
||||
auto double_class = JNI_FindClass(env, "java/lang/Double");
|
||||
auto boolean_class = JNI_FindClass(env, "java/lang/Boolean");
|
||||
auto byte_class = JNI_FindClass(env, "java/lang/Byte");
|
||||
auto char_class = JNI_FindClass(env, "java/lang/Character");
|
||||
auto short_class = JNI_FindClass(env, "java/lang/Short");
|
||||
auto void_class = JNI_FindClass(env, "java/lang/Void");
|
||||
static auto *kIntTypeField =
|
||||
JNI_GetStaticFieldID(env, integer_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kLongTypeField =
|
||||
JNI_GetStaticFieldID(env, long_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kFloatTypeField =
|
||||
JNI_GetStaticFieldID(env, float_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kDoubleTypeField =
|
||||
JNI_GetStaticFieldID(env, double_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kBooleanTypeField =
|
||||
JNI_GetStaticFieldID(env, boolean_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kByteTypeField =
|
||||
JNI_GetStaticFieldID(env, byte_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kCharTypeField =
|
||||
JNI_GetStaticFieldID(env, char_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kShortTypeField =
|
||||
JNI_GetStaticFieldID(env, short_class, "TYPE", "Ljava/lang/Class;");
|
||||
static auto *kVoidTypeField =
|
||||
JNI_GetStaticFieldID(env, void_class, "TYPE", "Ljava/lang/Class;");
|
||||
|
||||
auto int_type = JNI_GetStaticObjectField(env, integer_class, kIntTypeField);
|
||||
auto long_type = JNI_GetStaticObjectField(env, long_class, kLongTypeField);
|
||||
auto float_type = JNI_GetStaticObjectField(env, float_class, kFloatTypeField);
|
||||
auto double_type = JNI_GetStaticObjectField(env, double_class, kDoubleTypeField);
|
||||
auto boolean_type = JNI_GetStaticObjectField(env, boolean_class, kBooleanTypeField);
|
||||
auto byte_type = JNI_GetStaticObjectField(env, byte_class, kByteTypeField);
|
||||
auto char_type = JNI_GetStaticObjectField(env, char_class, kCharTypeField);
|
||||
auto short_type = JNI_GetStaticObjectField(env, short_class, kShortTypeField);
|
||||
auto void_type = JNI_GetStaticObjectField(env, void_class, kVoidTypeField);
|
||||
|
||||
std::string out;
|
||||
auto type_to_shorty = [&](const ScopedLocalRef<jobject> &type) {
|
||||
if (env->IsSameObject(type, int_type)) return 'I';
|
||||
if (env->IsSameObject(type, long_type)) return 'J';
|
||||
if (env->IsSameObject(type, float_type)) return 'F';
|
||||
if (env->IsSameObject(type, double_type)) return 'D';
|
||||
if (env->IsSameObject(type, boolean_type)) return 'Z';
|
||||
if (env->IsSameObject(type, byte_type)) return 'B';
|
||||
if (env->IsSameObject(type, char_type)) return 'C';
|
||||
if (env->IsSameObject(type, short_type)) return 'S';
|
||||
if (env->IsSameObject(type, void_type)) return 'V';
|
||||
return 'L';
|
||||
};
|
||||
out += type_to_shorty(return_type);
|
||||
for (const auto ¶m : parameter_types) {
|
||||
out += type_to_shorty(param);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
inline namespace v2 {
|
||||
@ -589,7 +666,9 @@ using ::lsplant::IsHooked;
|
||||
}
|
||||
std::tie(built_class, hooker_field, hook_method, backup_method) = WrapScope(
|
||||
env,
|
||||
BuildDex(env, callback_class_loader, ArtMethod::GetMethodShorty(env, target_method),
|
||||
BuildDex(env, callback_class_loader,
|
||||
__builtin_expect(is_proxy, 0) ? GetProxyMethodShorty(env, target_method)
|
||||
: ArtMethod::GetMethodShorty(env, target_method),
|
||||
is_static, target->IsConstructor() ? "constructor" : target_method_name.get(),
|
||||
class_name.get(), callback_method_name.get()));
|
||||
if (!built_class || !hooker_field || !hook_method || !backup_method) {
|
||||
|
@ -8,7 +8,10 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.MethodSorters;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@ -116,4 +119,32 @@ public class UnitTest {
|
||||
Assert.assertFalse((Boolean) callStaticMethod.invoke(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void t06_proxyMethod() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
var proxyInterface = Class.forName("org.lsposed.lsplant.LSPTest$ForProxy");
|
||||
var proxy = Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, (proxy1, method, args) -> {
|
||||
if (method.getName().equals("abstractMethod")) {
|
||||
return (String) args[0] + (boolean) args[1] + (byte) args[2] + (short) args[3] + (int) args[4] + (long) args[5] + (float) args[6] + (double) args[7] + (Integer) args[8] + (Long) args[9];
|
||||
}
|
||||
return method.invoke(proxy1, args);
|
||||
});
|
||||
var abstractMethod = proxy.getClass().getDeclaredMethod("abstractMethod", String.class, boolean.class, byte.class, short.class, int.class, long.class, float.class, double.class, Integer.class, Long.class);
|
||||
var abstractMethodReplacement = Replacement.class.getDeclaredMethod("manyParametersReplacement", Hooker.MethodCallback.class);
|
||||
var a = "test";
|
||||
var b = true;
|
||||
var c = (byte) 114;
|
||||
var d = (short) 514;
|
||||
var e = 19;
|
||||
var f = 19L;
|
||||
var g = 810f;
|
||||
var h = 12345f;
|
||||
var o = a + b + c + d + e + f + g + h + e + f;
|
||||
var r = a + b + c + d + e + f + g + h + e + f + "replace";
|
||||
|
||||
Assert.assertEquals(abstractMethod.invoke(proxy, a, b, c, d, e, f, g, h, e, f), o);
|
||||
Hooker hooker = Hooker.hook(abstractMethod, abstractMethodReplacement, new Replacement());
|
||||
Assert.assertNotNull(hooker);
|
||||
Assert.assertEquals(abstractMethod.invoke(proxy, a, b, c, d, e, f, g, h, e, f), r);
|
||||
Assert.assertEquals(hooker.backup.invoke(proxy, a, b, c, d, e, f, g, h, e, f), o);
|
||||
}
|
||||
}
|
||||
|
@ -50,4 +50,8 @@ public class LSPTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ForProxy {
|
||||
String abstractMethod(String a, boolean b, byte c, short d, int e, long f, float g, double h, Integer i, Long j);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user