对Java反射方法进行封装

在这之前,我们需要分析一下封装Java反射方法是为了干什么。

显然在Android第一代加固壳的原理及实现一文中,我们重复地使用到了Java反射来获取类、字段、方法,无一例外,获取它们的最终目的是获取字段值、修改字段值、调用方法。为了实现这些目的,我们写了很臃肿的代码,因为太多重复的反射获取,所以我们可以将这些代码封装起来,形成一个自定义类,这样一来,加固的代码就可以变得轻巧了,且这个封装类以后还可以被重复利用。

分析完了封装目的之后,我们也得到了三个目的:

  1. 获取字段值
  2. 设置字段值
  3. 调用方法(构造方法和非构造方法,我这里统称为方法)

因此,我们只需要针对以上目的,分别实现它们就行了。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class RefInvoke{

/**
* 反射获取指定字段的值
* @param className: 实例对应的类
* @param field: 所要获取实例中的字段
* @param instanceObj: 实例对象,获取静态变量时设置为null
* @return
*/
public static Object getFieldObject(String className, String field, Object instanceObj){

try{
Class<?> aClass = Class.forName(className);
Field declaredField = aClass.getDeclaredField(field);
declaredField.setAccessible(true);
Object obj = declaredField.get(instanceObj);
return obj;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}

}

/**
* 反射设置指定字段的值
* @param className: 实例对应的类
* @param field: 所要设置实例中的字段
* @param instanceObj: 实例对象,设置静态变量时设置为null
* @param fieldValue: 字段所要设置成的值
* @return
*/
public static void setFieldObject(String className, String field, Object instanceObj ,Object fieldValue){
try{
Class<?> aClass = Class.forName(className);
Field declaredField = aClass.getDeclaredField(field);
declaredField.setAccessible(true);
declaredField.set(instanceObj, fieldValue);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

/**
* 反射调用指定方法
* @param className: 想要调用的方法所在的类名
* @param methodName: 所要调用方法的方法名
* @param paramTypes: 所要调用方法的参数类型数组,外部可通过匿名数组传入new Class[]{paramTypes that the method need}
* @param instanceObj: 类的实例对象,调用静态静态方法时设置为null
* @param paramValues: 调用方法时所要传入的具体参数值,外部可通过匿名数组传入new Obj[]{paramValues that the method need}
* @return
*/
public static Object invokeMethod(String className, String methodName, Class[] paramTypes, Object instanceObj , Object[] paramValues){
try{
Class<?> aClass = Class.forName(className);
Method declaredMethod = aClass.getDeclaredMethod(methodName, paramTypes);
declaredMethod.setAccessible(true);
Object obj = declaredMethod.invoke(instanceObj, paramValues);
return obj;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

/**
* 反射调用指定构造方法来创建对象
* @param className:
* @param paramTypes:
* @param paramValues:
* @return
*/
public static Object creatObject(String className, Class[] paramTypes,Object[] paramValues){
try {
Class<?> aClass = Class.forName(className);
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(paramTypes);
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance(paramValues);
return obj;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
}

}

写完这个之后,我也明白了之前写加固代码时的一个问题:为在获取ActivityThread实例时(get()invoke()),参数对象设置为null了。

因为静态方法、静态字段,这些可以通过类名点出来,不需要类的实例也能操作它们。(Java学艺不精😭)


对Java反射方法进行封装
http://example.com/2023/12/04/Android安全/对Java反射方法进行封装/
作者
gla2xy
发布于
2023年12月4日
许可协议