應用安全:JAVA反序列化漏洞之殤

應用安全:JAVA反序列化漏洞之殤

概述

  • 序列化是讓Java對象脫離Java運行環境的一種手段,可以有效的實現多平臺之間的通信、對象持久化存儲。
  • Java 序列化是指把 Java 對象轉換為字節序列的過程便于保存在內存、文件、數據庫中,ObjectOutputStream類的 writeObject() 方法可以實現序列化。反序列化是指把字節序列恢復為 Java 對象的過程,ObjectInputStream 類的 readObject() 方法用于反序列化。

漏洞成因

  • 序列化和反序列化本身并不存在問題。但當輸入的反序列化的數據可被用戶控制,那么攻擊者即可通過構造惡意輸入,讓反序列化產生非預期的對象,在此過程中執行構造的任意代碼。

  • 漏洞代碼示例如下:

  • ......
    //讀取輸入流,并轉換對象
    InputStream in=request.getInputStream();
    ObjectInputStream ois = new ObjectInputStream(in);
    //恢復對象
    ois.readObject();
    ois.close();
    
  • 這里特別要注意的是非預期的對象,正因為此java標準庫及大量第三方公共類庫成為反序列化漏洞利用的關鍵。安全研究人員已經發現大量利用反序列化漏洞執行任意代碼的方法,最讓大家熟悉的是Gabriel Lawrence和Chris Frohoff在《Marshalling Pickles how deserializing objects can ruin your day》中提出的利用Apache Commons Collection實現任意代碼執行。此后安全研究人員也陸續爆出XML、Json、Yaml等反序列化的相關漏洞。

  • 除了commons-collections 3.1可以用來利用java反序列化漏洞,還有更多第三方庫同樣可以用來利用反序列化漏洞并執行任意代碼,部分如下:

  • commons-fileupload 1.3.1

  • commons-io 2.4

  • commons-collections 3.1

  • commons-logging 1.2

  • commons-beanutils 1.9.2

  • org.slf4j:slf4j-api 1.7.21

  • com.mchange:mchange-commons-java 0.2.11

  • org.apache.commons:commons-collections 4.0

  • com.mchange:c3p0 0.9.5.2

  • org.beanshell:bsh 2.0b5

  • org.codehaus.groovy:groovy 2.3.9

  • ……

Java反序列化詳解

序列化數據結構

  • 通過查看序列化后的數據,可以看到反序列化數據開頭包含兩字節的魔術數字,這兩個字節始終為十六進制的0xAC ED。接下來是兩字節的版本號0x00 05的數據。此外還包含了類名、成員變量的類型和個數等。

  • 這里以類SerialObject示例來詳細進行介紹Java對象序列化后的數據結構:

  • public class SerialObject implements Serializable{private static final long serialVersionUID = 5754104541168322017L;private int id;public String name;public SerialObject(int id,String name){this.id=id;this.name=name;}...
    }
    
  • 序列化SerialObject實例后以二進制格式查看:

  • 00000000: aced 0005 7372 0024 636f 6d2e 7878 7878  ....sr.$com.xxxx
    00000010: 7878 2e73 6563 2e77 6562 2e68 6f6d 652e  xx.sec.web.home.
    00000020: 5365 7269 616c 4f62 6a65 6374 4fda af97  SerialObjectO...
    00000030: f8cc c5e1 0200 0249 0002 6964 4c00 046e  .......I..idL..n
    00000040: 616d 6574 0012 4c6a 6176 612f 6c61 6e67  amet..Ljava/lang
    00000050: 2f53 7472 696e 673b 7870 0000 07e1 7400  /String;xp....t.
    00000060: 0563 7279 696e 0a                        .cryin.
    
  • 序列化的數據流以魔術數字和版本號開頭,這個值是在調用ObjectOutputStream序列化時,由writeStreamHeader方法寫入:

  • protected void writeStreamHeader() throws IOException {bout.writeShort(STREAM_MAGIC);//STREAM_MAGIC (2 bytes) 0xACEDbout.writeShort(STREAM_VERSION);//STREAM_VERSION (2 bytes) 5}
    
  • 序列化后的SerialObject對象詳細結構:

  • STREAM_MAGIC (2 bytes) 0xACED 
    STREAM_VERSION (2 bytes) 0x0005TC_OBJECT (1 byte) 0x73TC_CLASSDESC (1 byte) 0x72classNamelength (2 bytes) 0x24 = 36text (36 bytes) com.xxxxxx.sec.web.home.SerialObjectserialVersionUID (8 bytes) 0x4FDAAF97F8CCC5E1 = 5754104541168322017classDescInfoclassDescFlags (1 byte) 0x02 = SC_SERIALIZABLEfieldscount (2 bytes) 2field[0]primitiveDescprim_typecode (1 byte) I = integerfieldNamelength (2 bytes) 2text (2 bytes) idfield[1]objectDescobj_typecode (1 byte) L = objectfieldNamelength (2 bytes) 4text (4 bytes)  nameclassName1TC_STRING (1 byte) 0x74length (2 bytes) 0x12 = 18text (18 bytes) Ljava/lang/String;classAnnotationTC_ENDBLOCKDATA (1 byte) 0x78superClassDescTC_NULL (1 byte) 0x70classdata[]classdata[0] (4 bytes) 0xe107 = id = 2017classdata[1]TC_STRING (1 byte) 0x74length (2 bytes) 5text (10 bytes) cryin
    

反序列化過程詳解

  • Java程序中類ObjectInputStream的readObject方法被用來將數據流反序列化為對象,如果流中的對象是class,則它的ObjectStreamClass描述符會被讀取,并返回相應的class對象,ObjectStreamClass包含了類的名稱及serialVersionUID。
  • 如果類描述符是動態代理類,則調用resolveProxyClass方法來獲取本地類。如果不是動態代理類則調用resolveClass方法來獲取本地類。如果無法解析該類,則拋出ClassNotFoundException異常。
  • 如果反序列化對象不是String、array、enum類型,ObjectStreamClass包含的類會在本地被檢索,如果這個本地類沒有實現java.io.Serializable或者externalizable接口,則拋出InvalidClassException異常。因為只有實現了Serializable和Externalizable接口的類的對象才能被序列化。

反序列化漏洞檢測方案

代碼審計

  • 反序列化操作一般在導入模版文件、網絡通信、數據傳輸、日志格式化存儲、對象數據落磁盤或DB存儲等業務場景,在代碼審計時可重點關注一些反序列化操作函數并判斷輸入是否可控,如下:

  • ObjectInputStream.readObject
    ObjectInputStream.readUnshared
    XMLDecoder.readObject
    Yaml.load
    XStream.fromXML
    ObjectMapper.readValue
    JSON.parseObject
    ...
    
  • 同時也要關注第三jar包是否提供了一些公共的反序列化操作接口,如果沒有相應的安全校驗如白名單校驗方案,且輸入可控的話就也可能存在安全問題。

進階審計

  • 對于直接獲取用戶輸入進行反序列化操作這種點比較好審計并發現,目前反序列化漏洞已經被談起太多次了,所以有經驗的開發都會在代碼中有相應的修復。但并不是所有修復都無懈可擊。比如采用黑名單校驗的修復方式,對于這種修復可在工程代碼中嘗試挖掘新的可以利用的’gadget‘。

  • 代碼中有使用到反序列化操作,那自身項目工程中肯定存在可以被反序列化的類,包括Java自身、第三方庫有大量這樣的類,可被反序列化的類有一個特點,就是該類必定實現了Serializable接口,Serializable 接口是啟用其序列化功能的接口,實現 java.io.Serializable 接口的類才是可序列化的。一個典型的示例如下:

  • public class SerialObject implements Serializable{private static final long serialVersionUID = 5754104541168322017L;private int id;public String name;public SerialObject(int id,String name){this.id=id;this.name=name;}public void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{//執行默認的readObject()方法in.defaultReadObject();}
    }
    
  • 所以在代碼審計時對這些類也可進行特別關注,分析并確認是否有可能被發序列化漏洞利用執行任意代碼。發現新的可利用的類即可突破使用黑名單進行校驗的一些應用。

白盒檢測

  • 大型企業的應用很多,每個都人工去審計不現實,往往都有相應的自動化靜態代碼審計工具,這里以ObjectInputStream.readObject()為例,其它反序列化接口的檢測原理也相似。在自動化檢測時,可通過實現解析java源代碼,檢測readObject()方法調用時判斷其對象是否為java.io.ObjectOutputStream。如果此時ObjectInputStream對象的初始化參數來自外部請求輸入參數則基本可以確定存在反序列化漏洞了。這是只需確認是否存在相應的安全修復即可。 可參考lgtm.com對于Deserialization of user-controlled data檢測方式的實現。

黑盒檢測

  • 調用ysoserial并依次生成各個第三方庫的利用payload(也可以先分析依賴第三方包量,調用最多的幾個庫的paylaod即可),該payload構造為訪問特定url鏈接的payload,根據http訪問請求記錄判斷反序列化漏洞是否利用成功。如:

  • java -jar ysoserial.jar CommonsCollections1 'curl " + URL + " '
    
  • 也可通過DNS解析記錄確定漏洞是否存在。現成的輪子很多,推薦NickstaDB寫的SerialBrute,還有一個針對RMI的測試工具BaRMIe,也很不錯~。.

RASP檢測

  • Java程序中類ObjectInputStream的readObject方法被用來將數據流反序列化為對象,如果流中的對象是class,則它的ObjectStreamClass描述符會被讀取,并返回相應的class對象,ObjectStreamClass包含了類的名稱及serialVersionUID。

  • 類的名稱及serialVersionUID的ObjectStreamClass描述符在序列化對象流的前面位置,且在readObject反序列化時首先會調用resolveClass讀取反序列化的類名,所以RASP檢測反序列化漏洞時可通過重寫ObjectInputStream對象的resolveClass方法獲取反序列化的類即可實現對反序列化類的黑名單校驗。

  • 百度的開源RASP產品就是使用的這種方法,具體可參考其DeserializationHook.java的實現:

  • @Overrideprotected MethodVisitor hookMethod(int access, String name, String desc,String signature, String[] exceptions, MethodVisitor mv) {if ("resolveClass".equals(name) && "(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;".equals(desc)) {return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {@Overrideprotected void onMethodEnter() {loadArg(0);invokeStatic(Type.getType(HookHandler.class),new Method("checkDeserializationClass", "(Ljava/io/ObjectStreamClass;)V"));}};}return mv;}
    
  • 其中檢測覆蓋的反序列化類黑名單如下:

  • plugin.register('deserialization', function (params, context) {var deserializationInvalidClazz = ['org.apache.commons.collections.functors.InvokerTransformer','org.apache.commons.collections.functors.InstantiateTransformer','org.apache.commons.collections4.functors.InvokerTransformer','org.apache.commons.collections4.functors.InstantiateTransformer','org.codehaus.groovy.runtime.ConvertedClosure','org.codehaus.groovy.runtime.MethodClosure','org.springframework.beans.factory.ObjectFactory','xalan.internal.xsltc.trax.TemplatesImpl']var clazz = params.clazzfor (var index in deserializationInvalidClazz) {if (clazz === deserializationInvalidClazz[index]) {return {action:     'block',message:    '嘗試反序列化攻擊',confidence: 100}}}return clean
    })
    

攻擊檢測

  • 通過查看反序列化后的數據,可以看到反序列化數據開頭包含兩字節的魔術數字,這兩個字節始終為十六進制的0xAC ED。接下來是兩字節的版本號。我只見到過版本號為5(0x00 05)的數據。考慮到zip、base64各種編碼,在攻擊檢測時可針對該特征進行匹配請求post中是否包含反序列化數據,判斷是否為反序列化漏洞攻擊。

  • xxxdeMacBook-Pro:demo xxx$ xxd objectexp 00000000: aced 0005 7372 0032 7375 6e2e 7265 666c  ....sr.2sun.refl00000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41  ect.annotation.A00000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174  nnotationInvocat00000030: 696f 6e48 616e 646c 6572 55ca f50f 15cb  ionHandlerU.....
    
  • 但僅從特征匹配只能確定有攻擊嘗試請求,還不能確定就存在反序列化漏洞,還要結合請求響應、返回內容等綜合判斷是否確實存在漏洞。

Java反序列化漏洞修復方案

通過Hook resolveClass來校驗反序列化的類

  • 通過上面序列化數據結構可以了解到包含了類的名稱及serialVersionUID的ObjectStreamClass描述符在序列化對象流的前面位置,且在readObject反序列化時首先會調用resolveClass讀取反序列化的類名,所以這里通過重寫ObjectInputStream對象的resolveClass方法即可實現對反序列化類的校驗。這個方法最早是由IBM的研究人員Pierre Ernst在2013年提出《Look-ahead Java deserialization》,具體實現代碼示例如下:

  • public class AntObjectInputStream extends ObjectInputStream{public AntObjectInputStream(InputStream inputStream)throws IOException {super(inputStream);}/*** 只允許反序列化SerialObject class*/@Overrideprotected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,ClassNotFoundException {if (!desc.getName().equals(SerialObject.class.getName())) {throw new InvalidClassException("Unauthorized deserialization attempt",desc.getName());}return super.resolveClass(desc);}
    }
    
  • 通過此方法,可靈活的設置允許反序列化類的白名單,也可設置不允許反序列化類的黑名單。但反序列化漏洞利用方法一直在不斷的被發現,黑名單需要一直更新維護,且未公開的利用方法無法覆蓋。

  • SerialKiller 是由Luca Carettoni利用上面介紹的方法實現的反序列化類白/黑名單校驗的jar包。具體使用方法可參考其代碼倉庫。

  • contrast-rO0是一個輕量級的agent程序,通過通過重寫ObjectInputStream來防御反序列化漏洞攻擊。使用其中的SafeObjectInputStream類來實現反序列化類白/黑名單控制,示例代碼如下:

  • SafeObjectInputStream in = new SafeObjectInputStream(inputStream, true);
    in.addToWhitelist(SerialObject.class);in.readObject();
    

使用ValidatingObjectInputStream來校驗反序列化的類

  • 使用Apache Commons IO Serialization包中的ValidatingObjectInputStream類的accept方法來實現反序列化類白/黑名單控制,具體可參考ValidatingObjectInputStream介紹;示例代碼如下:

  • private static Object deserialize(byte[] buffer) throws IOException,
    ClassNotFoundException , ConfigurationException {Object obj;ByteArrayInputStream bais = new ByteArrayInputStream(buffer);// Use ValidatingObjectInputStream instead of InputStreamValidatingObjectInputStream ois = new 	ValidatingObjectInputStream(bais); //只允許反序列化SerialObject classois.accept(SerialObject.class);obj = ois.readObject();return obj;
    }
    

使用ObjectInputFilter來校驗反序列化的類

  • Java 9包含了支持序列化數據過濾的新特性,開發人員也可以繼承java.io.ObjectInputFilter類重寫checkInput方法實現自定義的過濾器,,并使用ObjectInputStream對象的setObjectInputFilter設置過濾器來實現反序列化類白/黑名單控制。示例代碼如下:

  • import java.util.List;
    import java.util.Optional;
    import java.util.function.Function;
    import java.io.ObjectInputFilter;
    class BikeFilter implements ObjectInputFilter {private long maxStreamBytes = 78; // Maximum allowed bytes in the stream.private long maxDepth = 1; // Maximum depth of the graph allowed.private long maxReferences = 1; // Maximum number of references in a graph.@Overridepublic Status checkInput(FilterInfo filterInfo) {if (filterInfo.references() < 0 || filterInfo.depth() < 0 || filterInfo.streamBytes() < 0 || filterInfo.references() > maxReferences || filterInfo.depth() > maxDepth|| filterInfo.streamBytes() > maxStreamBytes) {return Status.REJECTED;}Class<?> clazz = filterInfo.serialClass();if (clazz != null) {if (SerialObject.class == filterInfo.serialClass()) {return Status.ALLOWED;}else {return Status.REJECTED;}}return Status.UNDECIDED;} // end checkInput
    } // end class BikeFilter
    
  • 上述示例代碼,僅允許反序列化SerialObject類對象,上述示例及更多關于ObjectInputFilter的均參考自NCC Group Whitepaper由Robert C. Seacord寫的《Combating Java Deserialization Vulnerabilities with Look-Ahead Object Input Streams (LAOIS)》

黑名單校驗修復

  • 在反序列化時設置類的黑名單來防御反序列化漏洞利用及攻擊,這個做法在源代碼修復的時候并不是推薦的方法,因為你不能保證能覆蓋所有可能的類,而且有新的利用payload出來時也需要隨之更新黑名單。
  • 但有某些場景下可能又不得不選擇黑名單方案。寫代碼的時候總會把一些經常用到的方法封裝到公共類,這樣其它工程中用到只需要導入jar包即可,此前已經見到很多提供反序列化操作的公共接口,使用第三方庫反序列化接口就不好用白名單的方式來修復了。這個時候作為第三方庫也不知道誰會調用接口,會反序列化什么類,所以這個時候可以使用黑名單的方式來禁止一些已知危險的類被反序列化,部分的黑名單類如下:
  • org.apache.commons.collections.functors.InvokerTransformer
  • org.apache.commons.collections.functors.InstantiateTransformer
  • org.apache.commons.collections4.functors.InvokerTransformer
  • org.apache.commons.collections4.functors.InstantiateTransformer
  • org.codehaus.groovy.runtime.ConvertedClosure
  • org.codehaus.groovy.runtime.MethodClosure
  • org.springframework.beans.factory.ObjectFactory
  • com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
  • org.apache.commons.fileupload
  • org.apache.commons.beanutils

安全編碼建議

  • 更新commons-collections、commons-io等第三方庫版本;
  • 業務需要使用反序列化時,盡量避免反序列化數據可被用戶控制,如無法避免建議盡量使用白名單校驗的修復方式;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/213130.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/213130.shtml
英文地址,請注明出處:http://en.pswp.cn/news/213130.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MYSQL創建用戶以及解決MYSQL新建數據庫看不到相關數據庫信息的解決方案

MYSQL創建用戶以及解決MYSQL新建數據庫看不到相關數據庫信息的解決方案 首先 我們在root用戶下&#xff0c;創建一個mysql用戶賬戶 如創建一個test用戶 密碼123456 命令如下&#xff1a; CREATE USER ‘test’‘localhost’ IDENTIFIED BY ‘123456’; 創建了一個db1的數據庫 …

唯創知音WTN6040F-8S語音芯片在空氣消毒機中的應用:提升用戶體驗與健康保障

在現代生活中&#xff0c;空氣質量成為人們越來越關注的問題。空氣消毒機作為一種能夠凈化空氣、殺滅病毒細菌的設備&#xff0c;受到了廣大消費者的青睞。然而&#xff0c;對于很多用戶來說&#xff0c;操作空氣消毒機可能存在一定的困惑和不便。為了解決這一問題&#xff0c;…

對Spring源碼的學習:Bean實例化流程

目錄 SpringBean實例化流程 Spring的后處理器 Bean工廠后處理器 Bean后處理器 SpringBean實例化流程 Spring容器在進行初始化時&#xff0c;會將xml配置的<bean>的信息封裝成一個BeanDefinition對象&#xff0c;所有的BeanDefinition存儲到一個名為beanDefinitionMa…

Docker容器的可視化管理工具—DockerUI本地部署與遠程訪問

文章目錄 前言1. 安裝部署DockerUI2. 安裝cpolar內網穿透3. 配置DockerUI公網訪問地址4. 公網遠程訪問DockerUI5. 固定DockerUI公網地址 前言 DockerUI是一個docker容器鏡像的可視化圖形化管理工具。DockerUI可以用來輕松構建、管理和維護docker環境。它是完全開源且免費的。基…

【GlobalMapper精品教程】066:shp轉JSON(GeoJson)案例實現

文章目錄 一、JSON與GeoJson的區別二、globalmapper實現shp轉JSON1. 加載shp數據2. shp轉json一、JSON與GeoJson的區別 JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,它基于JavaScript的語法,可以將JavaScript對象中表示的一組數據轉換為字符串,在函數之…

ElasticSearch之cat recovery API

命令樣例如下&#xff1a; curl -X GET "https://localhost:9200/_cat/recovery?vtrue&pretty" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"執行結果輸出如下&#xff1a; index shard time type sta…

2023 CCF中國軟件大會(CCF ChinaSoft) “程序語義深度理解前沿進展”論壇成功召開...

2023年12月2日&#xff0c;2023年度CCF中國軟件大會軟件程序語義深度理解前沿進展論壇成功召開。 本次論壇由南京大學卜磊老師和國防科技大學陳振邦老師主持&#xff0c;計算機研究與發展期刊代表侯麗珊老師致辭&#xff0c;旨在反映程序語義理解及其應用相關研究前沿進展與實踐…

Vue之模板語法

模板語法有兩大類&#xff1a; 1.插值語法 2.指令語法 讓我為大家介紹一下吧&#xff01; 一、插值語法 功能:用于解析標簽體內容。 寫法: {{xxx}}&#xff0c;xxx是js表達式&#xff0c;且可以直接讀取到data中的所有屬性。 舉個例子&#xff1a; <!DOCTYPE html> &l…

探索未來新趨勢:鴻蒙系統的嶄新時代

探索未來新趨勢&#xff1a;鴻蒙系統的嶄新時代 隨著科技的不斷發展&#xff0c;操作系統作為計算機和移動設備的核心&#xff0c;扮演著至關重要的角色。近年來&#xff0c;一種備受矚目的操作系統——鴻蒙系統&#xff08;HarmonyOS&#xff09;嶄露頭角&#xff0c;正引領著…

uniapp 微信小程序請求攔截器 接口封裝

前言&#xff1a; 請求攔截器可以在我們需要傳遞請求頭的時候使用&#xff0c;例如&#xff1a;token 也會在當token發生變化的時候給予響應&#xff0c;所以我們做好對應的判斷即可 話不多說&#xff0c;直接進入正題&#xff1a; 1.首先在根目錄創建common文件夾&#xff0c…

Selenium 中并行測試的重要性!

隨著技術的進步&#xff0c;測試解決方案變得更具可擴展性&#xff0c;加速了團隊從手動測試到Selenium測試自動化的轉型。但是成年人的世界&#xff0c;沒有什么是容易的。對于許多團隊來說&#xff0c;并行運行多個測試仍然是不可擴展的。他們傾向于遵循傳統的順序執行測試方…

MIT6.5840-2023-Lab2A: Raft-leader election

前置知識 什么是一致性算法&#xff1f; 安全性保證&#xff0c;絕對不會返回一個錯誤的結果&#xff1b;可用性&#xff0c;容忍集群部分節點失敗&#xff1b;不依賴時序來保證一致性&#xff1b;一條指令可以盡可能快的在集群中大多數節點響應一輪遠程過程調用時完成。小部分…

uniapp實戰 —— 可滾動區域 scroll-view (自適配高度,下拉刷新)

自適配高度 自定義的頂部導航欄&#xff0c;可參考博文 https://blog.csdn.net/weixin_41192489/article/details/134852124 如圖可見&#xff0c;在頁面滾動過程中&#xff0c;頂部導航欄和底欄未動&#xff0c;僅中間的內容區域可滾動。 整個頁面的高度設置為 100%&#xf…

鴻蒙開發—學習聲明式UI

基本UI描述 ArkTS通過裝飾器Component和Entry裝飾struct關鍵字聲明的數據結構&#xff0c;構成一個自定義組件。自定義組件中提供了一個build函數&#xff0c;開發者需在該函數內以鏈式調用的方式進行基本的UI描述&#xff0c;UI描述的方法請參考UI描述規范。 基本概念 stru…

GZ029 智能電子產品設計與開發賽題第4套

2023年全國職業院校技能大賽高職組 “GZ029智能電子產品設計與開發”賽項賽卷四 題目&#xff1a;模擬工業傳送帶物品檢測系統的設計與開發 1 競賽任務 在智能電視機上播放工業傳送帶傳輸物品視頻&#xff0c;模擬工業傳送帶物品檢測系統&#xff08;以下簡稱物品檢測系統&…

DALI1.0學習——BIT解碼

最近在學習DALI調光相關知識并下載了Microchip提供的基于ATMega88PA的軟件工程及硬件設計參考方案。寫這些文章的目的就是把自己對知識的理解作一些梳理。 芯片廠果然專業&#xff0c;考慮得相當周到&#xff0c;為了芯片銷量連軟件和硬件方案全都提供了。芯片廠關于DALI1.0實…

【unity小技巧】實現槍武器隨鏡頭手臂搖擺效果

文章目錄 前言方法一、改變武器位置方法二、改變武器旋轉結語完結 前言 如果我們視角移動轉向&#xff0c;武器如果不跟著進行搖擺&#xff0c;會感覺我們的動作很生硬&#xff0c;特別是射擊類游戲&#xff0c;如下 實現武器搖擺這里主要分享兩種實現方法&#xff0c;一種是…

xtu oj 1271 color

題目描述 Alice在玩一個游戲&#xff0c;她在一個mn的格子里&#xff0c;隨機涂黑k個格子。然后她每次可以把一行或者一列的格子染成紅色&#xff0c;但是這一行中不能有黑色的格子。 請問她最多能把多少個格子涂成紅色&#xff1f; 輸入 第一行是一個整數T(T≤100)&#xf…

華為OD機試 - 數的分解(Java JS Python C)

題目描述 給定一個正整數 n,如果能夠分解為 m(m > 1)個連續正整數之和,請輸出所有分解中,m最小的分解。 如果給定整數無法分解為連續正整數,則輸出字符串"N"。 輸入描述 輸入數據為一整數,范圍為 (1, 2^30] 輸出描述 比如輸入為: 21 輸出: 21=10+11 …

SSD數據在寫入NAND之前為何要隨機化?-Part1

SSD的存儲介質是什么&#xff0c;它就是NAND閃存。那你知道NAND閃存是怎么工作的嗎&#xff1f;其實&#xff0c;它就是由很多個晶體管組成的。這些晶體管里面存儲著電荷&#xff0c;代表著我們的二進制數據&#xff0c;要么是“0”&#xff0c;要么是“1”。NAND閃存原理上是一…