首页 > 编程语言 >Java安全-反射

Java安全-反射

时间:2024-10-22 21:21:27浏览次数:14  
标签:反射 forName Java 安全 获取 实例 obj Class

反射


反射是一种强大的机制,它允许程序在运行时访问、检查和修改它自己的结构,比如类、接口、字段(属性)和方法。反射提供了一种动态性,使得Java程序可以在运行时处理对象和类。所以说通过反射,我们可以使java这类静态语言附上动态的特征。

几个反射中重要的方法:

获取类的⽅法: forName
实例化类对象的⽅法: newInstance
获取函数的⽅法: getMethod
执⾏函数的⽅法: invoke

这⼏个⽅法基本上包揽了Java安全⾥各种和反射有关的Payload。

获取类名方法:

obj.class //直接通过类名获取,已加载某个类
obj.getClass() //通过实例对象获取,存在某个类的实例obj
obj.forName() //通过类名的字符串形式获取,知道类名,获取类

forName重载形式:

  1. Class.forName(String className)
    这是最基本的重载形式,它接受一个类名的字符串作为参数,并返回对应的 Class 对象。

    Class<?> clazz = Class.forName("java.lang.String");
    
  2. Class.forName(String className, boolean initialize, ClassLoader loader)
    这个重载形式允许你指定是否要初始化类(即调用类的静态初始化块和静态变量赋值),以及使用哪个类加载器来加载类。

    initialize 参数为true时,类会被初始化;为false时,类不会被初始化,这在Java 9及以后的版本中被称为“未链接”状态。

    loader 参数指定了用于加载类的 ClassLoader 实例。如果为 null,则使用调用者的类加载器。

    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Class<?> clazz = Class.forName("java.lang.String", true, classLoader);
    

    在Java中,当你使用Class.forName()方法加载类时,无论initialize参数是true还是false,JVM都会执行类的静态初始化块(即static块)。这是因为Class.forName()方法的目的就是加载并初始化类,包括执行静态初始化块中的代码。所以forname执行的是静态初始化块。

    静态初始化块在类被Java虚拟机(JVM)加载并初始化时执行。这发生在类首次被“主动使用”时,静态初始化块只执行一次,无论类的构造函数被调用多少次。实例初始化块在类的每个实例被创建时执行,实例初始化块在构造函数之前执行。构造函数在创建类的新实例时调用,构造函数在实例初始化块之后执行。但在执行实例化初始化块前会先执行构造函数的super()。

另外:

java我们一般情况下拿到一个类需要用import导入才可以,不过forName就不需要,那么对我们攻击者来说很有利,可以加载任意类。

一些类名中会使用 符号, 符号, 符号,作用是查找内部类。

Java的普通类 C1中支持编写内部类 C2,而在编译的时候,会生成两个文件:C1.class 和 C1 C 2. c l a s s ,我们可以把他们看作两个无关的类,通过 C l a s s . f o r N a m e ( " C 1 C2.class,我们可以把他们看作两个无关的类,通过Class.forName("C1 C2.class,我们可以把他们看作两个无关的类,通过Class.forName("C1C2") 即可加载这个内部类。

在Java中,内部类是一种定义在另一个类中的类。内部类可以访问外部类的成员,包括私有成员。当内部类被编译时,它们会被编译成独立的.class文件,但文件名会包含外部类的名称和一个$符号,后跟内部类的名称。当你使用Class.forName("C1$C2")来加载内部类时,JVM会查找名为C1$C2的类,并将其加载到内存中。如果C1$C2类依赖于外部类C1的某些特性,那么C1类也必须已经被加载,因为内部类与外部类之间存在依赖关系。获得类以后,我们可以继续使用反射来获取这个类中的属性、方法,也可以实例化这个类,并调用方法。

获取类的字段:

obj.getFields() //获取所有public字段
obj.getDeclaredFields() //获取所有字段(包含private)

获取类的方法:

obj.getMethods() //获取所有public方法
obj.getDeclaredMethods() //获取所有方法(包含private)

Java中支持类的重载,不能仅通过函数名来确定一个函数,还需要传给他你想要获取的函数的参数类型列表。

获取类的构造器:

obj.getConstructors() //获取所有public构造器
obj.getDeclaredConstructors() //获取所有构造器,私有记得用法setAccessible修改作用域

创建类实例:

obj.newInstance() //只适用于无参构造器
constructor = obj.getDeclaredConstructor()
constructor.newInstance() //有参构造器

class.newInstance()的作用就是调用这个类的无参构造函数,不过,我们有时候在写漏洞利用方法的时候,会发现使用newInstance总是不成功,这时候原因可能是:

  1. 使用的类没有无参构造函数

  2. 使用的类构造函数是私有的
    这是因为私有类是限定在定义它的类内部使用的,它不能被外部类直接访问,因此也不能通过反射机制来创建实例。在Java 9及以后的版本中,引入了一个新的方法getDeclaredConstructor(),它可以用来获取私有构造器,但是即使如此,你仍然需要在定义私有类的类内部来使用它。

    Constructor<InnerClass> constructor = InnerClass.class.getDeclaredConstructor();
    InnerClass instance = constructor.newInstance();
    

    但是,这仍然需要 InnerClass 不是顶级私有类,并且你需要有权限访问它。对于顶级私有类,你仍然不能通过反射来创建实例。

    这里有一个特殊的类(java.lang.Runtime),这里是很常见的单例模式(单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。单例模式通常用于管理共享资源,如配置信息、线程池、缓存等。)。
    java.lang.Runtime是 Java 的一个核心类,它提供了一系列方法来与运行时环境进行交互。这个类不能被实例化,但可以通过Runtime.getRuntime()方法获取其实例,该实例代表了当前 Java 应用程序的运行时环境。
    Runtime 类的一些常用方法:
    Process exec(String command):执行指定的字符串命令,并返回一个Process对象。
    void load(String filename):从指定的文件路径加载库(如DLL或SO文件)。
    void loadLibrary(String libname):从系统的库路径中加载库。
    void addShutdownHook(Thread hook):注册一个新线程,该线程将在 Java 虚拟机退出时执行。
    void removeShutdownHook(Thread hook):取消注册之前添加的shutdown hook。

获取注解:

obj.getAnnotations()

执行方法:

method.invoke()

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

另一种执行命令方式:ProcessBuilder。使用反射来获取其类构造器,然后调用start()来执行命令。

public ProcessBuilder(List<String> command)
public ProcessBuilder(String... command)
class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();

有时候我们利用漏洞的时候(在表 达式上下文中)是没有强转换类型的。所以,我们仍需利用反射来完成这一步。

Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));

通过getMethod(“start”)获取到start方法,然后 invoke 执行,根据咱们上面说的invoke 的第一个参数就是 ProcessBuilder Object了。

Class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();

在调用newInstance的时候,因为这个函数本身接收的是一个可变长参数,我们传给ProcessBuilder的也是一个可变长参数,二者叠加为一个二维数组。

标签:反射,forName,Java,安全,获取,实例,obj,Class
From: https://blog.csdn.net/wbqww_/article/details/143168556

相关文章

  • Java调用第三方接口、http请求详解,一文学会
    系列文章目录提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:第一章Python机器学习入门之pandas的使用提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录系列文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据......
  • 精通Java并发锁机制:24种锁技巧+业务锁匹配方案
    在Java并发编程中,锁是确保线程安全、协调多线程访问共享资源的关键机制。从基本的synchronized同步关键字到高级的ReentrantLock、读写锁ReadWriteLock、无锁设计如AtomicInteger,再到复杂的同步辅助工具如CountDownLatch、CyclicBarrier和Semaphore,每种锁都针对......