概念

反射是 Java 的特征之一,是一种简介操作目标对象的机制,其核心是 JVM 在运行状态时才动态地加载类(任意一个类都能够知道这个类所有的属性和方法,并且都能够调用这个类的方法和访问属性)。这种动态获取信息以及动态调用对象方法的功能成为 Java 的反射机制。

作用

在程序运行前,不需要知道运行的对象是谁,在程序运行时,获得每一个类型的成员和成员的信息,达到动态创建、修改、调用、获取其属性的目的(在运行时而不是编译时,不改变原有代码逻辑,自行运行时动态创建和编译)

流程图

使用场景

  • 构造利用链,触发命令执行

  • 反序列化的利用链构造

  • 动态地获取或执行任意类当中的属性或方法

  • rmi反序列化

  • 动态代理的底层原理也用到了反射技术

方法

User 类为例(在每个软件包中创建这个类)

public class User {
    public String Name;

    public String Sex;

    private int Age;

    protected String Address;

    public User() {}

    public User(String name, String sex){
        Name = name;
        Sex = sex;
    }

    private User(String name, String sex, int age){
        Name = name;
        Sex = sex;
    }

    protected User(String name, String sex, int age, String address){
        Name = name;
        Sex = sex;
        Age = age;
        Address = address;
    }

    private void Say(String Job, int PhoneNumber) {
        System.out.println("My name is " + Name);
        System.out.println("My age is " + Age);
        System.out.println("My Job is " + Job);
        System.out.println("My PhoneNumber is " + PhoneNumber);
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getSex() {
        return Sex;
    }

    public void setSex(String sex) {
        Sex = sex;
    }

    private int getAge() {
        return Age;
    }

    private void setAge(int age) {
        Age = age;
    }

    protected String getAddress() {
        return Address;
    }

    protected void setAddress(String address) {
        Address = address;
    }

}

Class对象类获取

方法

描述

定义

class

根据类名获取Class对象类

getClass()

返回当前对象的Class对象类

Class<?> getClass()

forName()

传入全路径类名,返回 Class 对象类

Class<?> forName(String className)

getSystemClassLoader()

这个方法返回的是系统类加载器 ClassLoader 类型

ClassLoader getSystemClassLoader()

loadClass()

系统加载器的方法,传入全路径类名,返回 Class 对象类

Class<?> loadClass(String name)

package com.example.Class;

public class ClassGetTest {
    public static void main(String[] args) throws ClassNotFoundException {

        User u = new User();

        //  -------------------------------class--------------------------------
        Class<User> GClass = User.class;
        System.out.println(GClass);
        //  输出 class com.example.Class.User


        //  -----------------------------getClass()-----------------------------
        Class<? extends User> getClass = u.getClass();
        System.out.println(getClass);
        //  输出 class com.example.Class.User


        //  ----------------------forName(String className)----------------------
        Class<?> forName = Class.forName("com.example.Class.User");
        System.out.println(forName);
        //  输出 class com.example.Class.User


        //  -----------getSystemClassLoader() 和 loadClass(String name)-----------
        ClassLoader SystemClassLoader = ClassLoader.getSystemClassLoader();     //  创建系统类加载器
        Class<?> loadClass = SystemClassLoader.loadClass("com.example.Class.User");     // 使用系统类加载器获取对象类
        System.out.println(SystemClassLoader);
        System.out.println(loadClass);
        //  输出
        //  jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c
        //  class com.example.Class.User
    }
}

Field成员变量类获取

方法

描述

定义

getFields()

返回所有公共成员变量对象的数组

Field[] getFields()

getDeclaredFields()

返回所有成员变量对象的数组

Field[] getDeclaredFields()

getField()

传入对象属性变量名,返回公共单个成员变量对象

Field getField(String name)

getDeclaredField()

传入对象属性变量名,返回私有单个成员变量对象

Field getDeclaredField(String name)

set()

传入成员属性对象和对象的值,为成员变量赋值

void set(Object obj, Object value)

get()

传入属性对象,获取该属性变量的值

Object get(Object obj)

package com.example.Field;

import java.lang.reflect.Field;

public class FieldGetTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class<?> GClass = Class.forName("com.example.Field.User");

        //  --------------------------------getFields()--------------------------------
        Field[] Fields = GClass.getFields();
        for (Field f:Fields) {
            System.out.println(f);
            //  输出
            //  public java.lang.String com.example.FieldGetTest.User.Name
            //  public java.lang.String com.example.FieldGetTest.User.Sex
        }


        //  ----------------------------getDeclaredFields()----------------------------
        Field[] DeclaredFields = GClass.getDeclaredFields();
        for (Field f:DeclaredFields) {
            System.out.println(f);
            //  输出
            //  public java.lang.String com.example.FieldGetTest.User.Name
            //  public java.lang.String com.example.FieldGetTest.User.Sex
            //  private int com.example.FieldGetTest.User.Age
            //  protected java.lang.String com.example.FieldGetTest.User.Address
        }


        //  ---------------------------getField(String name)---------------------------
        Field Field1 = GClass.getField("Name");
        System.out.println(Field1);
        //  输出 public java.lang.String com.example.FieldGetTest.User.Name


        //  -----------------------getDeclaredField(String name)-----------------------
        Field Field2 = GClass.getDeclaredField("Age");
        System.out.println(Field2);

        Field Field3 = GClass.getDeclaredField("Address");
        System.out.println(Field3);
        //  输出
        //  private int com.example.FieldGetTest.User.Age
        //  protected java.lang.String com.example.FieldGetTest.User.Address


        // --------------set(Object obj, Object value) 和 get(Object obj)---------------
        User u = new User();
        Field field = GClass.getField("Name");      //  获取 User 类中的 Name 属性
        field.set(u, "Xao");        //  u 的 Name 属性赋值为 Xao
        Object a = field.get(u);    //  获取对象 u 的 Name 属性
        System.out.println(a);
        //  输出 Xao
    }
}

Method成员方法类获取

方法

描述

定义

getMethods()

返回所有公共成员方法对象的数组,包括继承的方法

Method[] getMethods()

getDeclaredMethods()

返回所有成员方法对象的数组,不包括继承的方法

Method[] getDeclaredMethods()

getMethod()

返回单个公共成员方法对象

Method getMethod(String name, Class<?>... parameterTypes)

getDeclaredMethod()

返回单个成员方法对象

Method getDeclaredMethod(String name, Class<?>... parameterTypes)

invoke()

运行获取的方法

Object invoke(Object obj, Object... args)

package com.example.Method;

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

public class MethodGetTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Class<?> GClass = Class.forName("com.example.Method.User");


        //  ---------------------------getMethods()---------------------------
        Method[] Methods = GClass.getMethods();
        for (Method m:Methods){
            System.out.println(m);
            //  输出
            //  public java.lang.String com.example.Method.User.getSex()
            //  public void com.example.Method.User.setSex(java.lang.String)
            //  public java.lang.String com.example.Method.User.getName()
            //  public void com.example.Method.User.setName(java.lang.String)
            //  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()
            //  public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
            //  public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            //  public final void java.lang.Object.wait() throws java.lang.InterruptedException
        }


        //  -----------------------getDeclaredMethods()-----------------------
        Method[] DeclaredMethods = GClass.getDeclaredMethods();
        for (Method m:DeclaredMethods) {
            System.out.println(m);
            //  输出
            //  public java.lang.String com.example.Method.User.getName()
            //  public void com.example.Method.User.setName(java.lang.String)
            //  protected java.lang.String com.example.Method.User.getAddress()
            //  private void com.example.Method.User.setAge(int)
            //  public java.lang.String com.example.Method.User.getSex()
            //  private void com.example.Method.User.Say(java.lang.String,int)
            //  private int com.example.Method.User.getAge()
            //  public void com.example.Method.User.setSex(java.lang.String)
            //  protected void com.example.Method.User.setAddress(java.lang.String)
        }


        //  ----------------------------getMethod()----------------------------
        Method method1 = GClass.getMethod("getName");
        System.out.println(method1);

        Method method2 = GClass.getMethod("setName", String.class);
        System.out.println(method2);
        //  输出
        //  public java.lang.String com.example.Method.User.getName()
        //  public void com.example.Method.User.setName(java.lang.String)


        //  ------------------------getDeclaredMethod()------------------------
        Method DeclaredMethod = GClass.getDeclaredMethod("Say", String.class, int.class);
        System.out.println(DeclaredMethod);
        //  输出 private void com.example.Method.User.Say(java.lang.String,int)


        //  -----------------------------invoke()------------------------------
        Method InvokeMethod = GClass.getDeclaredMethod("Say", String.class, int.class);
        User u = new User();        //  创建一个新对象
        InvokeMethod.setAccessible(true);       //  私有方法开启临时,其他不需要
        InvokeMethod.invoke(u, "Teacher", 10010);       //  执行 Say 私有方法,传入参数 Teacher(String) 10010(int)

        //  运行结果
        //  My name is null
        //  My age is 0
        //  My Job is Teacher
        //  My Number is 10010
    }
}

Constructor构造方法类获取

方法

描述

定义

getConstructors()

返回所有公共构造方法对象的数组

Constructor<?>[] getConstructors()

getDeclaredConstructors()

返回所有构造方法对象的数组

Constructor<?>[] getDeclaredConstructors()

getConstructor(Class<?>... parameterTypes)

返回单个公共构造方法对象

Constructor<T> getConstructor(Class<?>... parameterTypes)

getDeclaredConstructor(Class<?>... parameterTypes)

返回单个构造方法对象

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

newInstance(Object ... initargs)

运行获取的构造方法

T newInstance(Object ... initargs)

package com.example.Constructor;

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

public class ConstructorGetTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        Class<?> GClass = Class.forName("com.example.Constructor.User");


        //  ------------------------getConstructors()------------------------
        Constructor[] Constructors = GClass.getConstructors();
        for (Constructor c:Constructors) {
            System.out.println(c);
            //  输出 public com.example.Constructor.User()
        }


        //  --------------------getDeclaredConstructors()--------------------
        Constructor[] DeclaredConstructors = GClass.getDeclaredConstructors();
        for (Constructor c:DeclaredConstructors) {
            System.out.println(c);
            //  输出
            //  public com.example.Constructor.User()
            //  protected com.example.Constructor.User(java.lang.String,java.lang.String,int,java.lang.String)
            //  private com.example.Constructor.User(java.lang.String,java.lang.String)
        }


        //  -------------------------getConstructor()-------------------------
        Constructor con1 = GClass.getConstructor();
        System.out.println(con1);

        Constructor con2 = GClass.getConstructor(String.class, String.class);
        System.out.println(con2);
        //  输出
        //  public com.example.Constructor.User()
        //  public com.example.Constructor.User(java.lang.String,java.lang.String)


        //  ---------------------getDeclaredConstructor()---------------------
        Constructor con3 = GClass.getDeclaredConstructor();
        System.out.println(con3);

        Constructor con4 = GClass.getDeclaredConstructor(String.class, String.class, int.class);
        System.out.println(con4);
        //  输出
        //  public com.example.Constructor.User()
        //  private com.example.Constructor.User(java.lang.String,java.lang.String,int)


        //  ---------------------------newInstance()---------------------------
        con4.setAccessible(true);
        User u = (User) con4.newInstance("Xao", "Male", 20);    //  运行结果如下
        System.out.println(u);          //  com.example.Constructor.User@433c675d
        System.out.println(u.Name);     //  Xao
        System.out.println(u.Sex);      //  Male
        System.out.println(u.Address);  //  null
        //  Address属性为protected,所以不输出,Age为私有属性,无法直接获取
    }
}

不安全命令执行与反序列化链条

Runtime.getRuntime().exec("notepad"); 为例,用上述方法来反射这个对象。截取 Runtime 类中部分方法

    public class Runtime {
        private static java.lang.Runtime currentRuntime = new java.lang.Runtime();
        
        public Process exec(String command) throws IOException {
            return exec(command, null, null);
        }

        public static Runtime getRuntime() {
            return currentRuntime;
        }
    }

可见,getRuntime() 方法是用于返回一个 Runtime 类对象,再通过这个类对象调用 exec() 方法,来执行命令。

首先要获取 Runtime 类

Class<?> c = Class.forName("java.lang.Runtime");

接下来有两种方法获取 Runtime 类对象

  1. 使用 Runtime 类中的方法 getRuntime()获取 Runtime 类对象

Method getRuntime = c.getMethod("getRuntime");
Object Run = getRuntime.invoke(c);

  1. 使用 Runtime 类构造方法创建 Runtime 类对象

Constructor Runtime = c.getDeclaredConstructor();
Runtime.setAccessible(true);		//	私有方法需要设置
Object Run = Runtime.newInstance();

获取并调用 Runtime 对象中的 exec() 方法,执行命令 "notepad"

Method exec = c.getMethod("exec", String.class);
exec.invoke(Run,"notepad");

最后弹出记事本,完成命令执行。