• 技术文章 >Java >java教程

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

    长期闲置长期闲置2022-03-03 17:45:15转载346
    本篇文章给大家带来了关于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类获取对象的方式,个人感觉是每用一次就调一次比较麻烦,为了不调用一次建立一个对象所以封装成了函数

    类对象获取方式

    类初始化

    修改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类的完整路径)

    getMethod

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

    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”)

    最后我们合并一下

    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类有两个构造函数

    分别使用构造方法

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

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

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

    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();

    在这行打断点调试
    在这里插入图片描述
    我们传的是一个字符串数组到了实例化的时候变成了一个字符串,再看看另一个构造函数(List)

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

    依旧还是这行打断点

    在这里插入图片描述
    由此可知,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反序列化之反射机制的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:CSDN,如有侵犯,请联系admin@php.cn删除
    专题推荐:Java
    上一篇:动态代理的两种方式是什么 下一篇:五种方法!Java创建多线程总结
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• javascript对象和函数的区别是什么• javascript怎么计算2的几次方• javascript支持面向对象吗• javascript是什么框架• javascript怎么让li隐藏
    1/1

    PHP中文网