Fix hooking proxy method

This commit is contained in:
LoveSy 2022-11-01 22:27:40 +08:00
parent 3f513a43d1
commit bde8b5ed16
No known key found for this signature in database
3 changed files with 117 additions and 3 deletions

View File

@ -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 &param : 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) {

View File

@ -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);
}
}

View File

@ -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);
}
}