Java安全_反射篇

0x00 Java反射概述

Java Reflection

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看 到类的结构,所以,我们形象的称之为:反射。

  • 正常方式: 引入需要的“包类”名称–>通过new实例化–>取得实例化对象
  • 反射方式: 实例化对象–>getClass()方法–>得到完整的“包类”名称

Java反射机制研究及应用

Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • ……

0x01 Class类

Class类概述

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接 口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含 了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

  • Class本身也是一个类
  • Class 对象只能由系统建立对象
  • 一个加载的类在 JVM 中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

获取Class类的实例

  1. 前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠, 程序性能最高
  • Class class = String.class;
  1. 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
  • Class class = "www.fauns.com".getClass();
  1. 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方 法forName()获取,可能抛出ClassNotFoundException
  • Class class = Class.forName("java.lang.String");
  1. 其他方式
  • ClassLoader cl = this.getClass().getClassLoader();
  • Class class = cl.loadClass("类的全类名");
package proxy.test_test;

import com.mysql.jdbc.Driver;

import java.sql.*;

public class sql {
    public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
//        获得Driver类型的对象
        Driver driver = new Driver();
//        通过先get class类型的对象 然后再newInstance生成
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver1 = (Driver) aClass.newInstance();
//        通过getclass
        Class<Driver> driverClass = Driver.class;
        Driver driver2 = driverClass.newInstance();
    }
}
import bh.test1.Person;

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

public class Collection_Test {
    public static void main(String[] args) {
        try {
//        1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
            Class<Person> personClass = Person.class;
//        2.通过实例对象,调用该实例的getClass()方法获取Class对象
           Class<?> aClass = Class.forName("bh.test1.Person");
           Object m = aClass.newInstance();
           Class<?> aClass1 = m.getClass();
//        3.使用全类名配合forName()方法获得class对象
           Class<?> aClass2 = Class.forName("bh.test1.Person");

//            默认调用的是无参数的构造方法
            Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
            Person hehe = constructor.newInstance("hehe", 28);
            System.out.println(hehe);
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Class类的常用方法

方法名功能说明
static Class forName(Stringname)返回指定类名nameClass对象
ObjectnewInstance()调用缺省构造函数,返回该Class对象的一个实例
getName()返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称
ClassgetSuperClass()返回当前Class对象的父类的Class对象
Class []getInterfaces()获取当前Class对象的接口
ClassLoadergetClassLoader()返回该类的类加载器
ClassgetSuperclass()返回表示此Class所表示的实体的超类的Class
Constructor[]getConstructors()返回一个包含某些Constructor对象的数组
Field[]getDeclaredFields()返回Field对象的一个数组
Method getMethod(String name,Class …paramTypes)返回一个Method对象,此对象的形参类型为paramType
import bh.test1.Person;

public class Collection_Test {
    public static void main(String[] args) {
//        通过getclass方法可以获得类的完整的名称,包括:包名+类名
        Person person = new Person("haha", 1999);
        System.out.println(person.getClass());
        try {/*
         * 通过Class类中的forName方法,
         * 首先封装一个clasetAccessibless类型的对象,
         * 然后通过调用NewInstance方法实例化生成初始想要生成的对象
         * */
            /*
             * 假设现在的类名是一个可控的变量,
             * 则可以通过控制类名来实现实例化特定的对象
             * */
            Class<?> person1 = Class.forName("bh.test1.Person");
            System.out.println("返回指定类名 name 的 Class 对象: " + person1);
            System.out.println("调用缺省构造函数,返回该Class对象的一个实例: " + person1.newInstance());
            System.out.println("返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称: " + person1.getName());
            System.out.println("返回当前Class对象的父类的Class对象: " + person1.getSuperclass());
            System.out.println("获取当前Class对象的接口: " + person1.getInterfaces());
//            获得接口的名称
            Class<?>[] interfaces = person1.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                System.out.println(anInterface);
            }
            ClassLoader classLoader = person1.getClassLoader();
            System.out.println("返回该类的类加载器: " + classLoader);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
import bh.test1.Person;

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

public class Collection_Test {
    public static void main(String[] args) {
//        通过getclass方法可以获得类的完整的名称,包括:包名+类名
        Person person = new Person("haha", 1999);
        System.out.println(person.getClass());
        try {
            Class<?> person1 = Class.forName("bh.test1.Person");
            Constructor<?>[] constructors = person1.getConstructors();
            System.out.println("返回一个包含某些Constructor对象" + constructors);
            System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors));

            /*
            * 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
            * */
            for (Constructor<?> constructor : constructors) {
//                获得构造方法所需参数的个数
//                System.out.println(constructor.getParameters().length);
                if (constructor.getParameters().length == 2) {
                    Object hehe = constructor.newInstance("hehe", 20);
                    System.out.println(hehe);
                } else {
                    Object o = constructor.newInstance();
                    System.out.println(o);
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

1.1. getConstructors

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

public class Collection_Test {
    public static void main(String[] args) {
//        通过getclass方法可以获得类的完整的名称,包括:包名+类名
//        Person person = new Person("haha", 1999);
//        System.out.println(person.getClass());
        try {
            /*
             * 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
             * */
            Class<?> person2 = Class.forName("bh.test1.Person");
            Constructor<?>[] constructors1 = person2.getConstructors();
            System.out.println("返回一个包含某些Constructor对象" + constructors1);
            System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors1));
            for (Constructor<?> constructor : constructors1) {
                if (constructor.getParameters().length == 2) {
                    Object asdfasdf = constructor.newInstance("asdfasdf", 999);
                    System.out.println(asdfasdf);
                } else {
                    Object o = constructor.newInstance();
                    System.out.println(o);
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

1.2. getConstructorgetConstructors区别

import bh.test1.Person;

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

public class Collection_Test {
    public static void main(String[] args) {
//        通过getclass方法可以获得类的完整的名称,包括:包名+类名
        Person person = new Person("haha", 1999);
        System.out.println(person.getClass());
        try {
            /*
             * 通过getConstructor获得对象对应的构造器,此时可以实现有参数的构造方法的调用
             * 如果不想使用getConstructors获得多个构造器,
             * 此时可以使用getConstructor来获得特定的构造器,
             * 其参数为构造器需要使用的参数加上.class
             * */
            Class<?> person1 = Class.forName("bh.test1.Person");
            Constructor<?> constructor1 = person1.getConstructor(String.class, int.class);
            Object nihao = constructor1.newInstance("nihao", 29);
            System.out.println("nihao: " + nihao);

            /*
             * 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
             * */
            Class<?> person2 = Class.forName("bh.test1.Person");
            Constructor<?>[] constructors1 = person2.getConstructors();
            System.out.println("返回一个包含某些Constructor对象" + constructors1);
            System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors1));
            for (Constructor<?> constructor : constructors1) {
                if (constructor.getParameters().length == 2) {
                    Object asdfasdf = constructor.newInstance("asdfasdf", 999);
                    System.out.println(asdfasdf);
                } else {
                    Object o = constructor.newInstance();
                    System.out.println(o);
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

1.3. getMethod

import bh.test1.Person;

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

public class Collection_Test {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("bh.test1.Person");
            Object o = aClass.newInstance();
//            使用getMethod获得的方法的修饰符需要为public
            Method setName = aClass.getMethod("setName", String.class);
            setName.invoke(o, "nuew_name");
            String name = ((Person) o).getName();
            System.out.println(name);
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}
import bh.test1.Person;

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

public class Collection_Test {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("bh.test1.Person");
            Object o = aClass.newInstance();
//            使用getMethod获得的方法的修饰符需要为public
            Method setName = aClass.getMethod("classSayName");
            setName.invoke(o);
            setName.invoke(Person.class);
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

1.4. getDeclaredFields

import java.lang.reflect.Field;

public class Collection_Test {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("bh.test1.Person");
//            返回Field对象的一个数组
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
import java.lang.reflect.Field;

public class Collection_Test {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("bh.test1.Person");
            Object o = aClass.newInstance();
//            返回Field对象的一个数组
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField);
            }
            for (Field declaredField : declaredFields) {
//                System.out.println(declaredField.getName());
                if (declaredField.getName() == "namePublic") {
                    declaredField.set(o, "helloworld");
                } else if (declaredField.getName() == "agePublic") {
                    declaredField.set(o, 10);
                }
            }
            System.out.println(o);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

1.5. getDeclaredFieldgetDeclaredFields

import java.lang.reflect.Field;

public class Collection_Test {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("bh.test1.Person");
            Object o = aClass.newInstance();

            Field namePublic = aClass.getDeclaredField("namePublic");
            namePublic.set(o, "hahahaha");

            /*for (Field declaredField : declaredFields) {
                System.out.println(declaredField);
            }*/

            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField.getName());
                if (declaredField.getName() == "namePublic") {
                    declaredField.set(o, "asdlkajsdlkjasdlkajsd");
                } else if (declaredField.getName() == "agePublic") {
                    declaredField.set(o, 9999999);
                }
            }
            System.out.println(o);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

1.6. getModifiers

import bh.test1.Person;

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

public class Collection_Test {
    public static void main(String[] args) {
        try {
//        1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
            Class<Person> personClass = Person.class;
//        2.通过实例对象,调用该实例的getClass()方法获取Class对象
//            Class<?> aClass = Class.forName("bh.test1.Person");
//            Object m = aClass.newInstance();
//            Class<?> aClass1 = m.getClass();
//        3.使用全类名配合forName()方法获得class对象
//            Class<?> aClass2 = Class.forName("bh.test1.Person");

//            默认调用的是无参数的构造方法
            Constructor<Person> constructor = personClass.getDeclaredConstructor(String.class, int.class);
//            Person hehe = constructor.newInstance("hehe", 28);
//            System.out.println(hehe);
            int modifiers = constructor.getModifiers();
            System.out.println(modifiers);

            /*
            * 通过Modifier类中的isPublic,isprotected,isPrivate方法
            * 可以判断某个方法是否是public,protected还是private
            * */
            if (Modifier.isPublic(modifiers)) {
                System.out.println("now constructor is public");
            } else if (Modifier.isProtected(modifiers)) {
                System.out.println("now constructor is protected");
            } else if (Modifier.isPrivate(modifiers)) {
                System.out.println("now constructor is private");
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

0x02 反射的常用方法

以下方法基本上涵盖了java安全中各种和反射有关的payload

forName

作用:动态地加载类,这个方法是java.lang.Class类的一部分,用于加载具有指定名字的类。

在正常情况下,除了系统类,想获得一个类,需要先 import 。而使用forName就不需要,这对于我们攻击者来说十分有利,可以加载任意类。

forName的两个重载

  1. 常见的获取class的方式,可以理解为第二种形式的一个封装
Class<?> forName(String name)
Class<?> forName(String name, **boolean** initialize, ClassLoader loader)
  1. 第一个参数为类名,第二个参数为是否初始化,第三个参数为ClassLoader
Class.forName(className) <==> Class.forName(className, true, currentLoader)

第三个参数ClassLoader是一个加载器,告诉Java虚拟机如何加载这个类,后面再详细介绍漏洞利用方法。Java默认的ClassLoader就是根据类名来加载类,这个类名是完整路径,如java.lang.Runtime

然而对于第二个参数initialize来说是作为初始化,那么初始化指的是哪部分?

可以将这个初始化理解为类的初始化

public class Person {
    {
        System.out.printf("Empty block initial %s\n", this.getClass());
    }

    static {
        System.out.printf("Static initial %s\n", TrainPrint.class);
    }

    public Person() {
        System.out.printf("Initial %s\n", this.getClass());
    }
}

上面这个类首先调用的是static{},其次是{},最后是构造函数。其中static{}就是再类初始化的时候调用的,而{}中的代码会放在构造函数的super()后面,但在当前构造函数内容的前面。

所以forName中的initialize=true其实就是告诉Java虚拟机是否执行类初始化

那么假设又如下函数,其中函数的参数name可控

public void ref(String name) throws Exception {
	Class.forName(name);
}

就可以编写一个恶意类,将恶意代码放置在static{}中,从而执行,这个恶意类如何带入目标机器中,涉及到ClassLoader的一些利用方法。

package com.example;

import java.lang.Runtime;
import java.lang.Process;

public class TouchFile {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"touch", "/tmp/success"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

newInstance

作用:实例化对象的方法,动态地创建类的实例,调用类的无参构造函数。这个方法是java.lang.Class类的一部分,用于创建给定类的新实例。

如使用class.newInstance()可能会失败,原因是以下问题导致的

  • 使用的类没有无参构造函数
  • 使用的类构造函数是私有的
package org.example;

public class test {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("java.lang.Runtime");
        aClass.getMethod("exec", String.class).invoke(aClass.newInstance(), "id");
    }
}

以下报错是因为Runtime类的构造方法是私有的

这是由于Runtime使用到了“单例模式”,只有类初始化的时候会执行一次构造函数,只能通过Runtime.getRuntime()来获取到Runtime对象。

Java单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的特点如下:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。
  • 单例模式可以确保某个类只有一个实例,并且提供全局访问点。

修改上面代码,如下

package org.example;

public class test {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("java.lang.Runtime");
//        aClass.getMethod("exec", String.class).invoke(aClass.newInstance(), "id");
        aClass.getMethod("exec", String.class).invoke(aClass.getMethod("getRuntime").invoke(aClass), "calc");
    }
}

getMethod

作用:通过反射动态地获取一个类的某个特定的公有方法。这个方法是java.lang.Class类的一部分,用于获取给定类中定义的方法。

Java中支持类的重载无法通过函数名来确定一个函数,在调用getMethod时,依据需要获取的函数的参数来判断,如Runtime.getRuntime().exec的方法重载

最简单是使用getMethod("exec", String.class)来获取Runtime.exec方法,一个参数,类型String

invoke

作用:动态地调用类的方法,执行方法。这个方法是java.lang.Class类的一部分,用于调用一个对象的方法或构造函数。需要两个参数:一个对象(如果调用的是非静态方法),一个方法名和一个参数列表。

参数:

如果这个方法是一个普通方法,那么第一个参数是类对象

如果这个方法是一个静态方法,那么第一个参数是类

正常执行方法是[1].method([2], [3], [4]...)

反射里就是method.invoke([1], [2], [3], [4]...)

静态方法:因为它直接与类Person关联;普通方法:因为它们与类的实例关联。

package org.example;

public class Person {
    private String name;

    // 静态方法,因为它直接与类Person关联
    public static int getMax(int a, int b) {
        return Math.max(a, b);
    }

    // 普通方法,因为它们与类的实例关联。
    public String getName() {
        return name;
    }
}

将上面命令执行payload分解后如下

package org.example;

import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("java.lang.Runtime");

        aClass.getMethod("exec", String.class).invoke(aClass.getMethod("getRuntime").invoke(aClass), "calc");
        Method exec = aClass.getMethod("exec", String.class);
        Method getRuntime = aClass.getMethod("getRuntime");
        Object invoke = getRuntime.invoke(aClass);
        exec.invoke(invoke, "calc");
    }
}

0x03 思考

一个类没有无参构造方法,也没有类似单例模式里的静态方法,改如何通过反射实例化该类?

getConstructor

使用新的反射方法getConstructor,获取一个类的指定构造函数

接受的参数是构造函数列表类型,因为构造函数也支持重载,所以必须用参数列表类型才能确定唯一一个构造函数,与getMethod("exec", String.class)类似,获取到构造函数,然后使用newInstance来实例化对象。

(List<String> command)

如另一种执行命令的方式ProcessBuilder,使用反射来获取构造函数,然后调用start()来执行命令

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;

public class testcc {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
        ((ProcessBuilder) aClass.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
    }
}

ProcessBuilder有两个构造函数,上面使用的是第一种构造函数,所以传参是List.class

public ProcessBuilder(List<String> command) {
    if (command == null)
        throw new NullPointerException();
    this.command = command;
}

public ProcessBuilder(String... command) {
    this.command = new ArrayList<>(command.length);
    for (String arg : command)
        this.command.add(arg);
}
构造反射-(List<String> command)

上面payload使用了Java强制类型转换,一般来说在利用漏洞时(表达式上下文中)没有这种语法,所以需要利用反射来完成这一步。

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;

public class testcc {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
//        ((ProcessBuilder) aClass.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
        aClass.getMethod("start").invoke(aClass.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));
    }
}

通过getMethod("start")获取到start方法,然后invoke执行,invoke第一个参数就是ProcessBuilder Object

(String... command)

当定义函数时不确定参数数量,可以使用...来表示这个函数的参数个数是可变的

对于可变长参数,Java会将其编译成一个数组,下面两种写法在底层是等价的(无法重载)

public void hello(String[] names) {}
public void hello(String... names) {}

对于反射来说,如果要获取的目标函数里包含可变长参数,我们可以默认它是数组

将字符串数组的类String[].class传给getConstructor,获取ProcessBuilder的第二种构造函数

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

public class testcc {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
        ((ProcessBuilder) aClass.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();
    }
}
构造反射-(String... command)
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class testcc {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        Class<?> aClass = Class.forName("java.lang.ProcessBuilder");
//        ((ProcessBuilder) aClass.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();
        aClass.getMethod("start").invoke(aClass.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}}));
    }
}

一个方法或构造方法是私有方法,改如何执行?

getDeclared

使用getDeclared系列的反射

  • getMethod系列方法获取的是当前类中所有公共方法,包括从父类继承的方法
  • getDeclaredMethod系列方法获取的是当前类中“声明”的方法,是存在当前这个类里,包括私有的方法,(但从父类里继承来的不包含?)

getDeclaredMethod的具体用法和getMethod类似,getDeclaredConstructor的具体用法和getConstructor类似

Java反射API中的方法

  1. getMethodgetDeclaredMethod:这两个方法用于获取类的特定方法。
  • getMethod 返回指定类中声明的公共方法(包括从父类继承的方法)。如果方法不存在,它返回 null
  • getDeclaredMethod 返回指定类中声明的任意方法(包括从父类继承的方法)。如果方法不存在,它抛出 NoSuchMethodException
  1. getConstructorgetDeclaredConstructor:这两个方法用于获取类的特定构造函数。
  • getConstructor 返回指定类中的公共构造函数(包括从父类继承的构造函数)。如果构造函数不存在,它返回 null
  • getDeclaredConstructor 返回指定类中声明的任意构造函数(包括从父类继承的构造函数)。如果构造函数不存在,它抛出 NoSuchMethodException

简单来说,如果你想获取公共方法或公共构造函数,可以使用 getMethodgetConstructor。如果你想获取类中声明的任意方法或任意构造函数(包括从父类继承的),你可以使用 getDeclaredMethodgetDeclaredConstructor

getDeclaredConstructor

Runtime类的构造函数是私有的,需要用Runtime.getRuntime()来获取对象。现在也可以使用getDeclaredConstructor来获取这个私有的构造方法来进行实例化对象,然后执行命令

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

public class testcc {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        Class<?> aClass = Class.forName("java.lang.Runtime");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        aClass.getMethod("exec", String.class).invoke(declaredConstructor.newInstance(), "calc.exe");
    }
}

setAccessible方法:在获取到一个私有方法后,必须用setAccessible修改它的作用域,否则无法调用

0x04 参考链接

https://govuln.com

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇