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 对象赋值给cClass c = (Class) defineClassCalc.invoke(cl,"Class.Calc",code,0,code.length);//初始化 Class对象 cc.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.mzph.cn/news/821145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JMeter控制器数据库获取一组数据后遍历输出

目录 1、测试计划中添加Mysql Jar包 2、添加线程组 3、添加 jdbc connection configuration 4、添加JDBC Request&#xff0c;从数据库中获取数据 5.获取数据列表&#xff0c;提取所有goodsName信息 6.通过添加控制器遍历一组数据 6.1 方式一&#xff1a;循环控制器方式 …

TiDB存储引擎TiKV揭秘

目录 一、TiKV 介绍 二、RocksDB 三、TiKV 与 RocksDB 架构 3.1 用户数据保存 3.2 TiKV 中 Region 一、TiKV 介绍 TiKV 是一个分布式事务型的键值数据库&#xff0c;提供了满足 ACID 约束的分布式事务接口&#xff0c;并且通过 Raft 协议保证了多副本数据一致性以及高可用。…

APP看广告变现,实现躺赚!!

想实现躺赚吗&#xff1f;开发一款APP&#xff0c;轻松对接广告联盟&#xff0c;收益丰厚&#xff01; 朋友们&#xff0c;你们是否想过如何让自己的手机变得更有价值&#xff1f;现在&#xff0c;有一款APP能让你轻松实现躺赚&#xff01;只需简单注册企业级营业执照和开通对…

Vue3项目中快速引入ElementUI框架

ElementUI介绍 ElementUI是一个强大的PC端UI组件框架&#xff0c;它不依赖于vue&#xff0c;但是却是当前和vue配合做项目开发的一个比较好的ui框架&#xff0c;其包含了布局&#xff08;layout)&#xff0c;容器&#xff08;container&#xff09;等各类组件&#xff0c;基本上…

算法学习——LeetCode力扣补充篇9(912. 排序数组、21. 合并两个有序链表、33. 搜索旋转排序数组、103. 二叉树的锯齿形层序遍历)

算法学习——LeetCode力扣补充篇9 912. 排序数组 912. 排序数组 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 示例 示例 1&#xff1a; 输入&#xff1a;nums [5,2,3,1] 输出&#xff1a;[1,2,3,5] 示例 2&…

基于ubuntu22.04系统安装nvidia A100驱动与NVLink启用

1、官方仓库 针对驱动包下载认准nvidia官网 dpkg -i nvidia-driver-local-repo-ubuntu2204-550.54.15_1.0-1_amd64.deb apt update apt search nvidia-driver-5502、安装 根据步骤1apt search nvidia-driver-550查出版本&#xff1a;此驱动包封在nvidia-driver-local-repo-ub…

Qt | 事件第二节

Qt | 事件第一节书接上回 四、事件的接受和忽略 1、事件可以被接受或忽略,被接受的事件不会再传递给其他对象,被忽略的事件会被传递给其他对象处理,或者该事件被丢弃(即没有对象处理该事件) 2、使用 QEvent::accept()函数表示接受一个事件,使用 QEvent::ignore()函数表示…

快速排序题目SelectK问题

力扣75.颜色分类 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sor…

普发Pfeiffer TPG252 TPG256A SingleGaugeTPG261-262使用说明手侧

普发Pfeiffer TPG252 TPG256A SingleGaugeTPG261-262使用说明手侧

卷积神经网络的结构组成与解释(详细介绍)

文章目录 前言 1、卷积层 2、激活层 3、BN层 4、池化层 5、FC层&#xff08;全连接层&#xff09; 6、损失层 7、Dropout层 8、优化器 9、学习率 10、卷积神经网络的常见结构 前言 卷积神经网络是以卷积层为主的深层网络结构&#xff0c;网络结构包括有卷积层、激活层、BN层、…

模板初阶的学习

目录&#xff1a; 一&#xff1a;泛型模板 二&#xff1a;函数模板 三&#xff1a;类模板 1&#xff1a;泛型模板 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础。 以交换函数为列进行讲解&#xff1a; void Swap(…

微服务中的重要模块

为什么要有微服务&#xff1f; 微服务提高开发效能&#xff0c;避免业务的重复理解&#xff0c;代码重复开发&#xff0c;增加开发效能和代码复用性。 在实际的工作中许多不同的业务有着共同的功能需求&#xff0c;如果我们每遇到一次这种需求就重新去理解构建一次的话会花费大…

【深度学习】【机器学习】用神经网络进行入侵检测,NSL-KDD数据集,基于机器学习(深度学习)判断网络入侵,网络攻击,流量异常【3】

之前用NSL-KDD数据集做入侵检测的项目是&#xff1a; 【1】https://qq742971636.blog.csdn.net/article/details/137082925 【2】https://qq742971636.blog.csdn.net/article/details/137170933 有人问我是不是可以改代码&#xff0c;我说可以。 训练 我将NSL_KDD_Final_1.i…

Day42:动态规划 LeedCode 01背包 416. 分割等和子集

01背包 1.确定dp数组以及下标的含义 dp[i][j]的含义&#xff1a;从下标为[0-i]的物品里任意取&#xff0c;放进容量为j的背包&#xff0c;价值总和最大是多少。 那么可以有两个方向推出来dp[i][j] 2.确定递推公式 不放物品i&#xff1a;由dp[i - 1][j]推出&#xff0c;即背…

十大排序——9.桶排序

这篇文章我们来介绍一下桶排序 目录 1.介绍 2.代码实现 3.总结与思考 1.介绍 桶排序和计数排序一样&#xff0c;都不是基于比较进行排序的。 下面通过一个例子来理解一下桶排序吧。 首先&#xff0c;给你一个无序数组[ 20,18,28,66,25,31,67,30 ]&#xff0c;然后&#…

Maven POM元素解析(二)

一、parent <parent>元素包含定位此项目将从中继承的父项目所需的信息。注意&#xff1a;此元素的子元素不是插值的&#xff0c;必须作为文字值给定。 ElementTypeDescriptiongroupIdString要从中继承的父项目的组id。artifactIdString要从中继承的父项目的项目id。ver…

【Entity Framework】你知道如何处理无键实体吗

【Entity Framework】你知道如何处理无键实体吗 文章目录 【Entity Framework】你知道如何处理无键实体吗一、概述二、定义无键实体类型数据注释 三、无键实体类型特征四、无键实体使用场景五、无键实体使用场景六、无键使用示例6.1 定义一个简单的Blog和Post模型&#xff1a;6…

高分二号卫星(GF-2):中国遥感科技的新高度

​高分二号卫星&#xff08;GF-2&#xff09;是中国在高分辨率地球观测领域的重要成就&#xff0c;其引入了先进的成像技术和灵活的数据获取模式&#xff0c;为地球资源监测、环境保护、城市规划等领域提供了强大的数据支持。本文将深入介绍高分二号卫星的技术特点、成像能力以…

Day 27 39. 组合总和 40.组合总和II 131.分割回文串

组合总和 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明&#xff1a; 所有数字&#xff08;包括 target&#xff09;都是正整数。解集不能…

自定义类型: 结构体 (详解)

本文索引 一. 结构体类型的声明1. 结构体的声明和初始化2. 结构体的特殊声明3. 结构体的自引用 二. 结构体内存对齐1. 对齐规则2. 为啥存在对齐?3. 修改默认对齐值 三. 结构体传参四. 结构体实现位段1. 什么是位段?2. 位段的内存分配3. 位段的应用4. 位段的注意事项 ​ 前言:…