使用 GZCTF 结合 GitHub 仓库搭建独立容器与动态 Flag 的 CTF 靶场+基于 Docker 的 Web 出题与部署+容器权限控制

news2024/11/13 18:33:59

写在前面

关于 CTF 靶场的搭建(使用 CTFd 或者 H1ve)以及 AWD 攻防平台的搭建,勇师傅在前面博客已经详细写过,可以参考我的《网站搭建》专栏,前段时间玩那个 BaseCTF,发现它的界面看着挺不错的,了解到也是一个开源项目-GZCTF,网上未见有这方面的详细介绍,看了下网上都只是简单说了下怎么搭出 GZCTF 这个靶场的界面,然而对于后续题目的部署并未进行详细的介绍与说明,因此这里分享下自己的部署经验以及可能遇到的问题、注意点与解决方案。

文章开始前给大家分享一个人工智能学习网站,通俗易懂,风趣幽默

人工智能教程icon-default.png?t=N7T8https://www.captainbed.cn/myon/

目录

一、靶场基础搭建

二、靶场基础配置与功能使用

三、Web 题目示例说明

四、Web 题目制作与部署

五、docker 镜像制作、上传、拉取

补充完善《容器权限控制》


一、靶场基础搭建

首先我们肯定得把基础的框架搭好,也就是靶场的界面,之前的一般都是直接 git 克隆 github 的项目,这个的话我们是需要先准备两个文件。

参照官方手册:快速上手 - GZ::CTF Docs (gzti.me)

1、文件1:appsettings.json

内容:

{
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "Database": "Host=db:5432;Database=gzctf;Username=postgres;Password=<Your POSTGRES_PASSWORD>"
  },
  "EmailConfig": {
    "SendMailAddress": "a@a.com",
    "UserName": "",
    "Password": "",
    "Smtp": {
      "Host": "localhost",
      "Port": 587
    }
  },
  "XorKey": "<Your XOR_KEY>",
  "ContainerProvider": {
    "Type": "Docker", // or "Kubernetes"
    "PortMappingType": "Default", // or "PlatformProxy"
    "EnableTrafficCapture": false,
    "PublicEntry": "<Your PUBLIC_ENTRY>", // or "xxx.xxx.xxx.xxx"
    // optional
    "DockerConfig": {
      "SwarmMode": false,
      "Uri": "unix:///var/run/docker.sock"
    }
  },
  "RequestLogging": false,
  "DisableRateLimit": true,
  "RegistryConfig": {
    "UserName": "",
    "Password": "",
    "ServerAddress": ""
  },
  "CaptchaConfig": {
    "Provider": "None", // or "CloudflareTurnstile" or "GoogleRecaptcha"
    "SiteKey": "<Your SITE_KEY>",
    "SecretKey": "<Your SECRET_KEY>",
    // optional
    "GoogleRecaptcha": {
      "VerifyAPIAddress": "https://www.recaptcha.net/recaptcha/api/siteverify",
      "RecaptchaThreshold": "0.5"
    }
  },
  "ForwardedOptions": {
    "ForwardedHeaders": 5,
    "ForwardLimit": 1,
    "TrustedNetworks": ["192.168.12.0/8"]
  }
}

该文件中必须修改的参数:

(1)数据库密码

Password=<Your POSTGRES_PASSWORD>"

(2)用于加密比赛私钥的随机字符串(随便输入一串字符就行了)

"XorKey": "<Your XOR_KEY>"

(3)外部访问地址,可以是 IP 或域名,用于提供给选手访问题目容器的地址

"PublicEntry": "<Your PUBLIC_ENTRY>"

2、文件2:compose.yml

内容:

services:
  gzctf:
    image: registry.cn-shanghai.aliyuncs.com/gztime/gzctf:develop
    restart: always
    environment:
      - "GZCTF_ADMIN_PASSWORD=<Your GZCTF_ADMIN_PASSWORD>"
      # choose your backend language `en_US` / `zh_CN` / `ja_JP`
      - "LC_ALL=zh_CN.UTF-8"
    ports:
      - "80:8080"
    volumes:
      - "./data/files:/app/files"
      - "./appsettings.json:/app/appsettings.json:ro"
      # - "./kube-config.yaml:/app/kube-config.yaml:ro" # this is required for k8s deployment
      - "/var/run/docker.sock:/var/run/docker.sock" # this is required for docker deployment
    depends_on:
      - db
 
  db:
    image: postgres:alpine
    restart: always
    environment:
      - "POSTGRES_PASSWORD=<Your POSTGRES_PASSWORD>"
    volumes:
      - "./data/db:/var/lib/postgresql/data"

该文件中必须修改的参数:

(1)初始管理员密码

"GZCTF_ADMIN_PASSWORD=<Your GZCTF_ADMIN_PASSWORD>" 

特别注意:该密码需要满足要求的密码复杂度,至少应包括大小写字母加数字,如果配置文件中设置的密码太简单是无法成功创建管理员这个账户的,默认管理员登录用户名是 Admin。

(2)数据库密码

"POSTGRES_PASSWORD=<Your POSTGRES_PASSWORD>"

文件改好后,都放到某个目录下,对了你需要提前装好 docker 和 docker-compose 。

在该目录下执行如下命令拉取并启动容器:

docker-compose up -d

确保你的 80 端口未被占用,如果你之前开了中间件 nginx、Apache 这些可能就会报错端口被占用,需要停掉再拉取。

拉取后会多出一个叫 data 的文件夹:

访问你前面配置文件 appsettings.json 里填的 "PublicEntry" 那个地址:

没什么问题的话,就可以看到靶场的页面了。

至此,靶场的基本搭建完成。

二、靶场基础配置与功能使用

使用 Admin 管理员账号和 compose.yml 中设置的密码进行登录(必须满足密码的复杂度,否则会提示用户不存在或者密码错误,其实是数据库里压根就没创建这个用户)。

首先肯定是需要新建一个比赛 

里面可以修改的内容如下: 

下面我自己创建的用户或者叫朋友创建的用户进行测试 

都是图形界面化的操作,很容易理解,到处点点就熟悉了,这里不过多赘述。

三、Web 题目示例说明

新建一个题目

选择动态容器,填好一些基础的信息。

最主要的是:容器镜像 * 这里,下面给出一些可以用于测试的镜像:

vaalacat/push_f12

glzjin/hctf_2018_warmup

ctftraining/hbctf_2017_dameixian

ctftraining/qwb_2019_smarthacker

ctftraining/buuctf_2018_online_tool

ctftraining/qwb_2019_upload

ctftraining/qwb_2019_supersqli

比如我们使用 vaalacat/push_f12 进行测试:

填好镜像名后,点击创建测试容器。

创建成功后就可以访问题目环境了

题目容器的端口是 32791(随机生成的) 

此时在终端 docker ps 可以看到这个被新创建的容器:

将题目设置为可见的

我们再创建一个用户对这个题目进行测试,创建好后需要先报名参赛,如果没有开启免审核则还需要管理员账号通过下,然后就可以看到比赛题目了。

(这里似乎一个 ip 只能登录一个用户,因为我在 Admin 和 test 用户间切换会影响到另一个用户)

可以看到,新建容器的端口是 32792

终端会发现又多出了一个新建的容器,这些容器间是相互独立的,也就是说每个选手访问的地址不同,题目环境也是相互独立的,flag 也是不同的。 

四、Web 题目制作与部署

写这篇文章最重要的就是我们如何上 web 题目呢?如何生成不同容器的动态 flag?

ok,来到最重要的部分,说白了也就是我们题目该怎么制作?

下面我将带大家一起来制作 docker 镜像,以及将 docker 镜像上传到自己的镜像仓库,最后拉取镜像到自己的靶场。

先给大家看一下至少需要准备哪些文件,文件的位置:

废话少说,上文件:

(1)docker-compose.yml

相比之前的 docker-compose.yml 文件,我们不需要再单独暴露某个端口作为题目的访问端口,同时我们新增了镜像名称和标签信息。

version: "3"
services:
    web1:
       build:
         context: ./web1  # 指定 Dockerfile 所在的构建上下文目录
         dockerfile: Dockerfile  # 指定 Dockerfile 的名称
       image: myon6/testweb1:latest  # 为构建的镜像指定名称和标签,需要改成你自己 github 的名字
       restart: always

我不知道大家对于 github 仓库是否熟悉,我最开始测试的其实是 docker hub 仓库,但是在执行 push 命令的时候一直无法解决超时的问题,无论是开了加速器还是换了 docker 源还是开全局代理都没有解决,解决 pull 命令无法拉取倒是完全可以的,主要是 push 命令有问题,最后还是选择使用 github 的仓库。

首先我们来看看 github 上自己的仓库在哪儿

下面这个 myon6/testweb1 就是我制作好的 docker 镜像,然后 push 到了 github 上的仓库。

好,你大概知道 github 的仓库在哪儿了,我们继续说出题需要的其他文件。

(2)Dockerfile

# 指定基础镜像
FROM php:7.0-fpm-alpine

# 删除默认的 web 根目录中的所有内容
RUN rm -rf /var/www/html/*

# 将本地的 html 目录复制到容器中
COPY html /var/www/html

# 将初始化脚本复制到容器的 /etc 目录中
COPY init.sh /etc/init.sh

# 设置权限
RUN chown -R root:root /var/www/html && chmod -R 755 /var/www/html

# 设置初始化脚本为可执行
RUN chmod +x /etc/init.sh

# 暴露 web 服务器的端口
EXPOSE 80

# 使用初始化脚本来启动容器
ENTRYPOINT ["/etc/init.sh"]

这里其实应该做一个权限控制,因为直接给 root 还是有一定风险在里面,比如 docker 逃逸。

 (但是我在测试中给 www-data 权限会导致 flag 无法成功写入根目录,后面再继续研究)

(3)初始化脚本 init.sh

#!/bin/sh

# Write the value of GZCTF_FLAG environment variable to /flag
echo "$GZCTF_FLAG" > /flag
chmod 444 /flag

# Unset the GZCTF_FLAG environment variable
unset GZCTF_FLAG

# Start the PHP built-in web server
php -S 0.0.0.0:80 -t /var/www/html

这个脚本是最容易出问题,也是当时折腾我最久的,最好不要使用 Windows 相关的东西去编辑这个初始化脚本,否则可能会导致 init.sh 文件中包含 Windows 风格的换行符 ^M:

这些字符会导致 Unix 系统无法正确解释脚本文件的内容,那么你在创建容器的时候就会遇到容器一直处于 restarting 的状态,无法看到映射的端口,实际就是容器刚启动就崩溃掉了。

当然我们也可以在 Dockerfile 中使用工具来处理这个问题:

# 安装 dos2unix 工具以转换脚本格式
RUN apk add --no-cache dos2unix

# 使用 dos2unix 转换脚本格式
RUN dos2unix /etc/init.sh

只要你操作得当,就不会遇到这个容器崩溃的问题,就可以不用加上面的内容。

(4)html 文件夹

这个就是大家很熟悉的题目环境文件了,我这里只做测试,因此就放了一个 index.php。

五、docker 镜像制作、上传、拉取

准备好上面说的文件后,在 web 目录下,使用 docker-compose build 来构建镜像:

docker-compose build

构建成功 

使用 docker images 查看镜像:

可以看到 myon6/testweb1

但是这是我们本地的镜像,我们需要将镜像标记为 GitHub Container Registry 的格式:

使用 docker tag 命令,注意一定要修改成你自己 github 账户的名字

docker tag myon6/testweb1:latest ghcr.io/myon5/myon6/testweb1:latest

可以看到现在就有了一个叫 ghcr.io/myon5/myon6/testweb1 的镜像 

我们将这个镜像 push 到 github 仓库,首先你需要先登录 github 账号:

关于这个 key 在哪里获取,就是在 github 的扩展设置里生成你自己账户的 token 用于验证。

echo <your_key> | docker login ghcr.io -u <your_username> --password-stdin

注意生成的 token 权限需要允许上传 packages,勾上。

登录成后就可以上传镜像到自己的仓库了:注意替换成自己账号

docker push ghcr.io/myon5/myon6/testweb1:latest

为了给你们再演示一遍我是删掉了原来仓库的东西重新构建上传的 

默认是私有的,如果你要所有人都能直接拉取到,需要设为 public:

至此,我们成功将镜像从本地上传到了 github 公开镜像。

接下来我们就可以在靶场-题目管理-容器镜像里填上我们题目镜像的名字了,然后创建容器。

这个是我自己做的一个简单题目

直接截断就可以 RCE ,命令执行成功

可以看到根目录下已经成功创建了 flag

为什么端口不一样了,因为我前面测试的是 www-data 用户,发现 flag 无法成功写入根目录,下面是 root 权限:

这也是我前面说的对用户权限的控制其实我还没有调整好,一般我们给 nobody 是最安全的,或者给 www-data,但是我试了这两个都有点问题,权限给高倒不是怕它影响到其他选手,因为每个容器是独立的,无论它怎么玩,就算把 flag 删了都没事,但是就担心它从 docker 容器里逃逸到服务器本地,所以还需要继续调整吧。

下面是测试不同用户选手访问不同端口,读取到的 flag 都是不同的:

经测试,两个 flag 都可以正确提交。

关于 flag 的随机生成规则我们也是可以进行设置的:

至此,我们实现了独立容器、动态 flag 的 web 题目制作与部署。

关于 GZCTF 那些界面的操作、功能点、设置等都是图形化的,我也不做过多介绍,大家到处点点就熟悉了。关于那种直接放附件的题,静态 flag 的,比如杂项、逆向这些和 CTFd、H1ve 方法一样,那么对于 web 题目的部署,我上面讲的是自己部署的方法以及注意事项,其实也就是新增了一个初始化 flag 的脚本,对于 pwn 题我目前还没试过,看了下官方给的示例是结合 xinetd 来部署的,应该和 web 题类似,大家自行摸索,如果不知道 xinetd 是什么,那么还是建议你先去看一下我前面介绍使用 xinted 部署 pwn 题的文章,也在专栏 《网站搭建》 里面。

补充完善《容器权限控制》

在前面的基础上,勇师傅又对 dockerfile 和 初始化脚本进行了调整。

附上修改后的文件:

Dockerfile

# 指定基础镜像
FROM php:7.0-fpm-alpine

# 删除默认的 web 根目录中的所有内容
RUN rm -rf /var/www/html/*

# 创建空的 flag 文件并设置权限
RUN touch /flag && chmod 666 /flag

# 将本地的 html 目录复制到容器中
COPY html /var/www/html

# 将初始化脚本复制到容器的 html 目录中
COPY init.sh /var/www/html/init.sh

# 设置权限(此时仍为 root 用户)
RUN chown -R www-data:www-data /var/www/html && chmod -R 755 /var/www/html

# 设置初始化脚本为可执行
RUN chmod +x /var/www/html/init.sh

# 暴露 web 服务器的端口
EXPOSE 80

# 以 root 用户启动脚本
ENTRYPOINT ["/var/www/html/init.sh"]

# 切换到 www-data 用户执行后续命令
USER www-data

init.sh

#!/bin/sh

# 将环境变量 GZCTF_FLAG 的值写入 /flag 文件
echo "$GZCTF_FLAG" > /flag

# 清除 GZCTF_FLAG 环境变量
unset GZCTF_FLAG

# 启动 PHP 服务器并替换当前 shell 进程
exec php -S 0.0.0.0:80 -t /var/www/html &

# 等待 PHP 服务器启动
sleep 5

# 删除 init.sh 脚本
rm -f "$0"

# 保持脚本运行
wait

简单说一下改进的优缺点:

(1)最终容器的用户是 www-data,而非 root,更安全。

(2)容器成功启动后,删掉初始化脚本,避免敏感信息泄露。

这里没有选择将 init.sh 放到 etc 目录,而是直接放在 html 目录,确保 www-data 用户可以成功删除掉,可以看到容器启动后已经不存在初始化脚本这个文件了。

(3)flag 文件可以被更改

我们在 Dockerfile 中 chmod 666 /flag 目的是为了后续让 www-data 用户可以成功执行 init.sh,写入环境变量到根目录下的 flag,但是这也会导致 flag 这个文件可以被 www-data 用户更改,因为我们使用 www-data 用户后就没有权限再执行 chmod 444 到根目录下的 flag。不过影响不大,因为每个选手分配的都是独立容器,就算更改也是改的自己的 flag,不会影响到其他用户的容器,如果确实把正确的 flag 改了,重启容器即可获取到新的 flag。

总的来说,相对于前面使用 root 用户,改成 www-data 用户还是会更好一些的,后面如果有新的改进和完善再与大家分享。

创作不易,期待大家的关注与支持!

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

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

相关文章

LVGL 控件之复选框(lv_checkbox)和下拉列表(lv_dropdown)

目录 一、复选框1、组成2、设置复选框文本3、复选框部件的状态4、复选框事件5、API 函数 二、下拉列表1、组成2、选项2.1 添加选项2.2 获取当前选中的选项 3、设置3.1 设置列表展开方向3.2 设置下拉列表图标3.3 设置列表常显文本 4、事件5、API 函数 一、复选框 1、组成 复选…

Android studio 导出 release 版本的 .aar 文件

不同的android studio 版本可能会有不同的方案&#xff0c;我针对的是&#xff1a; 首先打开settings: Setting —> Experimental 界面 将选项&#xff1a;【configure all gradle tasks】勾上&#xff1a; 接着点击 File —> Sync Project with Gradle Files 然后&…

【js逆向专题】8.webpack打包

本教程仅供学习交流使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;请各学员自觉遵守相关法律法规。小节目标: 熟悉 webpack打包原理熟悉 webpack打包方式了解 webpack多模块打包 一. webpack打包 概念: webpack 是…

【颤抖不再怕,帕金森患者的活力锻炼秘籍!】

Hey小伙伴们~&#x1f44b; 今天我们来聊聊一个温暖而重要的话题——如何帮助我们的亲人或自己&#xff0c;在帕金森病的挑战下&#xff0c;依然保持生活的活力与光彩&#xff01;&#x1f308; 帕金森病&#xff0c;这个名字听起来或许让人心生畏惧&#xff0c;但它绝不是生活…

地产行业如何利用Java实现精准营销

在当今竞争激烈的地产市场中&#xff0c;如何有效触达潜在客户并促进销售转化&#xff0c;成为众多房企关注的焦点。106短信平台作为一种精准的营销工具&#xff0c;在地产行业中发挥着越来越重要的作用。 支持免费对接试用&#xff1a;乐讯通PaaS平台 找好用的短信平台,选择乐…

AUTO TECH 2025 华南展 第十二届广州国际汽车零部件加工技术及汽车模具展览会——探索未来出行的创新动力

AUTO TECH 2025 华南展 第十二届广州国际汽车零部件加工技术及汽车模具展览会——探索未来出行的创新动力 随着全球汽车工业的不断进步和新能源汽车技术的迅猛发展&#xff0c;2025年11月20-22日在广州保利世贸博览馆将迎来一场行业瞩目的盛会——2025 第十二届广州国际汽车零部…

外接串口板,通过串口打开adb模式

一、依赖库 import subprocess import serial from serial.tools import list_ports import logging import time 二、代码 import subprocessimport serial from serial.tools import list_ports import logging import timedef openAdb(com):# com []# for i in list_por…

无人机之地面站篇

无人机的地面站&#xff0c;又称无人机控制站&#xff0c;是整个无人机系统的重要组成部分&#xff0c;扮演着作战指挥中心的角色。以下是对无人机地面站的详细阐述&#xff1a; 一、定义与功能 无人机地面站是指具有对无人机飞行平台和任务载荷进行监控和操纵能力的一组设备&…

[数据集][目标检测]翻越栏杆行为检测数据集VOC+YOLO格式512张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;512 标注数量(xml文件个数)&#xff1a;512 标注数量(txt文件个数)&#xff1a;512 标注类别…

通过卷积神经网络(CNN)识别和预测手写数字

一&#xff1a;卷积神经网络&#xff08;CNN&#xff09;和手写数字识别MNIST数据集的介绍 卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;是一种深度学习模型&#xff0c;它在图像和视频识别、分类和分割任务中表现出色。CNN通过模仿…

快排的深入学习

目录 交换类排序 一、冒泡排序 1. 算法介绍 2.算法流程 3. 算法性能分析 &#xff08;1&#xff09;时间复杂度分析 &#xff08;2&#xff09; 空间复杂度分析 冒泡排序的特性总结&#xff1a; 二、快速排序 1.算法介绍 2. 执行流程 1). hoare版本 2). 挖坑法 3)…

5.9灰度直方图

目录 实验原理 实验代码 运行结果 实验原理 calcHist 函数通常是指在计算机视觉和图像处理中用于计算图像直方图的一个函数。 cv:calcHist () 用于计算一个或多个数组的直方图。它可以处理图像数据并返回一个表示像素强度分布的向量&#xff08;对于灰度图像&#xff09;或…

Java:集合的相关汇总介绍

主要包含Set(集&#xff09;、 List(列表包含 Queue&#xff09;和 Map(映射)。 1、Collection&#xff1a; Collection 是集合 List、 Set、 Queue 的最基本的接口。 2、Iterator&#xff1a;迭代器&#xff0c;可以通过迭代器遍历集合中的数据。 3、Map&#xff1a;是映射表的…

VTK随笔十三:QT与VTK的交互

一、基于 Ot的 VTK 应用程序 以 VTK 读入一幅 JPG 图像&#xff0c;然后在 Qt 界面上使用 VTK 显示该图像为例&#xff0c;演示QT与VTK的交互。 1、创建QT项目QT_VTK_Demo 2、配置VTK库 在CMakeLists.txt中添加如下代码&#xff1a; 配置完成后重新打开工程加载VTK库。 3、编…

制裁下的转型:俄罗斯加密货币战略布局与人民币挂钩BRICS稳定币的崛起

在国际制裁重压下&#xff0c;俄罗斯正在积极推进加密货币政策改革&#xff0c;通过设立加密货币交易所和推动与人民币挂钩的BRICS稳定币&#xff0c;试图在全球金融体系中谋求新的生存与发展路径。这一系列举措标志着俄罗斯在数字经济领域的重大转向&#xff0c;既是对当前经济…

Linux【5】远程管理

目录 shutdown关机 ifconfig输出网卡信息 ping ip地址——检测连接正常 ssh 【-p port】 userip scp不同主机之间的文件copy 当前文件复制到远程 远程文件复制到本地 复制文件夹 -r shutdown关机 shutdown -r 重启 ifconfig输出网卡信息 ping ip地址——检测连接正常…

集成电路学习:什么是PCB印刷电路板

一、PCB&#xff1a;印刷电路板 PCB&#xff0c;全称为Printed Circuit Board&#xff0c;即印刷电路板&#xff0c;是现代电子设备中不可或缺的基础构件。它作为电子元器件的载体和连接体&#xff0c;在电子设备中发挥着至关重要的作用。以下是对PCB的详细解析&#xff1a; 二…

【C++初阶】一、C++入门(万字总结)

「前言」 「专栏」C详细版专栏 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 目录 一、关于C 1.1 什么是C 1.2 C 发展史 二、C关键字(C…

5.8幂律变换

目录 示例代码1 运行结果1 示例代码2 运行结果2 补充示例原理 示例&#xff1a;使用cv::pow进行图像处理 代码 运行结果 ​编辑 补充 实验代码3 运行结果3​编辑 在OpenCV中&#xff0c;幂律变换&#xff08;Power Law Transformations&#xff09;是一种常用的图像…

集成电路学习:什么是MOSFET(MOS管)

一、MOSFET&#xff1a;MOS管 MOSFET&#xff0c;全称Metal-Oxide-Semiconductor Field-Effect Transistor&#xff0c;即金属-氧化物半导体场效应晶体管&#xff0c;也常被称为MOS管或金氧半场效晶体管。它是一种可以广泛使用在模拟电路与数字电路的场效应晶体管&#xff08;f…