JAVA 反序列化漏洞入门学习笔记(二)--JAVA反射

参考文章

Java 反序列化漏洞(2) – Java 反射机制

看完参考后面忽略

定义

Java 反射机制是指在程序运行时,对于任何一个类,都能知道这个类的所有属性和方法,对于任何一个实例对象,都能调用该对象的任何一个属性和方法
Java 中这种 "动态获取信息" 和 "动态调用属性 "方法的机制被称为 Java 反射机制
实例对象可以通过反射机制获取它的类,类可以通过反射机制获取它的所有方法和属性,获取的属性可以设值,获取的方法可以调用
Java 反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。
反射最重要的用途是开发各种通用框架。很多框架都是通过 XML 文件来进行配置的(例如 struts.xml、spring-*.xml 等),即所谓的框架核心配置文件。为了确保框架的通用性,程序运行时需要根据配置文件中对应的内容加载不同的类或对象,调用不同的方法,这也依赖于 Java 反射机制

简单示例:

public class Reflectest{
    public static void main(String[] args) throws Exception {
        Object runtime = Class.forName("aaa.bbb.ccc").getMethod("ddd", String.class).invoke(null);
    }
}


可以看到,在程序编译时并未报错,在执行时才会提示 aaa.bbb.ccc 这个包不存在从而报错
也就是说:反射就是在程序运行的时候才知道要加载哪些类,加载完成后再去调用类中的方法(是不是感觉在 Python 反序列化中也见过)

反射机制步骤

  • 获取类
  • 获取类中方法及成员变量
  • 使用该类实例化一个对象
  • 调用实例对象中的方法/修改成员变量的值

获取类

  • Class.forName("xxx.xxx.xxx")
  • 类.class
  • 对象.getClass()

示例:

class Ref{
    public Ref(){
        String name = "1ndex";
    }
}

public class RefTest{
    public static void main(String [] args) throws ClassNotFoundException {
        //Class.forName
        System.out.println("Class.forName():    " + Class.forName("Ref"));
        
        //类.class
        System.out.println("类.class:           " + Ref.class);
        
        //对象.getClass()
        Ref cls = new Ref();
        System.out.println("对象.getClass():    " + cls.getClass());
    }
}

获取类中方法及成员变量

  • className.getMethod(functionName,[parameterType.class]),获取 functionName 对应的特定 public 方法
  • className.getMethods(),获取该类所有的 public 方法,包括其继承类的公用方法
  • className.getDeclaredMethod(functionName,[parameterType.class]),获取 functionName 对应的特定方法,包括公共、保护、默认(包)访问和私有方法
  • className.getDeclaredMethods(),获取某个类或接口声明的所有方法 , 包括公共、保护、默认(包)访问和私有方法,但不包括其继承类的方法

示例:

import java.lang.reflect.Method;

class Ref{
    public Ref(){
        String name = "1ndex";
    }
    public void ShowInfo(String name, int age){
        System.out.println(name + " is " + age);
    }
}

public class RefTest{
    public static void main(String [] args) throws ClassNotFoundException, NoSuchMethodException{
        Class<?> cls = Class.forName("Ref");
        
        //className.getMethod(functionName,[parameterType.class])
        Method method = cls.getMethod("ShowInfo",String.class,int.class);
        
        //className.getMethods()
        Method[] methods = cls.getMethods();
        
        //className.getDeclaredMethods()
        Method[] declareMethods = cls.getDeclaredMethods();
        
        System.out.println("className.getMethod(functionName,[parameterType.class]):\n" + method);
        
        System.out.println("\nclassName.getMethods():");
        for(Method m:methods){
            System.out.println(m);
        }
        
        System.out.println("\nclassName.getDeclaredMethods():");
        for(Method m:declareMethods){
            System.out.println(m);
        }
    }
    
}

使用该类实例化一个对象

  • className.newInstance(),调用无参构造函数
  • className.getConstructor(parameterType.class).newInstance("参数值"),调用 public 有参构造函数,之后只用调用实例化后的对象的公有方法
  • className.getDeclaredConstructor(parameterType.class).newInstance("参数值"),这个方法会返回指定参数类型的构造方法。包括 public、protected 以及 private 修饰符修饰的
  • className.getDeclaredConstructors(parameterType.class).newInstance("参数值"),这个方法会返回指定参数类型的所有构造方法。包括 public、protected 以及 private 修饰符修饰的
    示例:

import java.lang.reflect.InvocationTargetException;

class Ref{
    public Ref(){
        System.out.println("无参构造函数!\n");
    }
    public Ref(String motto){
        System.out.println("有参构造函数!\n" + motto);
    }
}

public class RefTest{
    public static void main(String [] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException{
        Class<?> cls = Class.forName("Ref");
        
        //className.newInstance(),调用无参构造函数
        Ref obj1 = (Ref)cls.newInstance();
        
        //className.getConstructor(parameterType.class).newInstance("参数值"),调用有参构造函数
        Ref obj2 = (Ref)cls.getConstructor(String.class).newInstance("一给我里Giao!");
    }
    
}

调用实例对象中的方法

  • Method.invoke(obj , args[]),传入实例对象和参数即可执行目标方法

示例:

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

class Ref{
    public Ref(){
        String name = "1ndex";
    }
    public void ShowInfo(String motto){
        System.out.println(motto);
    }
}

public class RefTest{
    public static void main(String [] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException{
        // 获取类
        Class<?> cls = Class.forName("Ref");
        //获取类中方法
        Method getFunc = cls.getMethod("ShowInfo",String.class);
        //实例化对象
        Ref obj = (Ref)cls.newInstance();
        //调用对象中的方法
        getFunc.invoke(obj,"一给我里Giao!");
    }
    
}

tips:

  • 如何调用特定的私有方法
    实例:
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;


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

        Class<?> cls = Class.forName("java.lang.Runtime");
        Method getFunc = cls.getMethod("exec",String.class);
        
        Constructor<?> cst = cls.getDeclaredConstructor();
        cst.setAccessible(true);
        
        Object obj = cst.newInstance();
        
        getFunc.invoke(obj,"calc");
    }
    
}
posted @ 2021-02-21 13:51  1ndex-  阅读(379)  评论(0编辑  收藏  举报