JAVA-Reflect
# Java 反射
JAVA 安全基础(二)-- 反射机制 - 先知社区 (aliyun.com)
反射是 Java 的特征之一,是一种间接操作目标对象的机制,核心是 JVM 在运行状态的时候才动态加载类,对于任意一个类都能够知道这个类所有的属性和方法,并且对于任意一个对象,都能够调用它的方法 / 访问属性。这种动态获取信息以及动态调用对象方法的功能成为 Java 语言的反射机制。** 通过使用反射我们不仅可以获取到任何类的成员方法 (Methods)、成员变量 (Fields)、构造方法 (Constructors) 等信息,还可以动态创建 Java 类实例、调用任意的类方法、修改任意的类成员变量值等。** 反射使原生 java 变成动态编译
反射:将类的各个组成部分封装为其他对象,这就是反射机制。
1、可以在程序运行过程中,操作这些对象。
2、可以解耦,提高程序的可扩展性。
为什么用到反射:
从官方定义中可以找到,在运行时获得程序或程序集中每一个类型的成员和成员的信息,从而动态的创建、修改、调用、获取其属性,而不需要事先知道运行的对象是谁。划重点:在运行时而不是编译时。(不改变原有代码逻辑,自行运行的时候动态创建和编译即可)
# 反射获取 Class 类
Java 虽不像 PHP 那么灵活,但其提供的 “反射” 功能,也是可以提供⼀些动态特性。⽐如,这样⼀段代码,在你不知道传⼊的参数值的时候,你是不知道他的作⽤是什么的:
1 | public void execute(String className, String methodName) throws Exception { |
⼏个在反射⾥极为重要的⽅法:
- 获取类的⽅法:
forName
- 实例化类对象的⽅法:
newInstance
- 获取函数的⽅法:
getMethod
- 执⾏函数的⽅法:
invoke
基本上,这⼏个⽅法包揽了 Java 安全⾥各种和反射有关的 Payload。
forName
不是获取 “类” 的唯⼀途径,通常来说我们有如下三种⽅式获取⼀个 “类”,也就是 java.lang.Class
对象:
obj.getClass()
如果上下⽂中存在某个类的实例 obj ,那么我们可以直接通过obj.getClass()
来获取它的类,但使用实例化对象的getClass()
方法,需要本身创建一个对象,本身就没有了使用反射机制意义。Test.class
如果你已经加载了某个类,只是想获取到它的java.lang.Class
对象,那么就直接拿它的class
属性即可。这个⽅法其实不属于反射。Class.forName
如果你知道某个类的名字,想获取到这个类,就可以使⽤forName
来获取- 通过类加载器
ClassLoader
获取类,但极少用到
# 获取 Field 成员变量
Class 类中用于获取成员变量的方法
Field[] getFields()
: 返回所有公共成员变量对象的数组Field[] getDeclaredFields()
: 返回所有成员变量对象的数组Field getField(String name)
: 返回单个公共成员变量对象Field getDeclaredField(Stringname)
: 返回单个成员变量对象
Field 类中用于创建对象的方法
Object get(Object obj)
获取值void set(Object obj, Object value)
: 赋值
获取成员变量是为了获取值或赋值
# 获取 Constructor 构造方法
Class 类中用于获取构造方法的方法
Constructor[] getConstructors()
: 返回所有公共构造方法对象的数组Constructor[] getDeclaredConstructors()
: 返回所有构造方法对象的数组Constructor getConstructor(Class.. parameterTypes)
: 返回单个公共构造方法对象Constructor getDeclaredConstructor(Class.. parameterTypes)
: 返回单个构造方法对象
Constructor 类中用于创建对象的方法
T newInstance(0bject...initargs)
: 根据指定的构造方法创建对象setAccessible(boolean flag)
: 设置为 true, 表示取消访问检查
newInstance 方法:
- newInstance 方法是 java.lang.reflect.Constructor 类的一个方法,用于创建新的类实例。
- 它通过调用类的构造方法来实例化对象。
- newInstance 方法通常用于动态创建对象,尤其是在反射时,允许在运行时通过 Constructor 对象调用类的构造方法。
setAccessible (true) 方法:
- setAccessible 方法是 java.lang.reflect.AccessibleObject 类的一个方法,用于启用或禁用 Java 语言访问检查。
- 当 setAccessible (true) 被调用时,表示反射对象在使用时取消了访问权限检查,可以访问类的私有成员。
- 这通常用于访问那些受到访问控制限制的类的私有成员(字段、方法、构造方法等)。
# 获取 Method 成员方法
Class 类中用于获取成员方法的方法
Method[] getMethods()
: 返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods()
: 返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class... parameterTypes)
: 返回单个公共成员方法对象
Method getDeclaredMethod(String name,Class..parameterTypes)
: 返回单个成员方法对象
Method 类中用于创建对象的方法
Object invoke(Object obj, Object... args)
: 运行方法
参数一:用 obj 对象调用该方法
参数二:调用方法的传递的参数 (如果没有就不写)
返回值:方法的返回值 (如果没有就不写)
# 反射 - 命令执行
原型:java 自带包含有的 java.lang 中有对于本机控制台的调用方法
如果是第三方 jar 包呢
- 首先使用 Class.forName 获取 java.lang.Runtime 类
- 通过 getMethods 获取该类包括继承的公共成员方法,并遍历出来
1 | // 使用 Class.forName 获取 java.lang.Runtime 类 |
- 通过查询找到对应需要的成员方法
- 根据成员方法的名称和参数,分别对应获取所需成员方法
- 通过使用获取的方法依次调用执行
# 不安全的反射
应用程序使用具有反射功能的外部输入来选择要使用的类或代码,可能被攻击者利用而输入或选择不正确的类或代码。
1 | public void ref(String name) throws Exception { |
如果用户可以控制 name 的输入,就有可能构建恶意代码造成严重的漏洞
后面应该就是反序列化了
- Title: JAVA-Reflect
- Author: Fc04dB
- Created at : 2024-08-28 15:00:59
- Updated at : 2024-08-29 16:18:50
- Link: https://redefine.ohevan.com/2024/08/28/JAVA-Reflect/
- License: This work is licensed under CC BY-NC-SA 4.0.