智能控制原理实践
最近期末 啥都要学 记录一下 很多东西直接搬运
最近期末 啥都要学 记录一下 很多东西直接搬运
继续读面试老师给的论文 14年 o.o
继续努力
学点技术 没坏处
解决一个git的bug
【Git】OpenSSL SSL_read: Connection was aborted, errno 10053
1 | git config --global http.postBuffer 524288000 |
似乎加上这个 hexo 就没出过问题
1 | git config --global http.sslVerify "false" |
远程方法调用是分布式编程中的一个基本思想。实现远程方法调用的技术有很多,比如:CORBA、WebService,这两种都是独立于编程语言的。而RMI(Remote Method Invocation)是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法。RMI依赖的通信协议为JRMP(Java Remote Message Protocol ,Java 远程消息交换协议),该协议为Java定制,要求服务端与客户端都为Java编写。这个协议就像HTTP协议一样,规定了客户端和服务端通信要满足的规范。在RMI中对象是通过序列化方式进行编码传输的。
使用远程方法调用,必然会涉及参数的传递和执行结果的返回。参数或者返回值可以是基本数据类型,当然也有可能是对象的引用。所以这些需要被传输的对象必须可以被序列化,这要求相应的类必须实现 java.io.Serializable 接口,并且客户端的serialVersionUID字段要与服务器端保持一致。
任何可以被远程调用方法的对象必须实现 java.rmi.Remote 接口,远程对象的实现类必须继承UnicastRemoteObject类。如果不继承UnicastRemoteObject类,则需要手工初始化远程对象,在远程对象的构造方法的调用UnicastRemoteObject.exportObject()静态方法。如下:
1 | public class HelloImpl implements IHello { |
注: IHello是客户端和服务端共用的接口(客户端本地必须有远程对象的接口,不然无法指定要调用的方法,而且其全限定名必须与服务器上的对象完全相同),HelloImpl是一个服务端远程对象,提供了一个sayHello方法供远程调用。它没有继承UnicastRemoteObject类或者实现java.rmi.Remote接口,而是在构造方法中调用了UnicastRemoteObject.exportObject()。
在JVM之间通信时,RMI对远程对象和非远程对象的处理方式是不一样的,它并没有直接把远程对象复制一份传递给客户端,而是传递了一个远程对象的Stub,Stub基本上相当于是远程对象的引用或者代理。Stub对开发者是透明的,客户端可以像调用本地方法一样直接通过它来调用远程方法。Stub中包含了远程对象的定位信息,如Socket端口、服务端主机地址等等,并实现了远程调用过程中具体的底层网络通信细节,所以RMI远程调用逻辑是这样的:
从逻辑上来看,数据是在Client和Server之间横向流动的,但是实际上是从Client到Stub,然后从Skeleton到Server这样纵向流动的。
那怎么获取Stub呢?
Stub的获取方式有很多,常见的方法是调用某个远程服务上的方法,向远程服务获取存根。但是调用远程方法又必须先有远程对象的Stub,所以这里有个死循环问题。JDK提供了一个RMI注册表(RMIRegistry)来解决这个问题。RMIRegistry也是一个远程对象,默认监听在传说中的1099端口上,可以使用代码启动RMIRegistry,也可以使用rmiregistry命令。
要注册远程对象,需要RMI URL和一个远程对象的引用。
1 | IHello rhello = new HelloImpl(); |
LocateRegistry.getRegistry()会使用给定的主机和端口等信息本地创建一个Stub对象作为Registry远程对象的代理,从而启动整个远程调用逻辑。服务端应用程序可以向RMI注册表中注册远程对象,然后客户端向RMI注册表查询某个远程对象名称,来获取该远程对象的Stub。
1 | Registry registry = LocateRegistry.getRegistry("kingx_kali_host",1099); |
使用RMI Registry之后,RMI的调用关系是这样的:
所以其实从客户端角度看,服务端应用是有两个端口的,一个是RMI Registry端口(默认为1099),另一个是远程对象的通信端口(随机分配的)。这个通信细节比较重要,真实利用过程中可能会在这里遇到一些坑。
RMI核心特点之一就是动态类加载,如果当前JVM中没有某个类的定义,它可以从远程URL去下载这个类的class,动态加载的对象class文件可以使用Web服务的方式进行托管。这可以动态的扩展远程应用的功能,RMI注册表上可以动态的加载绑定多个RMI应用。对于客户端而言,服务端返回值也可能是一些子类的对象实例,而客户端并没有这些子类的class文件,如果需要客户端正确调用这些子类中被重写的方法,则同样需要有运行时动态加载额外类的能力。客户端使用了与RMI注册表相同的机制。RMI服务端将URL传递给客户端,客户端通过HTTP请求下载这些类。
这个概念比较重要,JNDI注入的利用方法中也借助了动态加载类的思路。
这里涉及到的角色:客户端、RMI注册表、远程对象服务器、托管class文件的Web服务器可以分别位于不同的主机上:
接口
1 | package com.run.rmi; |
实现类
1 | package com.run.rmi; |
client – 在同进程中 我们当然可以直接调用 但是在不同进程中呢
1 | package client; |
首先在接口要调用 Remote 类
1 | package com.run.rmi; |
然后在 实现类 要继承 UnicastRemoteObject
1 | package com.run.rmi; |
然后我们要开个服务
1 | package com.run.rmi; |
再回到客户端
1 | package client; |
之后先开 服务端 再开客户端 发现调用成功!
首先考虑一个问题 如果让你自己写一个这样的功能 你需要考虑哪些问题?
1.提供对外的服务(socket)
2.服务如何调用?
跟了下源码 客可画出如下类图
服务端启动 Registry 的过程
1 | LocateRegistry.createRegistry(1099); |
跟一下 createRegistry
其反回了一个 接口的调用
1 | public static Registry createRegistry(int port) throws RemoteException { |
然后 Registry 仍然是继承了 Remote 类
1 | public interface Registry extends Remote { |
也就是说 RegistryImpl
和 HelloServiceImpl
是属于同一个等级的 且都会被包装成代理对象
即RegistryImpl stub
HelloServiceImpl stub
1 | public RegistryImpl(int port) |
看到了 UnicastServerRef
之前的 HelloServiceImpl
也会跟到这里 UnicastServerRef
1 | public static Remote exportObject(Remote obj, int port) |
绑定url和对应服务的关系 类似于注册中心
1 | Naming.rebind("rmi://127.0.0.1/Hello",helloService); |
再跟 exportObject
会 跟到一些协议上去 这里就不再深入了 毕竟我们是搞安全的
JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。 JNDI可以兼容和访问现有目录服务如:DNS、XNam、LDAP、CORBA对象服务、文件系统、RMI、DSML v1&v2、NIS等。
我在这里用DNS做一个不严谨的比喻来理解JNDI。当我们想访问一个网站的时候,我们已经习惯于直接输入域名访问了,但其实远程计算机只有IP地址可供我们访问,那就需要DNS服务做域名的解析,取到对应的主机IP地址。JNDI充当了类似的角色,使用统一的接口去查找对应的不同的服务类型。
这些命名/目录服务提供者:
- RMI (JAVA远程方法调用)
- LDAP (轻量级目录访问协议)
- CORBA (公共对象请求代理体系结构)
- DNS (域名服务)
简单来说,JNDI (Java Naming and Directory Interface) 是一组应用程序接口,它为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定位用户、网络、机器、对象和服务等各种资源。比如可以利用JNDI在局域网上定位一台打印机,也可以用JNDI来定位数据库服务或一个远程Java对象。JNDI底层支持RMI远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。
JNDI支持多种命名和目录提供程序(Naming and Directory Providers),RMI注册表服务提供程序(RMI Registry Service Provider)允许通过JNDI应用接口对RMI中注册的远程对象进行访问操作。将RMI服务绑定到JNDI的一个好处是更加透明、统一和松散耦合,RMI客户端直接通过URL来定位一个远程对象,而且该RMI服务可以和包含人员,组织和网络资源等信息的企业目录链接在一起。
JNDI客户端调用方式
1 | //指定需要查找name名称 |
这里的jndiName变量的值可以是上面的命名/目录服务列表里面的值
JNDI接口在初始化时,可以将RMI URL作为参数传入,而JNDI注入就出现在客户端的lookup()函数中,如果lookup()的参数可控就可能被攻击。
深入理解JNDI注入与Java反序列化漏洞利用 - FreeBuf网络安全行业门户
Java 反序列化过程中 RMI JRMP 以及 JNDI 多种利用方式详解 (seebug.org)
快一年前写的文章?自从决定走留学道路 web研究这方面进度慢了太多了吧 生活充满了艰辛和无奈啊 继续拾起来了
这几天肝了一个数据库的前端页面虽然很low 但是还是学会了很多前端的知识 记一些小技巧 以后说不定还会遇到
一个令我头疼的问题