不能食言,再晚也要写
✔ 0x01 Java反射
反射之中包含了一个「反」字,有「反」就会有「正」,那么解释反射就必须先从「正」开始解释。
一般情况下,当使用某个类时必定知道它是什么类(类名),是用来做什么的(类的属性和方法)。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
User user = new User();
user.setName("yhy");
System.out.println(user.getName());
"正射"就是通过new创建了一个User实例,然后通过实例(user)去调用其所属方法。
但是当你new的时候不知道类名怎么办?受private保护的方法怎么调用?这时候反射的作用就体现出来了。
// 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.通过字符串获取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() 方法
Class clz = Class.forName("yhy.reflect.User");
Object object = clz.newInstance();
第二种:通过 Constructor 对象的 newInstance() 方法
Class clz = Class.forName("yhy.reflect.User");
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
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 类的属性,但无法获取私有属性。
Class clz = Class.forName("yhy.reflect.User");
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:
Class clz = Class.forName("yhy.reflect.User");
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。
// 获取所有声明的构造方法
getDeclaredConstructors()
// 获取所有公有的构造方法
getConstructors()
// 获取所有声明的方法
getDeclaredMethods()
// 获取所有公有的函数
getMethods()
代码地址:https://github.com/yhy0/JavaSerializeDemo
免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。
来都来了,不关注一波?
本文始发于微信公众号(谁不想当剑仙):初探Java反序列化漏洞(二)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论