java反射代理安全


视频

反射

1.java中什么是反射(方法,字段)

  • 在类运行时动态获取类的信息,调用对象方法已经操作的字典
  • 使用反射可以在不知道类结构的情况下,动态调用类的方法和访问类的属性

2.反射核心类

下面几个都是java.long包下面的

  • Class:类的元数据,可以获取类的信息,如:方法 字段 构造函数
  • Method:类的方法
  • Field:类的字段
  • Construcror:类的构造函数

3.反射作用

  • 在框架中动态的加载和实例化类
  • 在序列化和反序列化中操作类的属性
  • 在测试框架中动态调用测试方法

4.反射原理

  • 主要通过java反射的核心机制Class类来实现
  • Class.forName()方法获取Class对象
  • 类名.class获取对象

大概的反射代码demo如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package day2;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Demo {
public static void main(String[] args) throws Exception {
Class<?> stringClass = String.class;//使用Class 获取类对象
Constructor<?> constructor = stringClass.getConstructor(String.class);//使用Constructor获取构造函数
String str = (String) constructor.newInstance("反射机制案例");// 构造函数实例化对象
Method lengthMethod = stringClass.getMethod("length");// String类的length()方法
int length = (int) lengthMethod.invoke(str);//获取字符串长度
System.out.println("长度为:" + length);

// 删掉下面这两行反射访问私有字段的代码
// Field valueField = String.class.getDeclaredField("value");
// valueField.setAccessible(true);

// 直接用 String 自带的方法获取字符数组
char[] value = str.toCharArray();
System.out.println("值为:" + new String(value));
}
}

反射优点和缺点

  • 静态编译:在编译时确定好类型
  • 动态编译

优点:

  • 提高程序灵活性
  • 减少代码重复性
  • 提高程序可维护性

缺点:

  • 性能较低
  • 可以造成安全性问题

大概的demo1如下

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package day2;

import java.lang.reflect.Field;

public class Demo1 {

public static void main(String[] args) throws Exception {
Student studentOne = new Student(1, "测试", 12); // 实例化对象 studentOne 并赋值
Student studentTwo = new Student(); // 实例化对象 studentTwo 没有赋值
Class clazz = studentTwo.getClass(); // 获取 Student 类对象
Field[] declaredFields = clazz.getDeclaredFields(); // 获取 Student 类所有属性
for (Field field : declaredFields) {
field.setAccessible(true); // 设置私有属性可见
field.set(studentTwo, field.get(studentOne)); // 使用 set 方法把 studentOne 对象的值赋给 studentTwo 对象
}
System.out.println(studentTwo);
}
}

class Student {
private int id;
private String name;
private int age;

public Student() {
}

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

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

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;
}

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

demo3大概如下,是java反射和序列化相关的内容

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import java.io.*;
import java.lang.reflect.Method;

public class Demo2 implements Serializable {

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
try {
Method method = java.lang.Runtime.class.getMethod("exec", String.class);
Object result = method.invoke(Runtime.getRuntime(), "calc.exe");
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Demo2 demo2 = new Demo2();
fileTest.writer(demo2);
fileTest.read();
}
}

class Person implements Serializable {
private int id;
private String name;
private String password;

public Person() {}

public Person(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}

// Getter and Setter methods (if needed)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

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

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

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

class fileTest {
public static void writer(Object one) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("demo.one"));
oos.writeObject(one);
oos.flush();
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static void read() {
try {
File file = new File("demo.one");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Object x = ois.readObject();
System.out.print(x);
ois.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

大概是这样吧!但是只是看他的这样视频,是没有懂最基础的java开发知识从而得到安全相关的东西

代理

1.java中什么是代理

  • 一种设置模式,主要对其对象提供一个代理用来便于对该对象的控制访问

2.代理分类

  • 静态代理:编译时确定代理类和被代理类的关系
  • 动态代理:运行时生成代理类
  • 目标类:原始对象
  • 代理类:代理模式产生的对象
  • Person对象 Student对象

下面是静态代理的

IIEST.java

1
2
3
4
5
6
7
package daili;

// 公共接口
public interface ITest {
void out();
}

StaticProxy.java

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
package daili;

// 真实主题类(被代理类)
class RealSubject implements daili.ITest {
@Override
public void out() {
System.out.println("out接口实现");
}
}

// 代理类
class Proxy implements daili.ITest {
private RealSubject realSubject;

public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}

@Override
public void out() {
// 调用被代理对象之前的额外操作
System.out.println("调用被代理对象之前的操作");
realSubject.out();
// 调用被代理对象之后的额外操作
System.out.println("调用被代理对象之后的操作");
}
}

// 测试主类
public class StaticProxy {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.out();
}
}


动态代理

一个是什么label的,一个是JDK的

。。。

代理作用

  • 扩大程序功能

反射文章

参考大佬文章
https://blog.csdn.net/ngztx/article/details/142600266?ops_request_misc=elastic_search_misc&request_id=29eaffce9fb5679c8fb9a10fa5897a78&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-142600266-null-null.142^v102^control&utm_term=java%E5%8F%8D%E5%B0%84%E5%AE%89%E5%85%A8&spm=1018.2226.3001.4187

前言

在java rce黑名单绕过还有反序列化的时候都会用到这个反射相关的知识

反射的概念

在Java 程序中,JVM 加载完一个类后,在堆 内存 中就会产生该类的一个 Class 对象,一个类在堆内存中最多只会有一个 Class 对象,这个Class 对象包含了该类的完整结构信息,我们通过这个 Class 对象便可以得到该类的完整结构信息。(注:大写的Class是名为Class的实例对象,先理解Class的存在)

由于JVM为每个加载的类class创建了对应的Class类对象,并在实例中保存了该类class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等。因此,如果获取了某个Class类对象,我们就可以通过这个Class类对象获取到其对应的类class的所有信息,这个过程形象的成为反射。
反射机制是 Java实现动态语言的关键,也就是通过反射实现类的动态加载

Class对象,注意:这个Class类对象是 JVM 内部创建的,如果我们查看 JDK 源码,可以发现Class类的构造方法是private,即只有 JVM 能创建Class类对象,我们程序员自己的 Java 程序是无法创建Class类对象的

1
public final class Class { private Class() {} }

反射组成相关的类

java反射机制组成需要重点注意以下的类:(下面都会讲到具体的使用)

java.lang.Class:类对象;

java.lang.reflect.Constructor:类的构造器对象;

java.lang.reflect.Field:类的属性对象;

java.lang.reflect.Method:类的方法对象;

反射常见的使用方法

获取类的方法:forname

实例化类对象的方法:newInstance

获取函数的方法:getMethod

执行函数的方法:invok

后续会在代码演示中依次介绍 invok()函数在后续漏洞利用中会嵌套使用(个人理解)注意后方高能

为什么要学他

其实从官方定义中就能找到其存在的价值,在运行时获得程序或程序集中每一个类型的成员和成员的信息,从而动态的创建、修改、调用、获取其属性,而不需要事先知道运行的对象是谁。划重点:在运行时而不是编译时。(不改变原有代码逻辑,自行运行的时候动态创建和编译即可

其次,安全应用场景:
构造利用链,触发命令执行
反序列化中的利用链构造
动态获取或执行任意类中的属性或方法
动态代理的底层原理是反射技术
rmi反序列化也涉及到反射操作

代码演示

Java-反射-Class对象类获取

你获取的是.class文件,就是.java稍微编译了一下的那个文件

1
2
3
4
5
//1、根据类名:类名.class Class userClass = User.class; 
//2、根据对象:对象.getClass() User user = new User(); Class aClass = user.getClass();
//3、根据全限定类名:Class.forName("全路径类名") Class aClass1 = Class.forName("com.example.reflectdemo.User");
//4、通过类加载器获得Class对象://ClassLoader.getSystemClassLoader().loadClass("全路径类名"); ClassLoader clsload=ClassLoader.getSystemClassLoader(); Class aClass2 = clsload.loadClass("com.example.reflectdemo.User");

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
package com.example.reflectdemo;

public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1,根据全限定类名:Class.forName("全路径类名“);适用于在运行时动态加载类。当类名是以字符串形式存在时(例如从配置文件或用户输入获取),可以用这种方式来获取类的元信息。
Class aClass =Class.forName("com.example.reflectdemo.User");
System.out.println(aClass);

//2.根据类名:类名.class;适用于在编译时已知类名的情况。相比 Class.forName,这种方式更简单和安全,编译时即可检查类是否存在。
Class userClass = User.class;
System.out.println(userClass);

//3.根据对象:对象.getClass();适用于在运行时已有某个类的实例对象的情况。通过对象的 getClass 方法可以获取到该对象对应的类的 Class 对象。
User user =new User();
Class aClass1 = user.getClass();
System.out.println(aClass1);

//4、通过类加载器获得Class对象://ClassLoader.getSystemClassLoader().loadClass("全路径类名");
//获取系统类加载器;使用类加载器加载指定路径的类,并返回 Class 对象
ClassLoader clsload=ClassLoader.getSystemClassLoader();
Class aClass2 = clsload.loadClass("com.example.reflectdemo.User");
System.out.println(aClass2);


}
}

但是佬的文章有一些方面没有讲清楚
在不是很懂的情况下,我们要完整的刨析一下原理

先直接把代码复制到IDEA里面,运行肯定是会失败的
因为他的结构应该是在软件包com.example.reflectdemo下面,有两个java类
一个是User类,一个是GetClass类
User类的内容大概可以是

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
// 包名必须和文件夹路径一致:com/example/reflectdemo
package com.example.reflectdemo;

/**
* 被反射的User类
* 可以是空类,也可以加属性/方法,不影响获取Class对象
*/
public class User {
// 可选:加一些属性/方法,让类更贴近实际场景(不加也能运行)
private String name;
private int age;

// 无参构造(默认会有,写出来更清晰)
public User() {}

// 有参构造
public User(String name, int age) {
this.name = name;
this.age = age;
}

// 可选:get/set方法(不加不影响核心功能)
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;
}
}

这个时候就会有一些问题了,但是一个com.example.reflectdemo软件包下有User类和GetClass类,我GetClass类的内容中,不是已经写了Class aClass = Class.forName(“com.example.reflectdemo.User”);
System.out.println(“方式1结果:” + aClass);,你都知道有com.example.reflectdemo.User这个了,为什么还要再画蛇添足呢?

1
2
Class aClass = Class.forName("com.example.reflectdemo.User");
System.out.println("方式1结果:" + aClass);

Class.forName(“com.example.reflectdemo.User”)在代码里面申明了要找的地址目标
Class.forName(“com.example.reflectdemo.User”)返回的是User类的Class对象
这个对象里存着 User 类的所有元信息:比如类名、包名、有哪些属性、有哪些方法、构造方法是什么样的;
它不是 User 类的实例(不是new User()创建的那个对象),而是「描述 User 类本身」的特殊对象;
每个类在 JVM 中只有唯一一个这样的 Class 对象(不管用 4 种方式中的哪一种获取,返回的都是同一个

对于Class aClass = Class.forName(“com.example.reflectdemo.User”);
System.out.println(“方式1结果:” + aClass);应该是没有什么问题了

然后还有一点补充的就是
IDEA在原来的语句下面画了黄色的波浪线,Class aClass = Class.forName(“com.example.reflectdemo.User”);
System.out.println(“方式1结果:” + aClass);,然后我按照他的提示点了一下,IDEA自动帮我把它改为了 Class<?> aClass = Class.forName(“com.example.reflectdemo.User”);

Class aClass:相当于你声明了一个 “装任意类元信息的盒子”,但没告诉编译器 “这个盒子能装啥”—— 编译器不确定你要装 User、String 还是其他类的 Class 对象,所以标黄提醒你 “可能不安全”;
Class aClass: 是 “无界通配符”,意思是 “这个盒子可以装任意类的 Class 对象,但我明确告诉编译器‘我知道是任意类型’”—— 这是 Java 5 引入泛型后,处理未知类型 Class 对象的标准、安全写法,所以 IDEA 会自动帮你改成这种形式,消除警告

因为在JVM里面,数组什么的都是以Class的形式存在的,即JVM 里都是以 Class 对象的形式存在的

所以

1
2
3
4
5
6
7
8
9
10
Class aClass1 = Class.forName("com.example.reflectdemo.User");
System.out.println("1. 原始类型:" + aClass1);

// 2. 无界通配符(IDE自动推荐,无警告)
Class<?> aClass2 = Class.forName("com.example.reflectdemo.User");
System.out.println("2. 无界通配符:" + aClass2);

// 3. 明确指定User类型(最严谨)
Class<User> aClass3 = User.class;
System.out.println("3. 指定User类型:" + aClass3);

这三个表示都是可以的

第一个应该是看懂了,没有什么问题了

第二个解释

1
2
Class userClass = User.class;
System.out.println("方式2结果:" + userClass);

User.class;中的User:你定义的 com.example.reflectdemo.User 类的类名
class:Java 特殊语法,直接从 JVM 缓存中获取 User 类的 Class 对象(拿图纸),不是方法、不是属性

第三份解释

1
User user = new User (); Class aClass1 = user.getClass (); System.out.println (aClass1);
1
2
3
4
5
6
7
第一行拆解:User user = new User ();

User:变量类型声明,代表这个变量只能存 User 类的实例对象(具体的 “汽车”);
user:自定义变量名,存放 User 实例的 “盒子”;
=:赋值符号;
new User():创建 User 类的实例对象(造一辆具体的 User 牌 “汽车”),调用 User 的无参构造方法。

1
2
3
4
5
6
7
8
第二行拆解:Class aClass1 = user.getClass ();

Class:变量类型声明,只能存类的 Class 对象(图纸);
aClass1:自定义变量名,存放 Class 对象的 “盒子”;
=:赋值符号;
user:引用上面创建的 User 实例(具体的 “汽车”);
.getClass():所有 Java 对象都有的方法,作用是 “让实例告诉我们它对应的类的图纸(Class 对象)”。就是因为.getClass是让实例化告诉我们,所以我们第一行是先进行实例化

第三行不需要拆解了

方式4:
ClassLoader clsload=ClassLoader.getSystemClassLoader ();
Class aClass2 = clsload.loadClass (“com.example.reflectdemo.User”);
System.out.println (aClass2);

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
第一行拆解:ClassLoader clsload=ClassLoader.getSystemClassLoader ();

ClassLoader:变量类型声明,代表这个变量只能存 “类加载器” 对象(JVM 的 “快递员”);
clsload:自定义变量名,存放类加载器的 “盒子”;
=:赋值符号;
ClassLoader.getSystemClassLoader():获取 JVM 默认的 “系统类加载器”(核心快递员,负责加载 classpath 下的类)。

第一行大白话翻译:
「准备一个装 “快递员” 的盒子(类型 ClassLoader),命名为 clsload,然后把 JVM 的 “系统快递员” 放进这个盒子里。」
第二行拆解:Class aClass2 = clsload.loadClass ("com.example.reflectdemo.User");

Class:变量类型声明,只能存类的 Class 对象(图纸);
aClass2:自定义变量名,存放 Class 对象的 “盒子”;
=:赋值符号;
clsload:引用上面的系统类加载器(快递员);
.loadClass():类加载器的核心方法,接收 “类的全限定名” 字符串,作用是 “让快递员根据地址找类文件,加载到内存并返回 Class 对象”;
"com.example.reflectdemo.User":传给 loadClass 的参数,即 User 类的完整地址(包名 + 类名)。

第二行大白话翻译:
「准备一个装 “类图纸” 的盒子(类型 Class),命名为 aClass2,然后让 clsload 这个 “快递员” 去 “com/example/reflectdemo” 地址找 User 类的文件,找到后加载到内存,把生成的 User 类图纸放进盒子里。」
第三行拆解:System.out.println (aClass2);

System.out.println():标准输出语句;
aClass2:引用上面的变量,即 “装着 User 类图纸的盒子”。

前面不是说了,这三种表示的方式了嘛

1
2
3
4
5
6
7
8
9
10
Class aClass1 = Class.forName("com.example.reflectdemo.User");
System.out.println("1. 原始类型:" + aClass1);

// 2. 无界通配符(IDE自动推荐,无警告)
Class<?> aClass2 = Class.forName("com.example.reflectdemo.User");
System.out.println("2. 无界通配符:" + aClass2);

// 3. 明确指定User类型(最严谨)
Class<User> aClass3 = User.class;
System.out.println("3. 指定User类型:" + aClass3);

所以整个代码也可以换一下

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.reflectdemo;

/**
* 演示获取Class对象的4种方式
* 注意:必须和User类在同一个包下
*/
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:Class.forName("全限定类名") —— 动态加载,运行时找类
Class<?> aClass = Class.forName("com.example.reflectdemo.User");
System.out.println("方式1结果:" + aClass);

// 方式2:类名.class —— 编译时已知类名,最安全
Class<User> userClass = User.class;
System.out.println("方式2结果:" + userClass);

// 方式3:对象.getClass() —— 已有对象,通过对象找类
User user = new User(); // 创建User实例
Class<? extends User> aClass1 = user.getClass();
System.out.println("方式3结果:" + aClass1);

// 方式4:类加载器.loadClass("全限定类名") —— 用类加载器加载类
ClassLoader clsload = ClassLoader.getSystemClassLoader(); // 获取系统类加载器
Class<?> aClass2 = clsload.loadClass("com.example.reflectdemo.User");
System.out.println("方式4结果:" + aClass2);
}
}

这里就只看一下第三种不太一样的
重点拆解泛型 <? extends User>

1
2
3
4
5

?:通配符,代表 “任意类型”;
extends User:限定这个 “任意类型” 必须是 User 类或者 User 的子类(比如如果有class Student extends User {},Student 也符合条件)。

👉 核心含义:aClass1 这个变量只能存放「User 类」或「User 子类」的 Class 对象,不能存 String、Integer 等无关类的 Class 对象 —— 这是比 Class<?> 更精准的类型限定

所以,对于第三种的,
<?>这些也是可以的,或者不要也是可以的

小结:Class对象类的获取,这四种方式还有不同的代码表示应该都是没有任何问题的了

Java-反射-Field成员变量类获取

获取成员变量Field位于java.lang.reflect.Field包中

Field[] getFields() :获取所有public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

Field getField(String name) 获取指定名称的 public修饰的成员变量

Field getDeclaredField(String name) 获取指定的成员变量

第一个获取类的没有什么问题了
上面这个的代码也没有什么太大的问题

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
package com.example.reflectdemo;

import java.lang.reflect.Field;

public class GetFiled {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Class aClass =Class.forName("com.example.reflectdemo.User");
//1.获取public属性的成员变量
// Field[] fields = aClass.getFields(); //接收所有成员变量时用数组接收
// for(Field fd:fields){
// System.out.println(fd);
// }
//2.获取所有的成员变量
Field[] fieldss = aClass.getDeclaredFields();

//3.获取单个的pubilc属性成员变量
Field fd1 = aClass.getField("name");

//4.获取单个的任意属性成员变量
Field fd = aClass.getDeclaredField("job");
System.out.println(fd);

//获取公共成员变量age的值
User u = new User();
Field field=aClass.getField("age");
//取值
Object a=field.get(u);
System.out.println(a);
//赋值
field.set(u,30);
Object aa=field.get(u);
System.out.println(aa);


}
}

Java-反射-Method成员方法类获取

Method getMethod(String name, 类<?>… parameterTypes) //返回该类所声明的public方法

Method getDeclaredMethod(String name, 类<?>… parameterTypes) //返回该类所声明的所有方法

//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型

Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法

Method[] getDeclaredMethods() // 获取该类中的所有方法

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
package com.example.reflectdemo;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetMethod {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class aClass =Class.forName("com.example.reflectdemo.User");

//1.获取包括继承的公共成员方法
Method[] methods = aClass.getMethods();
for (Method m:methods){
System.out.println(m);
}

//2.获取不包括继承的所有成员方法
Method[] method = aClass.getDeclaredMethods();
for(Method me:method){
System.out.println(me);
}

//3.获取单个的成员方法,通过方法名和参数区分是哪个方法
Method users = aClass.getDeclaredMethod("users", String.class);
System.out.println(users);

//对成员方法进行执行
User u = new User();
Method user = aClass.getDeclaredMethod("users", String.class);
user.invoke(u,"gay1");//调用






}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void com.example.reflectdemo.User.useinfo(java.lang.String,int,java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
protected void com.example.reflectdemo.User.users(java.lang.String)
public void com.example.reflectdemo.User.useinfo(java.lang.String,int,java.lang.String)
protected void com.example.reflectdemo.User.users(java.lang.String)
users成员方法:gay1

Java-反射-Constructor构造方法类获取

Constructor<?>[] getConstructors() :只返回public构造函数

Constructor<?>[] getDeclaredConstructors() :返回所有构造函数

Constructor<> getConstructor(类<?>… parameterTypes) : 匹配和参数配型相符的public构造函数

Constructor<> getDeclaredConstructor(类<?>… parameterTypes) : 匹配和参数配型相符的构造函数

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
package com.example.reflectdemo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class GetConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class aClass =Class.forName("com.example.reflectdemo.User");

//1.获取公共的构造方法
Constructor[] constructors = aClass.getConstructors();
for(Constructor con:constructors){
System.out.println(con);
}

//2.获取所有的构造方法
Constructor[] constructorss = aClass.getDeclaredConstructors();
for(Constructor con:constructorss){
System.out.println(con);
}

//3.获取单个pubilc构造方法 or 获取带相同参数的成员方法
Constructor constructor = aClass.getConstructor(String.class);
System.out.println(constructor);

//4.获取单个构造方法
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class,int.class);
System.out.println(declaredConstructor);

//5.对构造方法进行操作(两个参数String,int)
Constructor con2=aClass.getDeclaredConstructor(String.class,int.class);
//临时开启对私有的访问
con2.setAccessible(true);
User uu=(User) con2.newInstance("congratulation",30);
System.out.println(uu);

//6.对构造方法进行操作(1个参数String)
Constructor con3=aClass.getDeclaredConstructor(String.class);
con3.newInstance("123");
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
public com.example.reflectdemo.User(java.lang.String)
public com.example.reflectdemo.User()
private com.example.reflectdemo.User(java.lang.String,int)
public com.example.reflectdemo.User(java.lang.String)
public com.example.reflectdemo.User()
public com.example.reflectdemo.User(java.lang.String)
private com.example.reflectdemo.User(java.lang.String,int)
congratulation
30
com.example.reflectdemo.User@4554617c
我的名字123

Java-反射-不安全命令执行&反序列化链构造

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
package com.example.reflectdemo;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetRunExec {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//原生调用 JDK自带的rt.jar
//Runtime.getRuntime().exec("calc");


//如果是第三方的jar包呢
Class aClass = Class.forName("java.lang.Runtime");
//获取所有公共包括继承的成员方法
// Method[] methods = aClass.getMethods();
// for(Method me:methods){
// System.out.println(me);
// }
//获取exec成员方法
Method exec = aClass.getMethod("exec", String.class);
//获取getRuntime成员方法
Method getRuntimeMethod = aClass.getMethod("getRuntime");
//调用 getRuntime 方法 返回给 Runtime 的实例
Object runtime = getRuntimeMethod.invoke(aClass);//相当于Runtime.getRuntime()
//调用 exec 方法执行命令 "calc.exe"
exec.invoke(runtime, "calc.exe");
//相当于Runtime.getRuntime().exec("calc");



}
}

刚刚说的invoke()函数的高能使用在这里,之后会在各种绕过中使用这种方法,这次一定要理解懂
通过java反射机制的学习,能够为我们为我们后面的java漏洞分析调试,java漏洞 poc 测试和服务端模板引擎注入等有着十分重要的意义

到这里还是非常基础的知识,如果还是有一些开发方面不太懂的,可以看黑马程序员的java下部191~199的课程
之前我是快速过了一遍,所以得回来补

黑马程序员反射

建议前面开发部分没有看懂的,就跟着阿伟老师手敲一下代码

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
# 1. 反射

## 1.1 反射的概述:

​ **专业的解释(了解一下):**

​ 是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;

​ 对于任意一个对象,都能够调用它的任意属性和方法;

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

​ **通俗的理解:(掌握)**

* 利用**反射**创建的对象**可以无视修饰符**调用类里面的内容

* 可以跟**配置文件结合起来使用**,把要创建的对象信息和方法写在配置文件中。

读取到什么类,就创建什么类的对象

读取到什么方法,就调用什么方法

此时当需求变更的时候不需要修改代码,只要修改配置文件即可。

## 1.2 学习反射到底学什么?

反射都是从class字节码文件中获取的内容。

* 如何获取class字节码文件的对象
* 利用反射如何获取构造方法(创建对象)
* 利用反射如何获取成员变量(赋值,获取值)
* 利用反射如何获取成员方法(运行)

## 1.3 获取字节码文件对象的三种方式

* Class这个类里面的静态方法forName(“全类名”)**(最常用)**
* 通过class属性获取
* 通过对象获取字节码文件对象

代码示例:

java
//1.Class这个类里面的静态方法forName
//Class.forName("类的全类名"): 全类名 = 包名 + 类名
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
//源代码阶段获取 --- 先把Student加载到内存中,再获取字节码文件的对象
//clazz 就表示Student这个类的字节码文件对象。
//就是当Student.class这个文件加载到内存之后,产生的字节码文件对象


//2.通过class属性获取
//类名.class
Class clazz2 = Student.class;

//因为class文件在硬盘中是唯一的,所以,当这个文件加载到内存之后产生的对象也是唯一的
System.out.println(clazz1 == clazz2);//true


//3.通过Student对象获取字节码文件对象
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz2 == clazz3);//true


## 1.4 字节码文件和字节码文件对象

java文件:就是我们自己编写的java代码。

字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)

字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。

​ 这个对象里面至少包含了:构造方法,成员变量,成员方法。

而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是唯一的。

## 1.5 获取构造方法

规则:

​ get表示获取

​ Declared表示私有

​ 最后的s表示所有,复数形式

​ 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

| 方法名 | 说明 |
| ------------------------------------------------------------ | --------------------------------- |
| Constructor<?>[] getConstructors() | 获得所有的构造(只能public修饰) |
| Constructor<?>[] getDeclaredConstructors() | 获得所有的构造(包含private修饰) |
| Constructor<T> getConstructor(Class<?>... parameterTypes) | 获取指定构造(只能public修饰) |
| Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 获取指定构造(包含private修饰) |

代码示例:

java
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获得整体(class字节码文件对象)
Class clazz = Class.forName("com.itheima.reflectdemo.Student");


//2.获取构造方法对象
//获取所有构造方法(public)
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}

System.out.println("=======================");

//获取所有构造(带私有的)
Constructor[] constructors2 = clazz.getDeclaredConstructors();

for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
System.out.println("=======================");

//获取指定的空参构造
Constructor con1 = clazz.getConstructor();
System.out.println(con1);

Constructor con2 = clazz.getConstructor(String.class,int.class);
System.out.println(con2);

System.out.println("=======================");
//获取指定的构造(所有构造都可以获取到,包括public包括private)
Constructor con3 = clazz.getDeclaredConstructor();
System.out.println(con3);
//了解 System.out.println(con3 == con1);
//每一次获取构造方法对象的时候,都会新new一个。

Constructor con4 = clazz.getDeclaredConstructor(String.class);
System.out.println(con4);
}
}


## 1.6 获取构造方法并创建对象

涉及到的方法:newInstance

代码示例:

java
//首先要有一个javabean类
public class Student {
private String name;

private int age;


public Student() {

}

public Student(String name) {
this.name = name;
}

private Student(String name, int age) {
this.name = name;
this.age = age;
}


/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public int getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}

public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}



//测试类中的代码:
//需求1:
//获取空参,并创建对象

//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取空参的构造方法
Constructor con = clazz.getConstructor();
//3.利用空参构造方法创建对象
Student stu = (Student) con.newInstance();
System.out.println(stu);


System.out.println("=============================================");


//测试类中的代码:
//需求2:
//获取带参构造,并创建对象
//1.获取整体的字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取有参构造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.临时修改构造方法的访问权限(暴力反射)
con.setAccessible(true);
//4.直接创建对象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);


## 1.7 获取成员变量

规则:

​ get表示获取

​ Declared表示私有

​ 最后的s表示所有,复数形式

​ 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

方法名:

| 方法名 | 说明 |
| ----------------------------------- | -------------------------------------------- |
| Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
| Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
| Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
| Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |

代码示例:

java
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//获取成员变量对象

//1.获取class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");

//2.获取成员变量的对象(Field对象)只能获取public修饰的
Field[] fields1 = clazz.getFields();
for (Field field : fields1) {
System.out.println(field);
}

System.out.println("===============================");

//获取成员变量的对象(public + private)
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}

System.out.println("===============================");
//获得单个成员变量对象
//如果获取的属性是不存在的,那么会报异常
//Field field3 = clazz.getField("aaa");
//System.out.println(field3);//NoSuchFieldException

Field field4 = clazz.getField("gender");
System.out.println(field4);

System.out.println("===============================");
//获取单个成员变量(私有)
Field field5 = clazz.getDeclaredField("name");
System.out.println(field5);

}
}



public class Student {
private String name;

private int age;

public String gender;

public String address;


public Student() {
}

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


public Student(String name, int age, String gender, String address) {
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public int getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}

/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}

/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}

/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}

/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}

public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
}
}



## 1.8 获取成员变量并获取值和修改值

| 方法 | 说明 |
| ----------------------------------- | ------ |
| void set(Object obj, Object value) | 赋值 |
| Object get(Object obj) | 获取值 |

代码示例:

java
public class ReflectDemo5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Student s = new Student("zhangsan",23,"广州");
Student ss = new Student("lisi",24,"北京");

//需求:
//利用反射获取成员变量并获取值和修改值

//1.获取class对象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");

//2.获取name成员变量
//field就表示name这个属性的对象
Field field = clazz.getDeclaredField("name");
//临时修饰他的访问权限
field.setAccessible(true);

//3.设置(修改)name的值
//参数一:表示要修改哪个对象的name?
//参数二:表示要修改为多少?
field.set(s,"wangwu");

//3.获取name的值
//表示我要获取这个对象的name的值
String result = (String)field.get(s);

//4.打印结果
System.out.println(result);

System.out.println(s);
System.out.println(ss);

}
}


public class Student {
private String name;
private int age;
public String gender;
public String address;


public Student() {
}

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


public Student(String name, int age, String gender, String address) {
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public int getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}

/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}

/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}

/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}

/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}

public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
}
}



## 1.9 获取成员方法

规则:

​ get表示获取

​ Declared表示私有

​ 最后的s表示所有,复数形式

​ 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

| 方法名 | 说明 |
| ------------------------------------------------------------ | -------------------------------------------- |
| Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
| Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
| Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
| Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象,存在就能拿到 |

代码示例:

java
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获取class对象
Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");


//2.获取方法
//getMethods可以获取父类中public修饰的方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}

System.out.println("===========================");
//获取所有的方法(包含私有)
//但是只能获取自己类中的方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}

System.out.println("===========================");
//获取指定的方法(空参)
Method method3 = clazz.getMethod("sleep");
System.out.println(method3);

Method method4 = clazz.getMethod("eat",String.class);
System.out.println(method4);

//获取指定的私有方法
Method method5 = clazz.getDeclaredMethod("playGame");
System.out.println(method5);
}
}


## 1.10 获取成员方法并运行

方法

Object invoke(Object obj, Object... args) :运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

代码示例:

java
package com.itheima.a02reflectdemo1;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.获取字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");

//2.获取一个对象
//需要用这个对象去调用方法
Student s = new Student();

//3.获取一个指定的方法
//参数一:方法名
//参数二:参数列表,如果没有可以不写
Method eatMethod = clazz.getMethod("eat",String.class);

//运行
//参数一:表示方法的调用对象
//参数二:方法在运行时需要的实际参数
//注意点:如果方法有返回值,那么需要接收invoke的结果
//如果方法没有返回值,则不需要接收
String result = (String) eatMethod.invoke(s, "重庆小面");
System.out.println(result);

}
}



public class Student {
private String name;
private int age;
public String gender;
public String address;


public Student() {

}

public Student(String name) {
this.name = name;
}

private Student(String name, int age) {
this.name = name;
this.age = age;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public int getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}

public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}

private void study(){
System.out.println("学生在学习");
}

private void sleep(){
System.out.println("学生在睡觉");
}

public String eat(String something){
System.out.println("学生在吃" + something);
return "学生已经吃完了,非常happy";
}
}


## 面试题:

​ 你觉得反射好不好?好,有两个方向

​ 第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。

​ 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

## 1.11 练习泛型擦除(掌握概念,了解代码)

理解:(掌握)

​ 集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。

代码示例:(了解)

java
package com.itheima.reflectdemo;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectDemo8 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.创建集合对象
ArrayList<Integer> list = new ArrayList<>();
list.add(123);
// list.add("aaa");

//2.利用反射运行add方法去添加字符串
//因为反射使用的是class字节码文件

//获取class对象
Class clazz = list.getClass();

//获取add方法对象
Method method = clazz.getMethod("add", Object.class);

//运行方法
method.invoke(list,"aaa");

//打印集合
System.out.println(list);
}
}


## 1.12 练习:修改字符串的内容(掌握概念,了解代码)

在这个练习中,我需要你掌握的是字符串不能修改的真正原因。

字符串,在底层是一个byte类型的字节数组,名字叫做value

java
private final byte[] value;


真正不能被修改的原因:final和private

final修饰value表示value记录的地址值不能修改。

private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。

如果要强行修改可以用反射:

代码示例:(了解)

java
String s = "abc";
String ss = "abc";
// private final byte[] value= {97,98,99};
// 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值
// 如果我们利用反射获取了value的地址值。
// 也是可以修改的,final修饰的value
// 真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,比较危险

//1.获取class对象
Class clazz = s.getClass();

//2.获取value成员变量(private)
Field field = clazz.getDeclaredField("value");
//但是这种操作非常危险
//JDK高版本已经屏蔽了这种操作,低版本还是可以的
//临时修改权限
field.setAccessible(true);

//3.获取value记录的地址值
byte[] bytes = (byte[]) field.get(s);
bytes[0] = 100;

System.out.println(s);//dbc
System.out.println(ss);//dbc


## 1.13 练习,反射和配置文件结合动态获取的练习(重点)

需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

①通过Properties加载配置文件

②得到类名和方法名

③通过类名反射得到Class对象

④通过Class对象创建一个对象

⑤通过Class对象得到方法

⑥调用方法

代码示例:

java
public class ReflectDemo9 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.读取配置文件的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("day14-code\\prop.properties");
prop.load(fis);
fis.close();
System.out.println(prop);

String classname = prop.get("classname") + "";
String methodname = prop.get("methodname") + "";

//2.获取字节码文件对象
Class clazz = Class.forName(classname);

//3.要先创建这个类的对象
Constructor con = clazz.getDeclaredConstructor();
con.setAccessible(true);
Object o = con.newInstance();
System.out.println(o);

//4.获取方法的对象
Method method = clazz.getDeclaredMethod(methodname);
method.setAccessible(true);

//5.运行方法
method.invoke(o);


}
}

配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep


## 1.14 利用发射保存对象中的信息(重点)

java
public class MyReflectDemo {
public static void main(String[] args) throws IllegalAccessException, IOException {
/*
对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去
*/
Student s = new Student("小A",23,'女',167.5,"睡觉");
Teacher t = new Teacher("播妞",10000);
saveObject(s);
}

//把对象里面所有的成员变量名和值保存到本地文件中
public static void saveObject(Object obj) throws IllegalAccessException, IOException {
//1.获取字节码文件的对象
Class clazz = obj.getClass();
//2. 创建IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\a.txt"));
//3. 获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//获取成员变量的名字
String name = field.getName();
//获取成员变量的值
Object value = field.get(obj);
//写出数据
bw.write(name + "=" + value);
bw.newLine();
}

bw.close();

}
}


java
public class Student {
private String name;
private int age;
private char gender;
private double height;
private String hobby;

public Student() {
}

public Student(String name, int age, char gender, double height, String hobby) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
this.hobby = hobby;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return age
*/
public int getAge() {
return age;
}

/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}

/**
* 获取
* @return gender
*/
public char getGender() {
return gender;
}

/**
* 设置
* @param gender
*/
public void setGender(char gender) {
this.gender = gender;
}

/**
* 获取
* @return height
*/
public double getHeight() {
return height;
}

/**
* 设置
* @param height
*/
public void setHeight(double height) {
this.height = height;
}

/**
* 获取
* @return hobby
*/
public String getHobby() {
return hobby;
}

/**
* 设置
* @param hobby
*/
public void setHobby(String hobby) {
this.hobby = hobby;
}

public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
}
}


java
public class Teacher {
private String name;
private double salary;

public Teacher() {
}

public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

/**
* 获取
* @return salary
*/
public double getSalary() {
return salary;
}

/**
* 设置
* @param salary
*/
public void setSalary(double salary) {
this.salary = salary;
}

public String toString() {
return "Teacher{name = " + name + ", salary = " + salary + "}";
}
}



# 2. 动态代理

## 2.1 好处:

​ 无侵入式的给方法增强功能

## 2.2 动态代理三要素:

1,真正干活的对象

2,代理对象

3,利用代理调用方法

切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

## 2.3 代码实现:

java
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱一首歌
1. 获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2. 再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
//1. 获取代理的对象
BigStar bigStar = new BigStar("鸡哥");
Star proxy = ProxyUtil.createProxy(bigStar);

//2. 调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}


java
/*
*
* 类的作用:
* 创建一个代理
*
* */
public class ProxyUtil {
/*
*
* 方法的作用:
* 给一个明星的对象,创建一个代理
*
* 形参:
* 被代理的明星对象
*
* 返回值:
* 给明星创建的代理
*
*
*
* 需求:
* 外面的人想要大明星唱一首歌
* 1. 获取代理的对象
* 代理对象 = ProxyUtil.createProxy(大明星的对象);
* 2. 再调用代理的唱歌方法
* 代理对象.唱歌的方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
//参数三:用来指定生成的代理对象要干什么事情
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:要运行的方法 sing
* 参数三:调用sing方法时,传递的实参
* */
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}


java
public interface Star {
//我们可以把所有想要被代理的方法定义在接口当中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}


java
public class BigStar implements Star {
private String name;


public BigStar() {
}

public BigStar(String name) {
this.name = name;
}

//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}

//跳舞
@Override
public void dance(){
System.out.println(this.name + "正在跳舞");
}

/**
* 获取
* @return name
*/
public String getName() {
return name;
}

/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}

public String toString() {
return "BigStar{name = " + name + "}";
}
}



## 2.4 额外扩展

动态代理,还可以拦截方法

比如:

​ 在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。

​ 但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。

java
/*
* 类的作用:
* 创建一个代理
* */
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("cleanWC".equals(method.getName())){
System.out.println("拦截,不调用大明星的方法");
return null;
}
//如果是其他方法,正常执行
return method.invoke(bigStar,args);
}
}
);
return star;
}
}


## 2.5 动态代理的练习

​ 对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强

java
public class MyProxyDemo1 {
public static void main(String[] args) {
//动态代码可以增强也可以拦截
//1.创建真正干活的人
ArrayList<String> list = new ArrayList<>();

//2.创建代理对象
//参数一:类加载器。当前类名.class.getClassLoader()
// 找到是谁,把当前的类,加载到内存中了,我再麻烦他帮我干一件事情,把后面的代理类,也加载到内存

//参数二:是一个数组,在数组里面写接口的字节码文件对象。
// 如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截
// 但是,一定要写ArrayList真实实现的接口
// 假设在第二个参数中,写了MyInter接口,那么是错误的。
// 因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截
//参数三:用来创建代理对象的匿名内部类
List proxyList = (List) Proxy.newProxyInstance(
//参数一:类加载器
MyProxyDemo1.class.getClassLoader(),
//参数二:是一个数组,表示代理对象能代理的方法范围
new Class[]{List.class},
//参数三:本质就是代理对象
new InvocationHandler() {
@Override
//invoke方法参数的意义
//参数一:表示代理对象,一般不用(了解)
//参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截
//参数三:就是下面第三步调用方法时,传递的参数。
//举例1:
//list.add("阿玮好帅");
//此时参数二就是add这个方法名
//此时参数三 args[0] 就是 阿玮好帅
//举例2:
//list.set(1, "aaa");
//此时参数二就是set这个方法名
//此时参数三 args[0] 就是 1 args[1]"aaa"
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//对add方法做一个增强,统计耗时时间
if (method.getName().equals("add")) {
long start = System.currentTimeMillis();
//调用集合的方法,真正的添加数据
method.invoke(list, args);
long end = System.currentTimeMillis();
System.out.println("耗时时间:" + (end - start));
//需要进行返回,返回值要跟真正增强或者拦截的方法保持一致
return true;
}else if(method.getName().equals("remove") && args[0] instanceof Integer){
System.out.println("拦截了按照索引删除的方法");
return null;
}else if(method.getName().equals("remove")){
System.out.println("拦截了按照对象删除的方法");
return false;
}else{
//如果当前调用的是其他方法,我们既不增强,也不拦截
method.invoke(list,args);
return null;
}
}
}
);

//3.调用方法
//如果调用者是list,就好比绕过了第二步的代码,直接添加元素
//如果调用者是代理对象,此时代理才能帮我们增强或者拦截

//每次调用方法的时候,都不会直接操作集合
//而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截
proxyList.add("aaa");
proxyList.add("bbb");
proxyList.add("ccc");
proxyList.add("ddd");

proxyList.remove(0);
proxyList.remove("aaa");


//打印集合
System.out.println(list);
}
}



ctf赛题

1
NSS,BUU,ctfshow,vilfocus这个CTF靶场关于java的反射和代理有没有什么题目,先不要来和反序列化相关的

我叫ai帮我爬了一下,然后做下那些题目

但是就如文章开篇说的,这个是非常基础的一个知识点,所以可能单独一个方面的题目就比较少,同时相关的代码审计的文章(如公众号菜狗安全什么的,相关的基础就是序列化反序列化的)


文章作者: wuk0Ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuk0Ng !
评论
  目录