ToDo-Java安全漫谈
01 反射(1)
反射:运行时才知道要操作的类具体是什么:程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法
Java安全:反序列化漏洞——可以从反射开始说起
反射是⼤多数语⾔⾥都必不可少的组成部分
对象通过反射——获取他的类——类通过反射拿到所有⽅法(包括私有)——拿到的⽅法可以调⽤
通过“反射”,可以将Java这种静态语⾔附加上动态特性
“动态特性”——⼀段代码,改变其中的变量,将会导致这段代码产⽣功能性的变化
⽐如:⼀段代码,在你不知道传⼊的参数值的时候,你是不知道他的作⽤是什么的
1java.lang.class
- java.lang.class 是一个重要的类,是 java 反射机制的核心,其允许
- 运行时动态获取类信息
- 具体包括:
- 类名、字段(成员变量)、类方法、类构造方法、父类、接口
1.1 常用方法
- 类
- 类的对象:是 Java 的反射机制中,用来表示类的元数据(即关于类的结构、字段、方法等信息)的对象
- 每个类都有一个与之关联的
Class
对象,包含了类的信息,如类的名称、方法、字段、构造函数等
- 每个类都有一个与之关联的
- 类的实例: 是类的具体对象,是类的一个实例化对象(通过调用构造函数创建)
- 实例拥有类的属性和方法,可以执行类定义的行为
在反射中,clazz
是一个 Class
类型的对象,它代表了一个类的信息。你可以通过 Class
对象来获取类的方法、字段、构造函数等。
1 | public void execute(String className, String methodName) throws Exception { |
Class.forName("com.example.Hello");
:根据类的完全限定名(包括包名)加载类- 如果不存在会抛异常
clazz.getMethod(methodName)
:获取类的指定公有方法(只接受方法名,不能携带参数)- 目的是:运行时动态获取类的方法、方便后续的执行(invoke)
- getMethod 可以获取类的 Method 对象,然后可以用 invoke 调用它
- 如果不指定方法名会返回一个方法数组:该类的所有公用方法
Method[] methods = clazz.getMethods();
clazz.getDeclaredMethod(String name, Class<?>... parameterTypes)
:可以携带参数- 可以获取私有方法
clazz.newInstance()
:使用反射创建一个新的类实例=调用类的无参构造方法.invoke(obj,para)
:是 Method 对象的一个方法,参数是方法调用需要的目标对象&调用方法的参数- obj:调用哪个对象的该方法
- para:传入的参数数组,可为 null
总结:
clazz.getMethod(methodName)
通过反射获取了 clazz 类中名为methodName公用方法——得到一个 Method 对象clazz.newInstance()
:通过反射创建类clazz
的一个新的实例=调用类的无参构造方法,创建该类的一个对象。invoke(clazz.newInstance())
:调用 Method 对象,其是一个名为methodName
的方法。invoke
方法会执行clazz
类的methodName
方法,并且在这个方法上使用clazz.newInstance()
创建的实例作为目标对象。
1.2 获取类对象的方法
obj.getClass()
:通过一个实例获取其类对象- 动态获取
- 场景: 有一个对象,并且想知道这个对象是什么类型
<font style="color:rgb(51,51,51);">Test.class()</font>
如果你已经加载了某个类,只是想获取到它的 java.lang.Class 对象- 那么就直接拿它的 class 属性即可。这个⽅法其实不属于反射(效率很高,编译时就知道)
- 场景: 这种方式适用于你已经知道某个类的名字,并且在编译时就能确定
<font style="color:rgb(51,51,51);">Class.forName()</font>
如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName 来获取- 通过完全限定名动态获取
- 场景:当你在运行时才知道类的名称,或者类名是动态提供的
3 沙盒绕过 ctf 例题
4forName 重载
使用 forName()
会自动初始化该 Class 对象
forName有两个函数重载:
Class<?> forName(String name)
- 第⼀个就是常⻅获取class的⽅式,可以理解为第⼆种⽅式的封装
- 等价于:
Class.forName(name, true, Thread.currentThread().getContextClassLoader());
Class<?> forName(String name, **boolean** initialize, ClassLoader loader)
- 第⼀个参数是类名;第⼆个参数表示是否初始化;第三个参数就是 ClassLoader
- ClassLoader就是⼀个“加载器”,告诉Java虚拟机如何加载这个类
- Java默认的 ClassLoader 就是根据类名来加载类(类名需要是完整路径)
加载和初始化
- 加载:把类的.class 文件加载到 JVM 内容,Class 对象已被创建
- 初始化:执行类的静态代码块,和静态变量(在类加载时执行,且只执行一次)
- 加载但不初始化好处:
- 1 反射:用 ClassLoader 加载类,获取其元信息、无需执行其静态代码块。
- eg:加载一个工具类,只需要调用方法,但不需要执行其静态代码
- 2 性能优化、延迟加载、避免静态代码被执行…
- 1 反射:用 ClassLoader 加载类,获取其元信息、无需执行其静态代码块。