初探Java反序列化漏洞(二)
不能食言,再晚也要写
0x01 Java反射
反射之中包含了一个「反」字,有「反」就会有「正」,那么解释反射就必须先从「正」开始解释。
一般情况下,当使用某个类时必定知道它是什么类(类名),是用来做什么的(类的属性和方法)。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
1 | User user = new User(); |
“正射”就是通过new创建了一个User实例,然后通过实例(user)去调用其所属方法。
但是当你new的时候不知道类名怎么办?受private保护的方法怎么调用?这时候反射的作用就体现出来了。
1 | // new |
上面两段代码的执行结果,是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(User),而第二段代码则是在运行时通过字符串值才得知要运行的类(yhy.reflect.User)。
所以说什么是反射?
反射就是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。
java反射机制给漏洞利用提供了很多便利,我们可以在很多java漏洞的exp中看到它的影子,所以,学习java安全是绕不开它的。
Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
0x02 反射常用API
2.1 获取Class对象
在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。
在 Java API 中,获取 Class 类对象有三种方法:
1 | // 1.通过字符串获取Class对象,这个字符串必须带上完整路径名 |
- 第一种方法是通过类的全路径字符串获取 Class 对象,这也是平时最常用的反射获取 Class 对象的方法;
- 第二种方法有限制条件:需要导入类的包;
- 第三种方法已经有了 User 对象,不再需要反射。
2.2 通过反射创建类对象
通过反射创建类对象主要有两种方式:
第一种:通过 Class 对象的 newInstance() 方法
1 | Class clz = Class.forName("yhy.reflect.User"); |
第二种:通过 Constructor 对象的 newInstance() 方法
1 | Class clz = Class.forName("yhy.reflect.User"); |
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
1 | Class clz = Class.forName("yhy.reflect.User"); |
2.3 通过反射获取类属性、方法、构造器
两个属性:一个公有(age),一个私有(Name)
我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。
1 | Class clz = Class.forName("yhy.reflect.User"); |
而如果使用 Class 对象的 **getDeclaredFields() **方法则可以获取包括私有属性在内的所有属性:
1 | Class clz = Class.forName("yhy.reflect.User"); |
与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。
1 | // 获取所有声明的构造方法 |















