不能食言,再晚也要写

0x01 Java反射

反射之中包含了一个「反」字,有「反」就会有「正」,那么解释反射就必须先从「正」开始解释。

一般情况下,当使用某个类时必定知道它是什么类(类名),是用来做什么的(类的属性和方法)。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

1
2
3
User user = new User();
user.setName("yhy");
System.out.println(user.getName());

“正射”就是通过new创建了一个User实例,然后通过实例(user)去调用其所属方法。

但是当你new的时候不知道类名怎么办?受private保护的方法怎么调用?这时候反射的作用就体现出来了。

1
2
3
4
5
6
7
8
9
10
11
12
// new
Class clz = Class.forName("yhy.reflect.User");
Object object = clz.newInstance();

// setName("yhy")
Method method = clz.getMethod("setName", String.class);
method.invoke(object, "yhy");

// getName()
Method name = clz.getDeclaredMethod("getName",null);
Object o1 = name.invoke(object, null);
System.out.println(o1);

上面两段代码的执行结果,是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(User),而第二段代码则是在运行时通过字符串值才得知要运行的类(yhy.reflect.User)。

所以说什么是反射?

反射就是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。

java反射机制给漏洞利用提供了很多便利,我们可以在很多java漏洞的exp中看到它的影子,所以,学习java安全是绕不开它的。

Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

0x02 反射常用API

2.1 获取Class对象

在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。

在 Java API 中,获取 Class 类对象有三种方法:

1
2
3
4
5
6
7
// 1.通过字符串获取Class对象,这个字符串必须带上完整路径名
Class clz = Class.forName("yhy.reflect.User");
// 2.通过类的class属性
Class clz = User.class;
// 3.通过对象的getClass()函数
User user = new User();
Class clz = user.getClass();
  • 第一种方法是通过类的全路径字符串获取 Class 对象,这也是平时最常用的反射获取 Class 对象的方法;
  • 第二种方法有限制条件:需要导入类的包;
  • 第三种方法已经有了 User 对象,不再需要反射。

2.2 通过反射创建类对象

通过反射创建类对象主要有两种方式:

第一种:通过 Class 对象的 newInstance() 方法

1
2
Class clz = Class.forName("yhy.reflect.User");
Object object = clz.newInstance();

第二种:通过 Constructor 对象的 newInstance() 方法

1
2
3
Class clz = Class.forName("yhy.reflect.User");
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

1
2
3
Class clz = Class.forName("yhy.reflect.User");
Constructor constructor = clz.getConstructor(String.class);
Object object = constructor.newInstance("yhy");

2.3 通过反射获取类属性、方法、构造器

两个属性:一个公有(age),一个私有(Name)

我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

1
2
3
4
5
Class clz = Class.forName("yhy.reflect.User");
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}

而如果使用 Class 对象的 **getDeclaredFields() **方法则可以获取包括私有属性在内的所有属性:

1
2
3
4
5
Class clz = Class.forName("yhy.reflect.User");
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}

与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

1
2
3
4
5
6
7
8
9
// 获取所有声明的构造方法
getDeclaredConstructors()
// 获取所有公有的构造方法
getConstructors()

// 获取所有声明的方法
getDeclaredMethods()
// 获取所有公有的函数
getMethods()

代码地址:https://github.com/yhy0/JavaSerializeDemo