【病毒分析】新兴TOP2勒索软件!存在中国受害者的BianLian勒索软件解密原理剖析

news2024/9/25 18:34:56

1. 概述

近期,笔者在浏览网络中威胁情报信息的时候,发现美国halcyon.ai公司于2024年3月25日发布了一篇《Ransomware on the Move: LockBit, BianLian, Medusa, Hunters International》报告,此报告对当前勒索软件团伙的实力进行了排名,排名顺序为:LockBit勒索软件、BianLian勒索软件、Medusa勒索软件、Hunters International勒索软件;进一步对报告内容进行研读,发现报告中在对BianLian勒索软件进行描述时,提到「总部位于上海的知名汽车电子企业科博达科技」已成为BianLian勒索软件团伙的受害者,相关报告截图如下:

图片

尝试对BianLian勒索软件团伙进行调研,发现BianLian勒索软件团伙的攻击方式为:利用漏洞在网络中传播,窃取最有价值的数据并对关键机器进行加密。相关报告截图如下:

图片

为了能够深层次的对BianLian勒索软件进行研究,笔者从近期Palo Alto Networks安全公司2024年1月23日发布的《Threat Assessment: BianLian》报告中提取了勒索加密工具的Hash信息,成功下载了多个样本用于研究剖析:

  • BianLian勒索软件功能剖析:梳理其样本功能及勒索加密后的特征。

  • BianLian勒索软件加解密原理剖析:梳理其加密算法及加密逻辑。

  • 构建BianLian勒索软件解密工具:结合加解密原理,模拟构建了一款针对BianLian勒索软件的解密工具,可实现一键化勒索解密。

相关报告Hash信息如下:

图片

2. 勒索软件功能分析

通过分析,发现BianLian勒索软件为Golang语言编写,相关截图如下:

图片

图片

进一步分析,发现此样本存在调试信息,可以较快速的识别关键代码函数,相关代码截图如下:

图片

2.1 遍历驱动器中的文件

通过分析,发现样本运行后,将从A盘到Z盘识别系统驱动器信息,并从系统中提取可用的文件,便于后续加密行为,相关代码截图如下:

图片

2.2 创建带付款说明的文本文件

通过分析,发现此样本运行后,将在加密行为开始前,在多个目录中创建名为“look at this Instruction.txt”的带付款说明的文本文件,相关代码截图如下:

图片

图片

实际加密行为截图如下:

图片

图片

2.3 加密系统文件

通过分析,发现样本运行后,将按照如下逻辑对系统中的文件进行加密:

  • 从待加密原始文件中读取指定大小数据块;

  • 调用AES CBC算法对数据块内容进行加密;

  • 将加密后的数据块内容写入原始文件中;「(此时,原始文件的内容已经被修改)」

  • 使用".bianlian"后缀重命名加密文件;

「备注:由于BianLian勒索软件是直接基于原始文件进行的数据读写操作,因此,系统文件被加密后,我们是无法通过数据恢复的方式恢复数据的。」

相关代码截图如下:

图片

3. 勒索软件加解密原理剖析

在尝试对BianLian勒索软件的加解密原理进行剖析的过程中,笔者也是经历了一波三折,「由于笔者在面对此样本的加解密问题时,总是以常规思维去思考此样本的加解密问题,导致在多个问题上走了弯路」

  • 最开始拿到这个样本时,笔者发现网络中针对此家族样本的报告有多种描述,一时分不清哪种说法正确

    • 描述1:“勒索软件主要使用GoLang加密软件包进行AES和RSA加密”;

    • 描述2:“样本中虽然引用了非对称加密库(RSA和elliptic curves),但勒索软件未调用其执行任何操作。文件数据是使用AES-256 CBC模式加密的”

  • 后续,笔者又发现网络中有报告称avast安全公司发布了免费的BianLian勒索软件解密器,因此,笔者推测BianLian勒索软件可能与描述2的内容吻合

  • 笔者尝试从近期Palo Alto Networks安全公司2024年1月23日发布的《Threat Assessment: BianLian》报告中提取BianLian勒索软件Hash,成功下载了5个样本,尝试对其进行分析,笔者发现此5个样本均调用了AES算法,但未调用RSA算法

  • 然后,笔者就开始琢磨研究BianLian勒索软件加解密原理及模拟编写解密工具

    • 「起初,笔者认为:勒索软件运行后,具备勒索软件后缀的文件均是被其勒索加密后的文件。」

    • 于是,笔者根据BianLian勒索软件的加密效果及加密算法原理,快速的编写了一个BianLian勒索软件解密工具

    • 基于笔者编写的BianLian勒索软件解密工具尝试对勒索软件运行后的加密文件进行解密,同时对加密前与解密后的文件MD5做对比,笔者发现存在大量文件MD5不同的情况......此刻有点慌,应该是解密工具编写的有问题。。。

    • 基于此情况,笔者又反复对比加密前与加密后的文件内容,发现部分文件虽然具备勒索软件后缀,但是文件内容却并未加密。后续,笔者还尝试构建了大量不同大小的文件,用以对比其加密前与加密后的变化情况,测试过程中还一度认为是勒索软件作者写代码时的文件加密逻辑没思考清楚......例如:区分使用按块加密还是整块加密的方法是基于文件大小的,但实际上,0xfff大小的文件是按块加密的,0x1009大小的文件是整块加密的,0x1000-0x1008大小的文件是不调用加密的

  • 最后,笔者决定通过动态调试探索一下加密逻辑,在动态调试后,笔者才比较清晰的对其加密逻辑进行了详细梳理,「发现样本中存在两层校验,用于确定是否对文件进行加密」

    • 在开始加密行为前,样本将定义三个特殊数据

      • 文件大小

      • 块数据大小,由文件大小计算所得

      • 加密偏移位置:「内置数据」,不同样本的加密偏移位置不同,用于指定从文件的哪个数据位置开始数据加密

    • 第一层校验:样本在开始加密行为前,将调用project1_common_GetBlocksAmount函数,并向project1_common_GetBlocksAmount函数传递上述3个特殊数据,函数返回值将决定样本是否调用加密函数加密文件

    • 第二层校验:样本将计算实际读取的文件大小(从加密偏移位置开始读取)是否与块大小相等,计算结果将决定样本是否调用加密函数加密文件

  • 「最终梳理发现,受害系统中虽然有很多勒索软后缀文件,但并非所有文件均被加密,而且不同文件大小所调用的加密算法逻辑也不一样」

3.1 加密算法

通过分析,笔者发现此样本将调用AES CBC算法进行勒索文件加密,相关代码截图如下:

图片

进一步分析,发现其AES CBC算法所需的KEY和IV值均内置于样本中,相关截图如下:

图片

图片

尝试将已掌握的所有样本的KEY和IV值进行提取,梳理密钥及关键信息如下:

/#1fd07b8d1728e416f897bef4f1471126f9b18ef108eb952f4b75050da22e8e43
加密偏移位置:9
KEY:633A56D0388869237C8DA6B7FC09F55DF35408C332614F692D11222583B36B62
IV:FC55A60A25B0472CFC2C6EBC3DD89DAB

#3a2f6e614ff030804aa18cb03fcc3bc357f6226786efb4a734cbe2a3a1984b6f
加密偏移位置:1
KEY:26DD1B400AED80B0980B34BAD76D3BE6599123FA562266B421A14AC8E02ECFA3
IV:D518BA928469306D579D625F56D09883

#46d340eaf6b78207e24b6011422f1a5b4a566e493d72365c6a1cace11c36b28b
加密偏移位置:0x34
KEY:979412FF08F7D2F0BD5F7E7E2A4919E9BF68CC7AABAB499872EC822DDCDA5307
IV:0FC14323F7A13CCA0569EBCFAE283996

#af46356eb70f0fbb0799f8a8d5c0f7513d2f6ade4f16d4869f2690029b511d4f
加密偏移位置:0x41
KEY:4A4105960D2C127D9711AC851BC1F10D17471B5A184CCDADA79003DA82CFDBA2
IV:5560E19D4B425420F6F5EF387D97065A

#eaf5e26c5e73f3db82cd07ea45e4d244ccb3ec3397ab5263a1a74add7bbcb6e2
加密偏移位置:0x3d
KEY:27CFAE34A83C9A7E48060E18A68A233914271DB7D414C838FB1EAEAEA89E5CDE
IV:223B67AC534F9938CC7B1F9777A95840

3.2 加密算法逻辑

结合静态分析及动态调试等分析手段,梳理BianLian勒索软件的加密算法逻辑如下:

  • 获取文件大小,并基于文件大小计算数据块大小

    • 若文件大小小于0x1000,则数据块大小为16**(按小数据块分别加密:从文件加密偏移位置开始提取多个数据块,并分别对每个小数据块内容进行加密)**

    • 若文件大小大于0x400000(4MB),则基于运算计算数据块大小,若计算后结果依然大于0x400000,则数据块大小为0x400000**(按大数据块整个加密:从文件加密偏移位置开始提取整个数据块,并直接对整个数据块内容进行加密)**

图片

  • 第一层校验:调用project1_common_GetBlocksAmount函数,并根据返回值判断是否对此文件进行加密

    图片

图片

  • 第二层校验:从***加密偏移位置***处开始读取块大小的文件内容,若实际读取数据大小等于块大小,则加密文件

图片

基于上述加密算法逻辑,以1fd07b8d1728e416f897bef4f1471126f9b18ef108eb952f4b75050da22e8e43样本作为案例样本(加密偏移位置:9),梳理不同文件大小的加密情况如下:

文件大小块大小第一层校验返回值第二层校验返回值是否加密加密方法
0x90x100x0falsefalse
0xa0x100x0falsefalse
0x170x100x1falsefalse
0x180x100x1falsefalse
0x190x100x1true「true」按小数据块分别加密
0x1a0x100x1true「true」按小数据块分别加密
0xfff0x100xfftrue「true」按小数据块分别加密
0x10000x10000x1falsefalse
0x10010x10000x1falsefalse
0x10080x10000x1falsefalse
0x10090x10000x1true「true」按大数据块整个加密
0x100a0x10000x1true「true」按大数据块整个加密
0x10240x10000x1true「true」按大数据块整个加密
0x3fffff0x3ff0000x0truefalse
0x4000000x4000000x0falsefalse
0x4000010x660000x1true「true」按大数据块整个加密

4. 构建BianLian勒索软件解密工具

通过上述分析,发现BianLian勒索软件的不同样本的AES KEY、AES IV、加密偏移位置均不同,因此,为了能够实现一键化解密,笔者准备从如下角度构建勒索软件解密工具:

  • 根据系统中的勒索加密文件情况,自动化匹配提取AES KEY、AES IV及加密偏移位置信息:以系统桌面中的desktop.ini文件作为参考文件,使用多个内置密钥进行解密尝试,若成功解密,则返回对应的AES KEY、AES IV及加密偏移位置信息。

  • 借助everything文件搜索工具,提取系统中的勒索后缀文件列表。

  • 基于上述BianLian勒索软件的加解密原理,模拟构建针对BianLian勒索软件的解密工具,解密还原原始文件,并将勒索后缀文件重命名为“.bak”文件后缀。

4.1 解密效果

勒索加密后,系统文件截图如下:

图片

使用BianLian勒索软件解密工具解密勒索加密文件,系统文件截图如下:

图片

加密前与解密后的文件MD5信息对比:「(共被勒索加密578个文件,使用BianLian勒索软件解密工具成功解密578个文件,仅有3个文件【系统运行过程中文件内容被修改导致】的MD5不同)」

图片

4.2 代码实现

在这里,笔者将使用golang语言模拟构建BianLian勒索软件的一键化解密工具,详细情况如下:

代码结构如下:

图片

  • main.go

package main

import (
 "awesomeProject5/common"
 "bytes"
 "encoding/hex"
 "fmt"
 "io/ioutil"
 "os"
 "os/user"
 "path/filepath"
 "strings"
)

func main() {
 //使用everything导出带勒索软件后缀的文件列表
 files := common.FileToSlice("C:\\Users\\admin\\Desktop\\11.txt")

 aeskey, aes_iv, offset := getAeskey()
 if aeskey == nil {
  fmt.Println("提取AES密钥失败")
  os.Exit(1)
 }
 fmt.Println("AES key:", hex.EncodeToString(aeskey))
 fmt.Println("AES iv:", hex.EncodeToString(aes_iv))
 fmt.Println("offset:", offset)

 for _, onefile := range files {
  fileSize, err := common.GetFileSize(onefile)
  if err != nil {
   fmt.Println("Error:", err)
   return
  }
  fmt.Printf("%s,0x%x\n", onefile, fileSize)

  file_decodeData := []byte{}
  fileData, err := ioutil.ReadFile(onefile)
  if err != nil {
   fmt.Println("Error reading file:", err)
   return
  }
  len_block := common.Calc_block(fileSize)
  if common.GetBlocksAmount(fileSize, len_block, int64(offset)) > 0 {
   if fileSize < 0x1000 {
                //按小数据块分别加密
    if (int64(offset) + len_block) > fileSize {
     file_decodeData = append(file_decodeData, fileData...)
    } else {
     file_decodeData = append(file_decodeData, fileData[:offset]...)

     blocks := (fileSize - int64(offset)) / 16

     for i := 0; i < int(blocks); i++ {
      output, _ := common.DecryptAES(fileData[offset+16*i:offset+16*(i+1)], aeskey, aes_iv)
      file_decodeData = append(file_decodeData, output...)
     }
     file_decodeData = append(file_decodeData, fileData[offset+int(blocks)*16:]...)
    }
   } else {
                //按大数据块整个加密
    file_decodeData = append(file_decodeData, fileData[:offset]...)

    if (int64(offset) + len_block) > fileSize {
     file_decodeData = append(file_decodeData, fileData[offset:]...)
    } else {
     output, _ := common.DecryptAES(fileData[int64(offset):int64(offset)+len_block], aeskey, aes_iv)
     file_decodeData = append(file_decodeData, output...)

     file_decodeData = append(file_decodeData, fileData[int64(offset)+len_block:]...)
    }
   }
   common.Writefile(strings.Split(onefile, ".bianlian")[0], string(file_decodeData))
   os.Rename(onefile, strings.Split(onefile, ".bianlian")[0]+".bak")
  } else {
   common.Writefile(strings.Split(onefile, ".bianlian")[0], string(fileData))
   os.Rename(onefile, strings.Split(onefile, ".bianlian")[0]+".bak")
  }
 }
}

func getAeskey() (aeskey []byte, aes_iv []byte, offset int) {
 currentUser, _ := user.Current()
 desktopDir := filepath.Join(currentUser.HomeDir, "Desktop")
 desktopini := filepath.Join(desktopDir, "desktop.ini.bianlian")

 aeskeys := []string{"633A56D0388869237C8DA6B7FC09F55DF35408C332614F692D11222583B36B62", "26DD1B400AED80B0980B34BAD76D3BE6599123FA562266B421A14AC8E02ECFA3",
  "979412FF08F7D2F0BD5F7E7E2A4919E9BF68CC7AABAB499872EC822DDCDA5307", "4A4105960D2C127D9711AC851BC1F10D17471B5A184CCDADA79003DA82CFDBA2",
  "27CFAE34A83C9A7E48060E18A68A233914271DB7D414C838FB1EAEAEA89E5CDE"}
 aesivs := []string{"FC55A60A25B0472CFC2C6EBC3DD89DAB", "D518BA928469306D579D625F56D09883", "0FC14323F7A13CCA0569EBCFAE283996",
  "5560E19D4B425420F6F5EF387D97065A", "223B67AC534F9938CC7B1F9777A95840"}
 offsets := []int{0x9, 0x1, 0x34, 0x41, 0x3d}

 for ii := 0; ii < 5; ii++ {

  fileSize, _ := common.GetFileSize(desktopini)
  len_block := common.Calc_block(fileSize)
  fileData, _ := ioutil.ReadFile(desktopini)

  file_decodeData := []byte{}
  aeskey, _ = hex.DecodeString(aeskeys[ii])
  aes_iv, _ = hex.DecodeString(aesivs[ii])
  offset = offsets[ii]

  if common.GetBlocksAmount(fileSize, len_block, int64(offset)) > 0 {
   if fileSize < 0x1000 {
    if (int64(offset) + len_block) > fileSize {
     file_decodeData = append(file_decodeData, fileData...)
    } else {
     file_decodeData = append(file_decodeData, fileData[:offset]...)

     blocks := (fileSize - int64(offset)) / 16

     for i := 0; i < int(blocks); i++ {
      output, _ := common.DecryptAES(fileData[offset+16*i:offset+16*(i+1)], aeskey, aes_iv)
      file_decodeData = append(file_decodeData, output...)
     }
     file_decodeData = append(file_decodeData, fileData[offset+int(blocks)*16:]...)
    }
   } else {
    file_decodeData = append(file_decodeData, fileData[:offset]...)

    if (int64(offset) + len_block) > fileSize {
     file_decodeData = append(file_decodeData, fileData[offset:]...)
    } else {
     output, _ := common.DecryptAES(fileData[int64(offset):int64(offset)+len_block], aeskey, aes_iv)
     file_decodeData = append(file_decodeData, output...)

     file_decodeData = append(file_decodeData, fileData[int64(offset)+len_block:]...)
    }
   }
   //system32
   if bytes.Contains(file_decodeData, []byte{0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6D, 0x00, 0x33, 0x00, 0x32, 0x00}) {
    return
   }
  } else {
   fmt.Println(ii, "由于desktop.ini文件未加密,因此暂无法提取AES密钥")
   os.Exit(1)
  }
 }
 return nil, nil, 0
}
  • common.go

package common

import (
 "bufio"
 "bytes"
 "crypto/aes"
 "crypto/cipher"
 "encoding/binary"
 "fmt"
 "io"
 "math/big"
 "os"
)

func DecryptAES(ciphertext, key, iv []byte) ([]byte, error) {
 block, err := aes.NewCipher(key)
 if err != nil {
  return nil, err
 }

 if len(ciphertext) < aes.BlockSize {
  return nil, fmt.Errorf("ciphertext too short")
 }

 if len(ciphertext)%aes.BlockSize != 0 {
  return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
 }

 mode := cipher.NewCBCDecrypter(block, iv)
 mode.CryptBlocks(ciphertext, ciphertext)

 return ciphertext, nil
}

func GetBlocksAmount(a1 int64, a2 int64, a3 int64) int64 {
 var v4 int64
 if a2 == -1 {
  v4 = -a1
 } else {
  v4 = a1 / a2
 }

 v5 := a1 - a3
 v6 := ((v5 >> 63) >> 54) + v5
 v7 := v5 / 1024
 if v7 <= 1024 {
  return v4
 }
 v8 := ((v6 >> 63) >> 54) + v7
 v9 := float64(a2) * 0.0009765625 * 0.0009765625
 v10 := (0.2 * float64(v8>>10) / v9)
 var v11 int64
 if v8>>10 >= 1024 {
  v11 = (((v8 >> 63) >> 54) + (v8 >> 10)) >> 10
  var v12 float64
  if (v11 - 101) >= 0x18F {
   if v11 <= 500 {
    v12 = 0.001
   } else {
    v12 = 0.00005
   }
  } else {
   v12 = 0.00015
  }
  return int64(v12) * v11 / int64(0.0009765625*v9)
 }
 return int64(v10)
}

func Calc_block(fileSize int64) (v27 int64) {
 v27 = (fileSize / 0x1000) << 0xc
 if fileSize > 0x400000 {
  num1 := big.NewInt(fileSize)

  hex_2 := "CCCCCCCCCCCCCCCD"
  num2 := new(big.Int)
  num2.SetString(hex_2, 16)

  result := new(big.Int).Mul(num1, num2)

  bytes := result.Bytes()

  tmp1, _ := BytesToInt_mode(bytes[:len(bytes)-8])
  tmp2, _ := BytesToInt_mode(bytes[:len(bytes)-8])

  tmp1 = tmp1 >> 3
  tmp2 = tmp2 >> 0x3f >> 0x34

  tmp3 := tmp1 + tmp2
  v27 = int64(tmp3 >> 0xc << 0xc)
  if tmp3 >= 0x400000 {
   v27 = 0x400000
  }
 }
 if v27 < 16 {
  v27 = 16
 }
 return
}

func BytesToInt_mode(b []byte) (int, error) {
 if len(b) == 3 {
  b = append([]byte{0}, b...)
 }
 bytesBuffer := bytes.NewBuffer(b)
 switch len(b) {
 case 1:
  var tmp int8
  err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
  return int(tmp), err
 case 2:
  var tmp int16
  err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
  return int(tmp), err
 case 4:
  var tmp int32
  err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
  return int(tmp), err
 default:
  return 0, fmt.Errorf("%s", "BytesToInt bytes lenth is invaild!")
 }
}

func checkPathIsExist(filename string) bool {
 var exist = true
 if _, err := os.Stat(filename); os.IsNotExist(err) {
  exist = false
 }
 return exist
}

func Writefile(filename string, buffer string) {
 var f *os.File
 var err1 error

 if checkPathIsExist(filename) { //如果文件存在
  f, err1 = os.OpenFile(filename, os.O_CREATE, 0666) //打开文件
  //fmt.Println(filename, "文件存在,更新文件")
 } else {
  f, err1 = os.Create(filename) //创建文件
  //logger.Logger.Info("文件不存在")
 }
 //将文件写进去
 _, err1 = io.WriteString(f, buffer)
 if err1 != nil {
  fmt.Println("写文件失败", err1)
  return
 }
 _ = f.Close()
}

func FileToSlice(file string) []string {
 fil, _ := os.Open(file)
 defer fil.Close()
 var lines []string
 scanner := bufio.NewScanner(fil)
 for scanner.Scan() {
  lines = append(lines, scanner.Text())
 }
 return lines
}

func GetFileSize(filePath string) (int64, error) {
 // 打开文件
 file, err := os.Open(filePath)
 if err != nil {
  return 0, fmt.Errorf("error opening file: %v", err)
 }
 defer file.Close()

 // 获取文件信息
 fileInfo, err := file.Stat()
 if err != nil {
  return 0, fmt.Errorf("error getting file info: %v", err)
 }

 // 获取文件大小
 fileSize := fileInfo.Size()

 return fileSize, nil
}

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

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

相关文章

裸土检测算法实际应用、裸土覆盖检测算法、裸土检测算法

裸土检测算法主要用于环境保护、农业管理、城市规划和土地管理等领域&#xff0c;通过图像识别技术来检测和识别地表上的裸露土壤。这种技术可以帮助管理者实时监控裸土面积&#xff0c;及时采取措施&#xff0c;防止水土流失、环境污染和生态退化。 一、技术实现 裸土检测算…

【第二轮通知】第二届网络、通信与智能计算国际会议(NCIC 2024)

NCIC 2024|第二届网络、通信与智能计算国际会议 2024年11月22日-25日 中国 | 北京 www.icncic.org 重要日期 二轮截稿时间&#xff1a;2024年10月15日 注册截止时间&#xff1a;2024年11月10日 会议日期&#xff1a;2024年11月22日-25日 第二届网络、通信与智能计算国际会…

ProgrammerAI—AI辅助编程学习指南

前言 随着AIGC&#xff08;AI生成内容&#xff09;技术的快速发展&#xff0c;诸如ChatGPT、MidJourney和Claude等大语言模型相继涌现&#xff0c;AI辅助编程工具正逐步改变程序员的工作方式。这些工具不仅可以加速代码编写、调试和优化过程&#xff0c;还能帮助解决复杂的编程…

【深度学习】注意力机制与自注意力机制详解

深度学习中的注意力机制/自注意力机制详解 1. 注意力机制的通俗理解2. 注意力和自注意力机制的区别3. 自注意力机制原理与计算流程3.1 引入自注意力机制的目的与思想3.2 从向量角度理解 [R1]3.3 从Self-Attention核心公式理解 [R3] 4. 多头自注意力机制&#xff08;Multi-head …

网络威慑战略带来的影响

文章目录 前言一、网络威慑的出现1、人工智能带来的机遇二、网络空间的威慑困境1、威慑概念的提出2、网络威慑的限度3、人类对网络威胁的认知变化4、网络空间的脆弱性总结前言 网络威慑是国家为应对网络空间风险和威胁而采取的战略。冷战时期核威慑路径难以有效复制至网络空间…

HT6881 4.7W防削顶单声道音频功率放大器

特点 防削顶失真功能(Anti-Clipping Function,ACF) AB类/D类切换 优异的全带宽EMI抑制性能 免滤波器数字调制&#xff0c;直接驱动扬声器 输出功率 1.4W (VDD3.6V, RL4Ω, THDN10%, Class D) 2.8W (VDD5.0V, RL4Ω, THDN10%, ClassD) 4.7W(VDD6.5V,RL40,THDN10%, ClassD) 2.5W …

【环境踩坑系列】centos7安装python3.10.X

前言 虽然centOS8已经发布了相当一段时间了&#xff0c;但是基于稳定性、成熟的社区等原因&#xff0c;大家在选择centOS作为服务器操作系统的时候仍然会选择centOS7作为首选。但是centOS7自带的是python2.7.5&#xff0c;当前大量的python程序要用到的又是python3&#xff0c…

网络原理之IP协议(网络层)

目录 前言 什么是IP协议&#xff1f; IP协议的协议头格式 16位总长度&#xff08;字节数&#xff09; 16位标识、3位标志位和13位片偏移 8位生存时间 IP地址管理 1.动态分配IP 2.NAT机制&#xff08;网络地址转换&#xff09; NAT机制是如何工作的 NAT机制的优缺点…

监控易监测对象及指标之:全面监控Oracle ODBC数据库

在数字化时代&#xff0c;数据库作为存储和管理企业核心数据的基石&#xff0c;其稳定性和性能直接关系到业务的连续性和效率。Oracle数据库以其强大的功能和稳定性&#xff0c;广泛应用于各行各业。为了确保Oracle数据库的稳定运行和高效性能&#xff0c;对其进行全面监控显得…

阴影的基本原理

1、现实中阴影的产生规则 如图所示&#xff0c;现实中的阴影产生规则是&#xff0c;在不考虑光线反射的前提下&#xff0c;当一个光源发射的一条光线遇到一个不透明物体A时&#xff0c;这条光线就不能够再继续照亮其他物体了&#xff08;物体B的一部分&#xff09;&#xff0c…

ChatGPT 向更多用户推出高级语音模式:支持 50 种语言;字节发布两款新视频生成大模型丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

AutosarMCAL开发——基于EB MCU驱动

这里写目录标题 1.MCU模块的作用2.EB配置以及接口应用3.总结 1.MCU模块的作用 MCU模块主要分为三部分&#xff1a; McuGeneralConfiguration MCU通用配置&#xff08;一般保持默认&#xff09;McuHardwareResourceAllocationConf 硬件资源分配管理器&#xff08;用于连接不同…

TEDxDUTH 使用 NocoBase 实现革新

作者&#xff1a;TEDxDUTH TEDxDUTH 是由德莫克里特大学的志愿者们组成的一个充满活力的团队。作为 TEDx 全球社区的一员&#xff0c;我们的使命简单而有力&#xff1a;传播能够激励和引发改变的思想。我们通过精心策划一系列活动&#xff0c;成功汇聚了众多思想家、创新家以及…

Module did not self-register: ‘drivelist.node‘报错解决

报错如下&#xff1a; node_modules/bindings/bindings.js:121throw e;^Error: Module did not self-register: xxxx/node_modules/drivelist/build/Release/drivelist.node.at process.func [as dlopen] (electron/js2c/asar.js:140:31)at Object.Module._extensions..node (…

探索 Python 中的 AI 魔法:markdownify 库的奥秘

文章目录 探索 Python 中的 AI 魔法&#xff1a;markdownify 库的奥秘背景&#xff1a;为何选择 markdownify&#xff1f;库简介&#xff1a;markdownify 是什么&#xff1f;安装指南&#xff1a;如何安装 markdownify&#xff1f;函数用法&#xff1a;markdownify 的五个简单函…

【Qwen2-VL】通义多模态新作速读

Qwen2-VL https://github.com/QwenLM/Qwen2-VL 结构&#xff1a; 整体&#xff1a;6.75 亿个参数的 Vision Transformer &#xff08;ViT&#xff09; &#xff08;Dosovitskiy et al.&#xff0c; 2021&#xff09; Qwen2 预处理阶段&#xff1a; 朴素动态分辨率支持&…

工程车辆目标检测、工程车检测算法、工程车辆类型检测算法

工程车检测算法主要用于智能交通系统、建筑工地管理、矿山开采、物流运输等领域&#xff0c;通过图像识别技术来检测和识别工程车&#xff0c;以提高安全管理、交通流量管理和资源调度的效率。以下是关于工程车检测算法的技术实现、应用场景及优势的详细介绍。 一、技术实现 工…

VRP-SAM

不建议复现

uni-app - - - - - 小程序获取宿主语言

const systemInfo uni.getSystemInfoSync(); console.log(systemInfo);uni.showModal({title: 宿主语言,content: systemInfo.hostLanguage })官网地址&#xff1a;【uni-app getSystemInfoSync】

西圣Mike Pro无线麦克风强势上线!百元级实力口碑与销量双冠王!

随着音频技术的不断革新与飞跃&#xff0c;西圣XISEM再次以卓越的创新驱动力推出全新力作&#xff0c;近日&#xff0c;西圣品牌震撼发布全新专业无线麦克风——西圣Mike Pro。用行业领先的硬件配置&#xff0c;百元价格打造千元专业级麦克风专业体验&#xff0c;音质远超同价位…