实例详解Java反序列化之反射机制

编程之家收集整理的这篇文章主要介绍了实例详解Java反序列化之反射机制编程之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

实例详解Java反序列化之反射机制,下方主要介绍关于实例详解Java反序列化之反射机制的全文内容,希望对你有所帮助。
本篇文章给大家带来了关于java的相关知识,其中主要介绍java反射机制的相关问题,动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制,希望对大家有帮助。

推荐学习:《java教程

每次听到大佬在讲或者看论坛等一些方式学java反序列化漏洞时,都会有一个词叫做反射机制,大佬顺势借着这个词,就给你造出一个payload,对于刚学java反序列化的我们,可能有点会懵圈,反正我是懵了,所以就赶紧学了一波,不然和大佬差距越来越大。所以这篇文章主要讲述java反射机制

Java反射机制

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

我不太擅长文字表达,还是上图操作把

不用反射机制的例子

//定义一个animals接口interface animals {
    public abstract voID print();}//定义类来实现animals接口的抽象方法class Dog implements animals {
    public voID print() {
        System.out.println("Dog");
    }}class Cat implements animals {
    public voID print() {
        System.out.println("Cat");
    }}// 构造一个zoo类// 之后如果我们添加其他的实例的时候只需要修改zoo类class zoo {

    public static animals getInstance(String animalsname) {
        animals a = null;
        if ("Dog".equals(animalsname)) {
            a = new Dog();
        }
        if ("Cat".equals(animalsname)) {
            a = new Cat();
        }
        return a;
    }}public class reflection {
    public static voID main(String[] args) {
        //借助zoo类寻找对应的类来实现接口
        animals a=zoo.getInstance("Cat");
        if(a!=null)
            a.print();
    }}

这时候添加动物,只需要

把上面修改为反射机制

//定义一个animals接口interface animals {
    public abstract voID print();}//定义类来实现animals接口的抽象方法class Dog implements animals {
    public voID print() {
        System.out.println("Dog");
    }}class Cat implements animals {
    public voID print() {
        System.out.println("Cat");
    }}// 构造一个zoo类// 之后如果我们添加其他的实例的时候只需要修改zoo类class zoo {

    public static animals getInstance(String classname) {
        animals a = null;
        try {
            //借助Class.forname寻找类名,并用newInstance实例化类似于new
            a = (animals) Class.forname(classname).newInstance();
        } catch (Exception e) {
            e.printstacktrace();
        }
        return a;
    }}public class reflection {
    public static voID main(String[] args) {
        //借助zoo类寻找对应的类来实现接口(classname为当前包名加类名)
        animals a = zoo.getInstance("com.cc1.Dog");
        if (a != null)
            a.print();
    }}

这时候添加动物只需要

省了一步,传入类名可控,发现好像是存在的类都可以调

反射机制方法

我们用的最多的可能是

Class.forname(classname).getmethod(methodname).invoke(Class.forname(classname).newInstance());

下面我们用反射机制来弹出计算机(calc)或者记事本(notepad)

由于弹出计算机有点多这次我就弹记事本把,总而言之,能弹出来就很美妙

Runtime.getRuntime().exec("notepad");

在这里插入图片描述


我们看下getRuntime函数

在这里插入图片描述


得知,该函数是Runtime类获取对象的方式,个人感觉是每用一次就调一次比较麻烦,为了不调用一次建立一个对象所以封装成了函数

类对象获取方式

  • Class.forname(类名获取
  • zoo.class(已经加载过的类)
  • obj.class(实例)

    在这里插入图片描述

类初始化

修改zoo类,增加初始块、静态初始块、和构造函数

class zoo {
    //初始块
    {
        System.out.println("1  " + this.getClass());
    }

    //静态初始块
    static {
        System.out.println("2  " + zoo.class);
    }

    public zoo() {
        System.out.println("3  " + this.getClass());
    }

    public static animals getInstance(String classname) {
        animals a = null;
        try {
            //借助Class.forname寻找类名,并用newInstance实例化类似于new
            a = (animals) Class.forname(classname).newInstance();
        } catch (Exception e) {
            e.printstacktrace();
        }
        return a;
    }}

类初始化执行顺序:静态初始块

在这里插入图片描述


类实例化执行顺序:静态初始块 - > 初始块 - > 构造函数

在这里插入图片描述


由此得知,类初始化和类实例化不一样

接下来增加zoo1类继承zoo类

class zoo1 extends zoo{
    //初始块
    {
        System.out.println("11  " + this.getClass());
    }

    //静态初始块
    static {
        System.out.println("12  " + zoo.class);
    }

    public zoo1() {
        System.out.println("13  " + this.getClass());
    }}

子类初始化顺序父类静态初始化块 - > 子类静态初始化块

在这里插入图片描述


类实例化顺序父类静态初始化块 - > 子类静态初始化块 - > 父类初始化块 - > 父类构造函数 - > 子类初始化块 - >子类构造函数

在这里插入图片描述


以上可以得知,当使用Class.forname时,且类静态初始化块可控,可以执行任意代码

调用内部类

Class.forname(“java.lang.Runtime”)来获取类(java.lang.Runtime是Runtime类的完整路径

@H_419_148@

getmethod

getmethod 的作用是通过反射获取类的某个特定的公有方法
java支持类重载,但不能仅通过一个函数名确定一个函数,所以在调用getmethod时,需要传给它方法参数类型列表
Class.forname(“java.lang.Runtime”).getmethod(“exec”, String.class)

@H_419_148@

invoke

静态和动态方法区别

在这里插入图片描述

invoke方法getmethod类下,作用时传递参数,执行方法
public Object invoke(Object obj, Object… args)
一个参数getmethod获取方法的类对象(如果方法是静态方法则传类)
获取exec函数的类对象
Class.forname(“java.lang.Runtime”).getmethod(“getRuntime”).invoke(Class.forname(“java.lang.Runtime”))
由于getRuntime是静态方法,所以传类
invoke(Class.forname(“java.lang.Runtime”).getmethod(“getRuntime”).invoke(Class.forname(“java.lang.Runtime”)),“calc.exe”)

@H_419_148@

最后我们合并一下

Class.forname("java.lang.Runtime").
                getmethod("exec", String.class).
                invoke(Class.forname("java.lang.Runtime").getmethod("getRuntime").invoke(Class.forname("java.lang.Runtime")), "notepad");

在这里插入图片描述

指定构造方法生成实例

String str="notepad";ProcessBuilder pb = new ProcessBuilder(str);pb.start();

getConsturctor(函数可以选定指定接口格式的构造函数(由于构造函数也可以根据参数进行重载)
选定后我们可以通过newInstance(),并传入构造函数参数执行构造函数

ProcessBuilder类有两个构造函数

  • public ProcessBuilder(String… command)(String…变长的字符串数组String[].class)
  • public ProcessBuilder(List command)

分别使用构造方法

  • Class.forname(“java.lang.ProcessBuilder”).getConstructor(String[].class).newInstance(new String[][]{{“notepad”}})
  • Class.forname(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))

执行完构造方法实例后,在进行强制转化使用start函数即可

( (ProcessBuilder) Class.forname(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))).start();

@H_419_148@

实际中,肯定用不了,哪有这么好的事,还是接着反射把

Class.forname(“java.lang.ProcessBuilder”).getmethod(“start”).invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList(“notepad”)));

在这里插入图片描述


这里可能有人会好奇我写的里那的另一个构造函数,String…command这个传参为什么用new String[][]{{“notepad”}},不应该是new String[]{“notepad”},现在用应该的

((ProcessBuilder) Class.forname(“java.lang.ProcessBuilder”).getConstructor(String[].class).newInstance(new String[]{“notepad”})).start();

@H_419_148@

在这行打断点调试

在这里插入图片描述


我们传的是一个字符串数组到了实例化的时候变成了一个字符串,再看看另一个构造函数List

( (ProcessBuilder) Class.forname(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))).start();

@H_419_148@

依旧还是这行打断点

在这里插入图片描述


由此可知,List传入时会被当作Object的第一项,而String[]会被当做Object,所以多加一层[]{}

执行私有方法

通过函数getDeclaredConstructor获取私有方法,再利用setAccessible(true)打破私有方法限制

Class cls = Class.forname("java.lang.Runtime"); 
Constructor m = cls.getDeclaredConstructor();
 m.setAccessible(true); 
 cls.getmethod("exec", String.class).invoke(m.newInstance(), "notepad");

推荐学习:《java教程

总结

以上是编程之家为你收集整理的实例详解Java反序列化之反射机制全部内容,希望文章能够帮你解决实例详解Java反序列化之反射机制所遇到的程序开发问题。

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

Java文章

实例详解Java反序列化之反射机制
五种方法!Java创建多线程总结
归纳整理JAVA学习之流程控制
详细讲解Java运算符(总结分享)
深入解析JAVA中字符串常量池和缓冲池理解与作用
详细解析Java反射机制原理和几种Class获取方式
简单聊聊java面向对象
图文详解!什么是Java内存模型
微信公众号搜索 “ 程序精选 ” ,选择关注!
微信公众号搜 "程序精选"关注