1. 静态代理
有一个师傅的文章讲的很好,我们经常说代理。我们可以把我们比作一个租房的人,我们找代理中介去找拥有房源的房东租房子。这个中介就可以作为一个代理。
1. 房源
房源不会做任何操作,它是被别人拿到的,我们可以用java中的接口来表示,这个类没有别的操作。
1 2 3 4 5 6 7
| package src.JdkProxy.StaticProxy;
public interface Rent { public void rent(); }
|
2. 房东
房东有租出房的操作,所以作为一类
1 2 3 4 5 6 7 8 9
| package src.JdkProxy.StaticProxy; public class Host implements Rent { public void rent(){ System.out.println("房东要出租房子"); } }
|
3. 中介类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package src.JdkProxy.StaticProxy;
public class Proxy { private Host host; public Proxy(){} public Proxy(Host host){ this.host = host; } public void rent(){ host.rent(); } }
|
4. 客户
1 2 3 4 5 6 7 8 9 10 11 12 13
| package src.JdkProxy.StaticProxy;
public class Client { public static void main(String[] args) { Host host = new Host(); Proxy proxy = new Proxy(host); proxy.rent(); } }
|
当然中介代理是一类人,他自然有和房东不同的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package src.JdkProxy.StaticProxy;
public class Proxy { private Host host; public Proxy(){} public Proxy(Host host){ this.host = host; } public void rent(){ host.rent(); contract(); fare(); } public void seeHouse(){ System.out.println("中介带你看房"); } public void fare(){ System.out.println("收中介费"); } public void contract(){ System.out.println("签租赁合同"); } }
|
总体静态代理比较简单,简单易懂,但是就是如果说房东很多时候,代码就要很繁琐。
2. 动态代理
2.1 源码
- 动态代理的角色和静态代理的一样。需要一个实体类,一个代理类,一个启动器。
- 动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的。
- ①:我们代理的是接口,而不是单个用户。
- ②:代理类是动态生成的,而非静态定死。
两个重要的类
2.2 InvocationHandler
1 2 3 4
| public interface InvocationHandler
|
通过这个接口,我们可以在代理时候做一些我们被代理的实体没有实现的方法和操作。也就是说他是个处理程序模块,我们的InvocationHandler 接口就是自动执行这个方法,因此这也是java反序列中一个很好的利用点,他跟readobject一样,都是在proxy对象建立之后代理对象执行方法时候,他会自动触发。
1 2 3 4 5
| Object invoke(Object proxy, 方法 method, Object[] args);
proxy 代理 method proxy执行的被代理对象的方法 args 执行方法的参数
|
2.3 Proxy
1
| public class Proxy extends Object implements Serializable
|
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口
1 2 3
|
public static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
|
this.getClass().getClassLoader()
- 这里的
this
是指 InvocationHandler
的实例(也就是你实现了 invoke()
方法的对象)。调用 getClass()
方法会返回当前对象的 Class
对象,然后通过 getClassLoader()
获取该类的类加载器。
- 作用:
getClass().getClassLoader()
返回的是加载 InvocationHandler
实现类的类加载器。这个类加载器会用来加载动态代理类。
- 这个类加载器是动态代理类的载体,负责加载代理对象的字节码。
. userService.getClass().getInterfaces()
userService
是你想要代理的实际对象。通过 getClass()
可以获取该对象的 Class
对象,然后通过 getInterfaces()
获取这个类所实现的所有接口。
- 作用:
getInterfaces()
返回的是目标对象 userService
实现的所有接口,代理对象需要实现这些接口。动态代理对象会实现目标类所实现的所有接口,从而可以通过代理对象调用这些接口的方法。
this
(即 InvocationHandler
实现类的实例)
this
是指当前的 InvocationHandler
实现对象,它实现了 invoke()
方法,负责拦截代理对象的方法调用并提供自己的处理逻辑。
因此当动态代理创建好之后,我们的所有操作都是在刚才的程序处理接口中进行的,invoke 就是程序处理程序
2.4 实例
此时有一个接口,同时开始实现这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package src.JdkProxy.DynamicProxy; public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
package src.JdkProxy.DynamicProxy; public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("更新了一个用户"); } @Override public void query() { System.out.println("查询了一个用户"); } }
|
此时我们实现动态代理让这个接口增加一些逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package src.JdkProxy.DynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class UserProxyInvocationHandler implements InvocationHandler { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public Object getProxy(){ Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), userService.getClass().getInterfaces(), this); return obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method); Object obj = method.invoke(userService, args); return obj; } public void log(Method method){ System.out.println("[Info] " + method.getName() + "方法被调用"); } }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package src.JdkProxy.DynamicProxy; import src.JdkProxy.DynamicProxy.UserServiceImpl; public class Client { public static void main(String[] args) { UserServiceImpl userServiceImpl = new UserServiceImpl(); UserProxyInvocationHandler userProxyInvocationHandler = new UserProxyInvocationHandler(); userProxyInvocationHandler.setUserService((UserService) userServiceImpl); UserService proxy = (UserService) userProxyInvocationHandler.getProxy(); proxy.add(); proxy.delete(); proxy.update(); proxy.query(); } }
|
因此其实 invoke 这个函数作用是阻止动态代理执行被代理类的方法,从而先执行增加的逻辑,然后再使用method方法的invoke 执行被代理类的方法。
3. 反序列化
其实readobjec 也是再反序列化时候自动执行,其中也有包括反射在内的利用而动态代理的invoke
入口类 A
存在 O.abc
这个方法,也就是 A[O] -> O.abc;而 O 呢,如果是一个动态代理类,在O进行一些方法调用时候首先会执行invoke 方法的增加逻辑,一旦O
的 invoke
方法里存在能调用有危险操作 .f
的方法的地方,那我们就可以进行利用
1 2 3 4 5 6 7 8 9
| # readobjec 时候如果有能够传参入一个对象,URLDNS链子就是传递了URL 类,因此在readobject时候直接一直打到URL类的危险操作。
# 如果说我不传递类对象,我传递一个动态代理类。那么我进去肯定要执行方法,那我即使我不像URLDNS链子一样有危险操作,但是我可以自动触发invoke方法,invoke方法又跟readobjet方法一样可以自动触发一些操作,如果说我们能够传递一个有危险操作的类,那我就又可以执行了. A[O] -> O.abc
O[O2] invoke -> O2.f // 此时将 B 去替换 O2 最后 ----> O[B] invoke -> B.f // 达到漏洞利用效果
|