JDK动态代理
JDK动态代理
代理模式是一种设计模式,能够使得在不修改源目标的前提下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了,只需要在代理类上增加就可以了
一个最简单的动态代理实现如下:
1 | import java.lang.reflect.InvocationHandler; |
在运行期动态创建一个interface
实例的方法如下:
- 定义一个
InvocationHandler
实例,它负责实现接口的方法调用 - 通过 Proxy.newProxyInstance() 创建 interface 实例,它需要3个参数:
- 使用的
ClassLoader
,通常就是接口类的ClassLoader
- 需要实现的接口数组,至少需要传入一个接口进去
- 用来处理接口方法调用的
InvocationHandler
实例
- 使用的
- 将返回的
Object
强制转型为接口
动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,它并没有什么黑魔法,把上面的动态代理改写为静态实现类大概长这样:
1 | public class HelloDynamicProxy implements Hello { |
其实就是JVM帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码),并不存在可以直接实例化接口的黑魔法
小结
Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例
动态代理是通过Proxy
创建代理对象,然后将接口方法“代理”给InvocationHandler
完成的相对于静态代理类来说,无论有多少接口,动态代理只需要一个代理类
动态代理意义: 少修改代码 适配强
在反序列化漏洞中的作用:
1、readObject -> 反序列化自动执行 2、invoke -> 有函数调用 3、拼接两条链 4、任意->固定
要利用反序列化的漏洞是需要一个入口类的,先假设存在一个能够漏洞利用的类为 B.f
,比如Runtime.exec
这种,我们将入口类定义为A
,我们最理想的情况是 A[O] -> O.f ,那么我们将传进去的参数 O
替换为 B
即可。但是在实战的情况下这种情况是极少的
回到实战情况,比如我们的入口类A
存在 O.abc
这个方法,也就是 A[O] -> O.abc
如果O 是一个动态代理类,O 的invoke
方法里存在 .f
的方法,便可以漏洞利用了
1 | A[O] -> O.abc |
动态代理在反序列化当中的利用和readObject
是异曲同工的:
readObject
方法在反序列化当中会被自动执行,而invoke
方法在动态代理当中会自动执行