Java反序列化基础-类的动态加载

类加载器&双亲委派

什么是类加载器

  • 类加载器是一个负责加载器类的对象,用于实现类加载的过程中的加载这一步。
  • 每个Java类都有一个引用指向加载它的ClassLoader。而数组类是由JVM直接生成的(数组类没有对应的二进制字节流)

类加载器有哪些

JVM 中内置了三个重要的 ClassLoader:

  1. BootstrapClassLoader(启动类加载器):最顶层的加载类,由C++实现,通常表示为null,并且没有父级,主要用来加载JDK内部的核心类库,以及被-Xbootclasspath参数指定路径下的所有类。
  2. ExtensionClassLoader(扩展类加载器):主要负责 “%JRE_HOME%/lib/ext” 目录下的jar包和类以及被 “java.ext.dirs” 系统变量所指定路径下的所有类。
  3. AppClassLoader(应用程序类加载器):面向用户的加载器,负责加载应用类的path下的所有jar包和类。

还有一个是用户自定义类加载器:

  1. User ClassLoader

什么是双亲委派

当一个类加载器收到类加载器的请求的时候,它不会直接加载指定的类,而是把这个请求委托给自己的父加载器去加载器。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。
双亲委派的好处就是一个类名只会被一个加载器加载
image.png

类加载不同代码的加载顺序

这里的代码块主要有以下四种:

  • 静态代码块:static{}
  • 构造代码块:{}
  • 无参构造器:ClassName()
  • 有参构造器:ClassName(String name)

实例化对象

  • Person.java:里面有静态代码块、构造代码块、无参构造器、有参构造器、静态成员变量、普通成员变量、静态方法。
  • Main.java:启动类

Person.java

package Class;

public class Person {
    public static int statica;
    public int instancea;

    static {
        System.out.println("静态代码块");
    }

    Person(){
        System.out.println("无参构造器");
    }

    {
        System.out.println("构造代码块");
    }



    public Person(int instancea) {
        this.instancea = instancea;
        System.out.println("有参构造"+ this.instancea);
    }


    public static void staticAction(){
        System.out.println("静态方法");
    }
}

Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Person person = new Person();

    }
}

运行之后可以看见,调用的顺序是 :
静态代码块->构造代码块->无参构造器
image.png

调用静态方法

调用Person的静态方法
Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Person.staticAction();

    }
}

运行Main.java后,可以看见没有实例化对象会先调用:
静态代码块->静态方法
image.png

对静态成员变量赋值

Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Person.statica = 1;
    }
}

对静态成员变量赋值,也会调用静态代码块
image.png

使用class获取类

Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Class c = Person.class;

    }
}

运行后没有输出
image.png

使用forName获取类

获取类的class

需要在main方法上主动抛出异常
Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Class.forName("Class.Person");
    }
}

运行之后,可以看见调用了静态代码块
image.png

有true

Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Class.forName("Class.Person",true,ClassLoader.getSystemClassLoader());
    }
}

调用静态代码块
image.png

有false

Main.java

package Class;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException{
        Class.forName("Class.Person",false,ClassLoader.getSystemClassLoader());
    }
}

可以看见没有调用任何代码块
image.png

使用ClassLoader.loadClass() 获取类

Main.java

package Class;

public class Main {
    public static void main(String[] args) throws Exception{
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Class<?> c = cl.loadClass("Class.Person");
        c.newInstance();// 初始化类
    }
}

添加了 c.newInstance()可以看见调用了以下,否则不调用
image.png

动态加载字节码

什么是字节码?

Java中的字节码,英文名为 “bytecode”,是Java代码编译后的中间代码格式。JVM(Java虚拟机)执行使用的一类指令,字节码通常存储在 .class文件中。

javac Hello.java

这里使用 javac命令编译我们写好的java文件,可以看见生成了一个 Hello.class文件,这个就是字节码文件
image.png
我们打开Hello.class文件看一下,可以看见都是一些看不懂的文件,这些是需要JVM去做的事情,才能执行
image.png
可以看见执行的时候,也会输出成功,因为java加载了字节码
image.png

类加载器的原理

注意:

复现的时候JDK版本不可以太高,否则会不一样

JDK下载:

https://blog.csdn.net/weixin_51959343/article/details/135921731

这里主要断点调试 loadClass 这一行
image.png
这个时候就会来到它的父类抽象类 ClassLoader,然后调用 loadClass()的两个参数
image.png
继续跟进来到了 AppClassLoader的loadClass()
image.png
走到下面是双亲委派的逻辑,调用父类的loadClass重复查询
image.png
这个时候又回到了 ClassLoader()类中
image.png
来到所属的 Launcher类中,Ctrl + H 可以看见,类的流程关系是:
ClassLoader —-> SecureClassLoader —> URLClassLoader —-> APPClassLoader
image.png
后面继续断点跟踪调试可以发现,函数调用顺序:
loadClass() -> findClass() -> defineClass()

任意类加载class文件

urlClassLoader

编译一个Calc类的字节码文件

package Class;

import java.io.IOException;

public class Calc {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

image.png
然后编写urlClassLoader的启动类

package Class;

public class Main {
    public static void main(String[] args) throws Exception{
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file://编译后的class文件位置路径//")});
        Class<?> c = urlClassLoader.loadClass("Class.Calc"); 
        c.newInstance();
    }
}

运行之后可以看见成功弹出了计算器
image.png
也可以使用http协议进行远程加载

package Class;

public class Main {
    public static void main(String[] args) throws Exception{
    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://localhost:8080")});
        Class<?> c = urlClassLoader.loadClass("Class.Calc"); 
        c.newInstance();
    }
}

在字节码文件目录下开启http服务
image.png
运行之后可以看见弹出计算器
image.png
也可以使用jar,

  • 前面 jar:
  • 后面 xxx.jar!/
package Class;

public class Main {
    public static void main(String[] args) throws Exception{
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://localhost:8080/Calc.jar!/")});
        Class<?> c = urlClassLoader.loadClass("Class.Calc");
        c.newInstance();
    }
}

image.png
file协议同理

defineClass

也可以直接使用 defineClass方法加载恶意的class文件

package Class;


public class Main {
    public static void main(String[] args) throws Exception{

        //获取ClassLoader类的系统类加载器赋值到c1变量
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        
        //使用反射机制获取 ClassLoader 类的 defineClass 方法中的一些一些参数
        Method defineClassCalc = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        
        //设置权限为可访问
        defineClassCalc.setAccessible(true);
        
        //byte[]数组存储了,这个目录下的Calc.class字节码文件
        byte[] code = Files.readAllBytes(Paths.get(("E:\\语言学习\\java\\code\\Serialize1\\Serialize1\\out\\production\\Serialize1\\Class\\Calc.class")));

        //反射调用了之前获取到的 defineClass 方法,将字节码数组转换为 Class 对象赋值给c
        Class c = (Class) defineClassCalc.invoke(cl,"Class.Calc",code,0,code.length);
        
        //初始化 Class对象 c
        c.newInstance();
    }
}

image.png
缺点就是 defineClass方法是protected受保护的成员,在反序列化中难以反射调用

Unsafe

Unsafe 方法,是采用单例模式进行设计的,无法直接调用,所以需要反射

package Class;


public class Main {
    public static void main(String[] args) throws Exception{

        //获取ClassLoader类的系统类加载器赋值到c1变量
        ClassLoader cl = ClassLoader.getSystemClassLoader();

        //获取 Unsafe的类
        Class<Unsafe> unsafeClass = Unsafe.class;

        //通过反射 获取 Unsafe类的 theUnsafe字段
        Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");

        //设置访问权限为true,theUnsafe是prvate成员
        theUnsafeField.setAccessible(true);

        byte[] code = Files.readAllBytes(Paths.get(("E:\\语言学习\\java\\code\\Serialize1\\Serialize1\\out\\production\\Serialize1\\Class\\Calc.class")));

        //获取 Unsafe对象theUnsafeField方法静态的字段的值
        Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
        Class c2 = unsafe.defineClass("Class.Calc", code, 0, code.length, cl, null);
        c2.newInstance();

    }
}

image.png

总结

总的来说就是加载器字节码之间怎么构造一条链子,底层之间产生的漏洞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/549052.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

贝锐蒲公英企业路由器X5 Pro:无需专线和IT人员,分钟级异地组网

尽管我们公司规模较小&#xff0c;只有十几个人&#xff0c;但为了确保项目资料的安全&#xff0c;依旧在公司内部自建了文件存储服务器和办公系统。 但是&#xff0c;随着项目数量的增加&#xff0c;大家出差办公的情况也愈发普遍&#xff0c;如何解决远程访问内部系统成了问…

公司聚会计划:最优宾客名单的算法设计与分析

公司聚会计划&#xff1a;最优宾客名单的算法设计与分析 问题描述算法设计C代码实现时间复杂度分析空间复杂度分析结论 在组织公司聚会时&#xff0c;一个重要的考虑因素是如何确保聚会的愉快氛围。在本问题中&#xff0c;公司主席希望在聚会上避免员工及其直接主管同时出席&am…

Python写FTP文件自动传输脚本

FTP&#xff08;File Transfer Protocol&#xff09;是一种用于文件传输的标准协议&#xff0c;当我们需要上传或下载文件时&#xff0c;经常会使用 FTP。如果每天需要上传或下载大量文件&#xff0c;手工操作无疑是一件费时费力的事情。在本篇文章中&#xff0c;我们将向您介绍…

中国建筑模板出口供应商

随着"一带一路"倡议的深入推进,中国基建企业"走出去"的步伐正在加快。与之相应,建筑模板产品作为工程建设的重要材料,其国际化供应也愈发受到重视。在众多建筑模板生产企业中,贵港市能强优品木业有限公司以其卓越的产品质量和丰富的出口经验,成为了国内知名…

sed 字符替换时目标内容包含 特殊字符怎么处理

背景 想写一个自动修改配置的脚本&#xff0c;输入一个 mysql jdbc 的连接路径&#xff0c;然后替换目标配置中的模版内容&#xff0c;明明很简单的一个内容&#xff0c;结果卡在了 & 这个符号上。 & 到底是什么特殊字符呢&#xff1f;结论&#xff1a;它代表要替换的…

GmSSL-3.1.1编译

1.源码下载&#xff1a; 下载地址&#xff1a;https://github.com/guanzhi/GmSSL/releases选择对应版本下载。 ​ 2.选择要下载的源码包&#xff1a; ​ 2.编译&#xff1a; 2.1 windows编译&#xff1a;打开vs命令行&#xff0c;选择想要编译的版本&#xff0c;x86或x64…

大数据、数据架构、推荐冷启动...小红书的 AI 数据新方案都在这个会

伴随着行业数据持续积累&#xff0c;人工智能正加速渗透各类场景&#xff0c;大数据、数据架构和推荐系统等领域&#xff0c;依然是各行各业目之所聚。4 月 19 至 20 日&#xff0c;「DataFunCon 2024 上海站」来袭&#xff01;大会以“数聚垂域&#xff0c;智领未来”为主题…

数据结构——栈(C++实现)

数据结构——栈 什么是栈栈的实现顺序栈的实现链栈的实现 今天我们来看一个新的数据结构——栈。 什么是栈 栈是一种基础且重要的数据结构&#xff0c;它在计算机科学和编程中扮演着核心角色。栈的名称源于现实生活中的概念&#xff0c;如一叠书或一摞盘子&#xff0c;新添加…

AI概念普及-LangChain

文章目录 概念产品架构核心特性核心组件使用场景其他资源开发支持结论Langchain详细介绍LangChain的具体实现原理LangChain如何与其他大型语言模型&#xff08;LLM&#xff09;集成&#xff0c;有哪些具体的接口或协议&#xff1f;LangChain的性能表现和优化策略有哪些&#xf…

由于找不到msvcr120.dll,无法继续执行代码的详细处理方法,教你快修复msvcr120.dll

DLL文件&#xff0c;全称动态链接库文件&#xff0c;在计算机系统中具有重要作用。其中&#xff0c;msvcr120.dll是一个常见的DLL文件&#xff0c;它关联了许多程序和应用的正常运行。本指南将深入解释 msvcr120.dll文件的功能&#xff0c;并阐述如果缺少该文件会引起什么样的问…

Banana Pi开源社区推出BPI-5202开发板,国产龙芯Loongson 2K1000LA

BPI-5202开发板&#xff0c;国产龙芯Loongson 2K1000LA BPI-5202作为单纯的嵌入式通用控制器软硬件开发平台&#xff0c;采用龙芯2K1000LA芯片设计&#xff0c;基本配置中有2个独立MAC以太网端口、2个RS485端口1个RS232端口2个CAN2.0端口&#xff0c;配置灵活&#xff0c;广泛适…

# ABAP SQL 字符串处理-CONCATCAST

经常我都要在ABAP的sql语句中对字符串进行处理&#xff0c;现在就总结一下可以用到的方法 文章目录 字符串处理拼接字段运行结果 填充字符串运行结果 截取字符串 SUBSTRING运行结果 CAST转换类型程序运行结果 CAST 转换成 DATS类型&#xff08;日期&#xff09; 字符串处理 在…

客户案例:金蝶云星空对接纷享销客

正文&#xff1a;某国内食品贸易类客户&#xff0c;目前内部使用了多套系统。金蝶云星空ERP&#xff0c;纷享销客&#xff0c;钉钉&#xff0c;旺店通等系统。金蝶云星空作企业的业务财务一体化管理&#xff0c;与专业CRM平台纷享销客的战略合作&#xff0c;在产品管理、客户关…

Java智慧工地可视化管理云平台源码 施工进度、施工质量

目录 1、基础数据管理 2、考勤管理 3、安全隐患管理 4、视频监控 5、塔吊监控 6、升降机监控 7、管理分析报表 8、移动端数据推送 9、数据接收管理 慧工地全套源码&#xff08;PC端&#xff0c;移动端&#xff0c;大屏端&#xff09; 智慧工地系统利用APP监管施工现场…

SQL注入利用学习 - 延时盲注

延时盲注原理 无法利用页面显示结果判断SQL注入是否执行成功&#xff0c;此时可以利用 SQL语句执行的延时 判断SQL是 否执行成功。 只要可以执行延时&#xff0c;那么就可以利用该注入技术。 sql时间类型的盲注本质是利用插入的SQL语句执行造成时间延迟&#xff0c;插入的SQ…

软件测试中完整的Web请求流程

在软件开发的过程中&#xff0c;测试是一个至关重要的环节。而在现代互联网应用中&#xff0c;Web请求是很常见的一个测试需求。本文将介绍Web请求的完整测试流程&#xff0c;帮助读者更好地理解软件测试的关键步骤。 一、测试准备阶段 在进行Web请求测试之前&#xff0c;测试…

IK分词器安装、配置、分词自定义、Rest使用、SpringBoot使用

文章目录 1. 概述2. 安装配置3. 自定义拆分文本4. 调用4.1 拆分规则4.2 Rest 调用4.3 SpringBoot 调用 1. 概述 IK分词器是ElasticSearch(es)的一个最最最有名插件&#xff0c;能够把一段中文或者别的语句划分成一个个的关键字&#xff0c;进而在搜索的时候对数据库中或者索引库…

姓名升序,若相同则按照年龄升序——集合的几种排序方式(有问必答版)

见者有缘&#xff0c;缘来好运。诚邀各位围观我的博客【CS_GUIDER】&#xff1a; 我的云服务器到期了&#xff0c;所以这里放两个部署在码云和 GitHub 的链接&#xff1a; https://wlei224.gitee.io &#xff08;Gitee托管&#xff0c;速度极快&#xff09; https://wl2o2o.git…

go work模块与go mod包管理是的注意事项

如下图所示目录结构 cmd中是服务的包&#xff0c;显然auth,dbtables,pkg都是为cmd服务的。 首先需要需要将auth,dbtables,pkg定义到go.work中&#xff0c;如下&#xff1a; 在这样在各个单独的go mod管理的模块就可以互相调用了。一般情况下这些都是IDE自动进行的&#xff0c;…

Go微服务: 服务限流原理, 负载均衡与API网关

微服务里面的限流 (uber/limit)概述 go 微服务保稳三剑客: 熔断&#xff0c;限流&#xff0c;负载均衡限流的作用 限制流量&#xff0c;在服务端生效 注意&#xff1a;熔断是客户端生效 保护后端服务 餐厅吃饭排队的问题&#xff0c;提供凳子&#xff0c;让等候&#xff0c;这就…
最新文章