[漏洞分析] CVE-2024-6387 OpenSSH核弹核的并不是很弹

news2024/10/6 8:27:32

文章目录

    • 漏洞简介
    • 漏洞原理
      • 补丁分析
      • 漏洞原理
    • 漏洞利用
      • 漏洞利用1: SSH-2.0-OpenSSH_3.4p1 Debian 1:3.4p1-1.woody.3 (Debian 3.0r6, from 2005) [无ASLR无NX]
        • 漏洞利用原理
        • 漏洞利用关键点
      • 漏洞利用2: SSH-2.0-OpenSSH_4.2p1 Debian-7ubuntu3 (Ubuntu 6.06.1, from 2006) [无ASLR无NX]
      • 漏洞利用3: SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 (Debian 12.5.0, from 2024) [有ASLR无NX 32位]
    • 盖棺定论
    • poc分析

先说结论,在当前已经披露的漏洞利用方法而言,利用非常困难,也不能说完全利用不了吧,只能在特定场景成功,作者给了三个案例。但总的来说,核的并不是很弹,省流直接看下面盖棺定论

漏洞简介

漏洞编号: CVE-2024-6387

漏洞产品: OpenSSH - sshd

影响范围: 8.5p1 <= OpenSSH < 9.8p1

利用条件: 见最后"盖棺定论"章节

利用效果: 远程代码执行(RCE)(理论环境,单纯以博客中展示的漏洞利用方法而言,现网中几乎不可能完成利用

漏洞作者的博客如下:

https://www.qualys.com/2024/07/01/cve-2024-6387/regresshion.txt

由于作者没公开利用代码,并且利用实在太麻烦,本文纯纯基于该漏洞作者博客中的描述进行理论分析,不涉及实际调试。所有结论均为我YY的,不作为处置建议

漏洞原理

补丁分析

漏洞是在commit 752250c中引入的,关键点在于,该修改重命名了sigdie函数,并且去掉了#ifdef DO_LOG_SAFE_IN_SIGHAND

在这里插入图片描述

在这里插入图片描述

根据该宏定义的名字不难看出,该宏定义的意思是,在信号安全需求场景,不会执行中间的log部分代码,而是直接exit。现在去掉了该宏,则无条件执行这段log的代码。

漏洞原理

我们先来看看sshsigdie会在哪里使用:

log.h:

#define sigdie(...)		sshsigdie(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__)

这里将sshsigdie 定义为sigdie。

sshd.c:

/*
 * Signal handler for the alarm after the login grace period has expired.
 */
/*ARGSUSED*/
static void
grace_alarm_handler(int sig)
{
	if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0)
		kill(pmonitor->m_pid, SIGALRM);

	/*
	 * Try to kill any processes that we have spawned, E.g. authorized
	 * keys command helpers.
	 */
	if (getpgid(0) == getpid()) {
		ssh_signal(SIGTERM, SIG_IGN);
		kill(0, SIGTERM);
	}

	/* XXX pre-format ipaddr/port so we don't need to access active_state */
	/* Log error and exit. */
	sigdie("Timeout before authentication for %s port %d",
	    ssh_remote_ipaddr(the_active_state),
	    ssh_remote_port(the_active_state));
}

在grace_alarm_handler 函数中调用了sigdie打印log,看名字就知道grace_alarm_handler 是一个时钟信号的处理函数。在main函数中注册为SIGALRM信号的处理函数:

sshd.c:

    /*
	 * We don't want to listen forever unless the other side
	 * successfully authenticates itself.  So we set up an alarm which is
	 * cleared after successful authentication.  A limit of zero
	 * indicates no limit. Note that we don't set the alarm in debugging
	 * mode; it is just annoying to have the server exit just when you
	 * are about to discover the bug.
	 */
	ssh_signal(SIGALRM, grace_alarm_handler);
	if (!debug_flag)
		alarm(options.login_grace_time);

在options.login_grace_time 秒之后如果没有登录完成,则抛出超时信号SIGALRM。该参数默认为120,可以通过-g参数设置:

在这里插入图片描述
在这里插入图片描述

接下来看一下sshsigdie 函数中的逻辑,为什么这个函数在信号不安全场景会出问题:

void
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);
}

调用了sshlogv:

void
sshlogv(const char *file, const char *func, int line, int showfunc,
    LogLevel level, const char *suffix, const char *fmt, va_list args)
{
	char tag[128], fmt2[MSGBUFSIZ + 128];
	int forced = 0;
	const char *cp;
	size_t i;

	snprintf(tag, sizeof(tag), "%.48s:%.48s():%d",
	    (cp = strrchr(file, '/')) == NULL ? file : cp + 1, func, line);
	for (i = 0; i < nlog_verbose; i++) {
		if (match_pattern_list(tag, log_verbose[i], 0) == 1) {
			forced = 1;
			break;
		}
	}

	if (log_handler == NULL && forced)
		snprintf(fmt2, sizeof(fmt2), "%s: %s", tag, fmt);
	else if (showfunc)
		snprintf(fmt2, sizeof(fmt2), "%s: %s", func, fmt);
	else
		strlcpy(fmt2, fmt, sizeof(fmt2));

	do_log(file, func, line, level, forced, suffix, fmt2, args);
}

最后调用了do_log:

static void
do_log(const char *file, const char *func, int line, LogLevel level,
    int force, const char *suffix, const char *fmt, va_list args)
{
    ··· ···
	if (log_handler != NULL) {
		··· ···
	} else {
#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);
		syslog_r(pri, &sdata, "%.500s", fmtbuf);
		closelog_r(&sdata);
#else
		openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility);
		syslog(pri, "%.500s", fmtbuf);
		closelog();
#endif
	}
	errno = saved_errno;
}

可以看到这个函数中调用了syslog(),而syslog是信号不安全的函数,这其中也会使用大量的如malloc、free等信号不安全的libc函数。所以漏洞的根因是:在信号处理函数中使用信号不安全函数。

漏洞利用

指的一提的是,该漏洞并不是第一次出现,实际上这个漏洞就是CVE-2006-5051 漏洞的再现,上面提到的被删除的关键代码#ifdef DO_LOG_SAFE_IN_SIGHAND正式为了修复CVE-2006-5051 增加的,但是在上述commit中被错误删除,导致CVE-2006-5051 又重现。但不得不说CVE-2006-5051 漏洞从来没有被利用成功过。

除此之外,不可忽略的博客中的关于他们所尝试成功的漏洞利用的前提是:

  • 漏洞利用只针对虚拟机,而不是裸机服务器,控制网络的数据包抖动在0~10ms 之间;

漏洞利用1: SSH-2.0-OpenSSH_3.4p1 Debian 1:3.4p1-1.woody.3 (Debian 3.0r6, from 2005) [无ASLR无NX]

漏洞利用原理

该漏洞利用的环境没有ASLR与NX。

这个漏洞利用基本就是上古版本的ubuntu,来自2005年,其实这个漏洞利用尝试主要是尝试利用CVE-2006-5051。而且很容易就成功了,我们可以通过这个漏洞来理解一下他是如何利用的这个漏洞,并且理解一下该漏洞想要利用成功需要满足的前提条件。

首先,20年前的代码和现在有很大不同,我自然也懒得去找20年前的代码了反正思路是相同的,这个漏洞的原理还是相当简单的,直接看博客即可,根据博客中的描述,20年前的漏洞中大概使用的信号不安全函数不是syslog,而是packet_close,并且packet_close中调用了free:

------------------------------------------------------------------------
 302 grace_alarm_handler(int sig)
 303 {
 ...
 307         packet_close();
------------------------------------------------------------------------
 329 packet_close(void)
 330 {
 ...
 341         buffer_free(&input);
 342         buffer_free(&output);
 343         buffer_free(&outgoing_packet);
 344         buffer_free(&incoming_packet);
------------------------------------------------------------------------
 35 buffer_free(Buffer *buffer)
 36 {
 37         memset(buffer->buf, 0, buffer->alloc);
 38         xfree(buffer->buf);
 39 }
------------------------------------------------------------------------
 51 xfree(void *ptr)
 52 {
 53         if (ptr == NULL)
 54                 fatal("xfree: NULL pointer given as argument");
 55         free(ptr);
 56 }
------------------------------------------------------------------------

free函数是一个经典的信号不安全函数。在上古版本的glibc中,gree流程有如下关键代码:

------------------------------------------------------------------------
1028 struct malloc_chunk   这一部分是堆块结构,跟现在一样
1029 {
1030   INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
1031   INTERNAL_SIZE_T size;      /* Size in bytes, including overhead. */
1032   struct malloc_chunk* fd;   /* double links -- used only if free. */
1033   struct malloc_chunk* bk;
1034 };
------------------------------------------------------------------------
2516 #define unlink(P, BK, FD)                                           \
2517 {                                                                   \
2518   BK = P->bk;                                                       \
2519   FD = P->fd;                                                       \
2520   FD->bk = BK;                                                      \
2521   BK->fd = FD;                                                      \
2522 }                                                                   \
------------------------------------------------------------------------
3160 chunk_free(arena *ar_ptr, mchunkptr p)
....
3164 {
3165   INTERNAL_SIZE_T hd = p->size; /* its head field */
....
3177   sz = hd & ~PREV_INUSE;
3178   next = chunk_at_offset(p, sz);
3179   nextsz = chunksize(next);
....
3230   if (!(inuse_bit_at_offset(next, nextsz)))  这个分支的意思是,下一个堆块如果是释放状态,则将其unlink出来,然后跟当前要释放的堆块合并,在重新加入链表
3231   {
....
3241       unlink(next, bck, fwd);
....
3244   }
3245   else
3246     set_head(next, nextsz);                  /* clear inuse bit */
....
3251     frontlink(ar_ptr, p, sz, idx, bck, fwd);
------------------------------------------------------------------------

我们需要做的大概是构造如下堆布局,关于glibc的堆结构,经常打CTF的同学应该都很熟悉,不在这里解释了,直接看漏洞利用:

-----|---+---------------|---+---------------|---+---------------|-----
 ... |p|s|f|b|  chunk_X  |p|s|f|b|  chunk_Y  |p|s|f|b|  chunk_Z  | ...
-----|---+---------------|---+---------------|---+---------------|-----
                             |<------------->|
                                 user data

p,s,f,b分别是prev_size,size,fd 和 bk 字段。构造三个连续的堆块,chunk_X, chunk_Y, chunk_Z,其中chunk_Y是我们内容可控的堆块。当一个堆块在被使用状态的时候,fd和bk字段是没用的,这连个字段的位置是用户数据,也就是我们可控的数据。那么我们需要让他在free chunk_Y的过程中触发SIGARLM信号中断,并且中断的位置必须在上述代码的3246到3251之间,这样已经执行完set_head(next, nextsz);也就是已经修改chunk_Z的prev_inused位,已经将chunk_Y标记为释放,但还没将其正式添加到free_list中,这时触发中断。

进入到中断逻辑中,中断逻辑中还有free函数,这时需要再中断逻辑中free chunk_X堆块,这样在3230行,检测下一个堆块是否释放的时候,它会发现chunk_Y是释放的状态, 但他并不值得chunk_Y并不在free_list中,而是继续执行unlink函数,而参与unlink函数的两个指针fd和bk此时还是我们可控的用户数据。直接经典的unlink任意地址写,由于在该漏洞利用中没有ASLR与NX,则直接修改free_hook 到堆上,并在堆上部署shellcode完成利用。

漏洞利用关键点

有一个关键点被我一笔带过了,那就是,我们需要在正好free chunk_Y的时候的free的3246到3251之间触发超时中断。我不知道如何评价这个动作有多难,我只是说达成原理大家自行判断:

超时中断是在我们发起ssh链接的时候开始计时,默认状态120秒后触发。如果我们想要在正好free 的时候触发,那么我们得在发起ssh之后精确的等待120秒多一点触发free的操作。

但事实也并不是这么苛刻,漏洞作者采用的方法是通过DSA 公钥解析逻辑来触发free,因为该逻辑中有四次free我们可控内容的堆块的机会,并且sshd允许尝试6次认证,总共24次free机会。只要任意一次完成了,就是成功。

但即便如此代码的执行是非常快的,单条指令的执行时间是微妙级别,即便我们有24次机会,但触发从120秒之前设置的信号,还是很难,就好像赌博默示录中伊藤开司挑战柏青哥机器时需要连续进入123层3个小孔一样。

漏洞作者给出的一些优化方案是:

  • 再一次测试中如果我们收到对我们的 DSA 公钥数据包的响应(SSH2_MSG_USERAUTH_FAILURE),则说明我们发送得太早了
  • 如果我们甚至无法发送我们的 DSA 数据包的最后一个字节,那么我们发的太晚了(sshd 已经收到了 SIGALRM 并关闭了我们的连接);
  • 如果我们可以发送我们的 DSA 数据包的最后一个字节,并且在 sshd 关闭我们的连接之前没有收到任何响应,则我们的时间安排是相当准确的。

这三条基本可以让你对自己等待时间是否过长或过短有个判断,但即便如此能打到的效果大概就是,我能将等待时间缩短到一个比较小的范围内,但众所周知,这个时间每次都是不一样的,毕竟你要发送网络报文过去,该漏洞利用的环境是虚拟机,每次网络报文的延迟还算稳定,但如果换成远程网络,我都不敢想。

在虚拟机环境中,作者说平均10000次可以成功,没600秒处理10个连接,需要一周才能成功一次。

漏洞利用2: SSH-2.0-OpenSSH_4.2p1 Debian-7ubuntu3 (Ubuntu 6.06.1, from 2006) [无ASLR无NX]

同样是上古版本,但和上面的利用不同的是,不再使用packet_close函数,而是使用的pam系列的函数,原理大同小异,找到一个对全局资源的关键访问函数,并且在其还没完成完整的修改之前中断,然后在中断处理中再次访问该全局资源。

除了漏洞利用的目标函数有区别外,在时间优化方面没有进一步优化,还是需要上述漏洞利用的苛刻条件。除此之外,还是需要非NX环境,还是需要shellcode。

漏洞利用3: SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 (Debian 12.5.0, from 2024) [有ASLR无NX 32位]

该漏洞利用环境开启了ASLR,但该环境的ASLR 有问题,随的并不是很机,参考:

https://zolutal.github.io/aslrnt/

https://grsecurity.net/toolchain_necromancy_past_mistakes_haunting_aslr

然后呢漏洞利用原理大差不差,关于时间,在该版本中时间更加难以确定,但作者依旧采用了一个很好的区间法能够尽量确认正确等待时间的范围:

------------------------------------------------------------------------
 88 userauth_pubkey(struct ssh *ssh, const char *method)
 89 {
...
138         if (pktype == KEY_UNSPEC) {
139                 /* this is perfectly legal */
140                 verbose_f("unsupported public key algorithm: %s", pkalg);
141                 goto done;
142         }
143         if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
144                 error_fr(r, "parse key");
145                 goto done;
146         }
...
151         if (key->type != pktype) {
152                 error_f("type mismatch for decoded key "
153                     "(received %d, expected %d)", key->type, pktype);
154                 goto done;
155         }
------------------------------------------------------------------------

通过发送错误的数据包频繁触发138-142的错误和151-155的错误可以确认正确的数据包处理时间大概是多久。根据作者描述,120秒接受100个连接的情况下(注意远大于前两次的频率)大概3-4小时能成功竞争时间窗,6-8小时能成功爆破地址,根据他提供的数据,可以看出ASLR的问题还是很大的,只有两种可能这随机了个寂寞,况且最后漏洞利用还是shellcode,还是得在没有NX的环境才可以漏洞利用成功

盖棺定论

整体来说,我觉得漏洞的实际威胁程度不大,首先该利用在2024年的ubuntu环境之所以能成功,有如下几个先决条件:

  1. ASLR开了,但约等于没开,那个ASLR漏洞让ASLR随机几乎完全失效,只有两种地址可能。
  2. 不能开NX,还是依赖shellcode
  3. 在虚拟机中,网络的波动在可控范围内
  4. 120秒发起100个连接
  5. 32位环境

这上面的条件还只是硬性条件,满足了之后才能开始时间窗的竞争尝试,可见有多难,在虚拟机场景下3-4小时才能竞争成功一次时间窗也就是获得一次猜地址的机会,但凡ASLR正常也是利用不成的,除非欧皇一发入魂。

更别说64位环境了。

关于这个竞争时间窗,多说一嘴,想在网络环境中发起请求之后120秒出发超时中断的时候,还正好在我们目标的被中断的代码之间的难度,我理解大概就是一列火车正在全速行驶,你站在铁轨旁边向火车扔石头,石头正好从车厢之间穿过的概率吧。

poc分析

尽早来发现github上有poc了,链接如下:

https://github.com/zgzhang/cve-2024-6387-poc

其实大概原理都知道,主要是验证一下上面分析的结论是否正确:

  1. ASLR确实只有两种可能:

    在这里插入图片描述

  2. 最后用shellcode完成利用,所以还需要关闭NX

    在这里插入图片描述

这么看来当前确实很难利用,期待有人发现更强的理由方式。

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

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

相关文章

Python模拟火焰文字效果:炫酷的火焰字动效

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义火焰效果类主循环 完整代码 引言 火焰文字效果是一种炫酷的视觉效果&#xff0c;常用于广告、游戏和艺术设计中。在这篇博客中&#xff0c;我们将使用Python创建一个火焰文字的动画效果。通过利用Py…

元素的宽度和高度未知,如何水平垂直居中,3个小办法。

在前端开发中&#xff0c;如果元素的宽度和高度未知&#xff0c;但需要将其水平和垂直居中&#xff0c;可以使用以下方法之一&#xff1a; 使用Flexbox布局&#xff1a; Flexbox是一种强大的布局模型&#xff0c;可以轻松实现元素的居中。可以通过以下CSS代码实现水平和垂直居…

# Sharding-JDBC从入门到精通(7)- Sharding-JDBC 公共表 与 读写分离

Sharding-JDBC从入门到精通&#xff08;7&#xff09;- Sharding-JDBC 公共表 与 读写分离 一、Sharding-JDBC 公共表 1、公共表 公共表属于系统中数据量较小&#xff0c;变动少&#xff0c;而且属于高频联合查询的依赖表。参数表、数据字典表等属于此类型。可以将这类表在每…

LabVIEW汽车ECU测试系统

开发了一个基于LabVIEW开发的汽车发动机控制单元&#xff08;ECU&#xff09;测试系统。该系统使用了NI的硬件和LabVIEW软件&#xff0c;能够自动执行ECU的功能测试和性能测试&#xff0c;确保其在不同工作条件下的可靠性和功能性。通过自动化测试系统&#xff0c;大大提高了测…

深度网络现代实践 - 深度前馈网络之结构设计篇

序言 深度网络结构设计作为人工智能领域的基石&#xff0c;正引领着技术创新的浪潮。通过模拟人脑神经元间的复杂连接&#xff0c;深度神经网络展现了卓越的特征学习与模式识别能力。随着大数据与计算能力的提升&#xff0c;设计高效、精准且泛化能力强的深度网络结构成为研究…

Linux基础 - LNMP 架构部署动态网站环境

目录 零. 简介 一. 部署 二. 安装 Nginx 三. 安装MySQL 四. 安装PHP 五. 配置网站目录 六. 测试环境 零. 简介 LNMP 是指 Linux Nginx MySQL PHP 这一组合架构&#xff0c;用于部署动态网站环境。 Linux 作为操作系统&#xff0c;提供了稳定、安全和高效的基础平台。…

Linux基础 - MariaDB 数据库管理系统

目录 零. 简介 一. 安装 二. 基本使用 1. 设置root密码 2. 创建库 3. 创建表 4.添加数据 5. 查看数据 三. 管理表单及数据 四. 数据库的备份及恢复 零. 简介 MariaDB 是一种流行的开源数据库管理系统&#xff0c;它是 MySQL 的一个分支。 MariaDB 保留了与 MySQL 的…

学习笔记——动态路由——OSPF(工作原理)

九、OSPF协议的工作原理 1、原理概要 (1)相邻路由器之间周期性发送HELLO报文&#xff0c;以便建立和维护邻居关系。 (2)建立邻居关系后&#xff0c;给邻居路由器发送数据库描述报文(DD)&#xff0c;也就是将自己链路状态数据库中的所有链路状态项目的摘要信息发送给邻居路由器…

提高页面性能:懒加载和预加载都是啥原理。

懒加载&#xff08;Lazy Loading&#xff09;和预加载&#xff08;Preloading&#xff09;是在 Web 开发中常用的两种优化技术&#xff0c;用于提高页面性能和用户体验。那他们之间有什么不同&#xff0c;又有哪些应用场景呢&#xff0c;本文给小伙伴们分享下。 一、懒加载 懒…

【Python】已解决:ERROR: No matching distribution found for JPype1

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;ERROR: No matching distribution found for JPype1 一、分析问题背景 在安装Python的第三方库时&#xff0c;有时会遇到“ERROR: No matching distribution fo…

three.js地理坐标系有哪些,和屏幕坐标系的转换。

坐标系很好理解&#xff0c;就是点线面体的位置&#xff0c;一个点是一个坐标&#xff0c;一条线段2个坐标&#xff0c;一个矩形四个坐标&#xff0c;一个立方体8个坐标&#xff0c;three.js面对的是三维空间&#xff0c;屏幕则是二维的&#xff0c;这就面临着转换问题&#xf…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《面向电网调峰的电动汽车聚合商多层级实时控制策略》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

从零开始使用 Docsify 搭建文档站点

引言 在当今的技术环境中&#xff0c;拥有一份易于访问和美观的文档是至关重要的。Docsify 是一个非常适合快速搭建文档站点的工具&#xff0c;它简单易用&#xff0c;且不需要生成静态文件。本文将带你一步步从零开始使用 Docsify 搭建一个文档站点。 1. 安装 Node.js 和 np…

【面试干货】值传递与引用传递:理解Java中的参数传递机制

【面试干货】值传递与引用传递&#xff1a;理解Java中的参数传递机制 1、值传递&#xff08;Call by Value&#xff09;2、引用传递&#xff08;Call by Reference&#xff09;3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 值传递…

O2OA(翱途)开发平台 V9.1 即将发布,更安全、更高效、更开放

尊敬的O2OA(翱途)平台合作伙伴、用户以及亲爱的开发小伙伴们&#xff0c;O2OA(翱途)平台 V9.1将于7月3日正式发布&#xff0c;届时欢迎大家到O2OA官网部署下载及体验最新版本。新版本我们在如下方面做了更大的努力&#xff1a; 1.扩展数据库兼容性和功能范围&#xff1a;在O2OA…

前端技术(说明篇)

Introduction ##编写内容&#xff1a;1.前端概念梳理 2.前端技术种类 3.前端学习方式 ##编写人&#xff1a;贾雯爽 ##最后更新时间&#xff1a;2024/07/01 Overview 最近在广州粤嵌进行实习&#xff0c;项目名称是”基于Node实现多人聊天室“&#xff0c;主要内容是对前端界…

libigl对matlab链接不成功问题的解决办法

libigl使用matlab时出现以下问题&#xff1a; 即使在头文件中添加了 #include <igl/matlab/matlabinterface.h>在cmakelists.txt中添加了 target_link_libraries(${PROJECT_NAME} "C:\\F\\matlab_install\\extern\\lib\\win64\\microsoft\\libeng.lib") t…

bash条件判断基础adsawq1`1nn

判断的作用 判断后续操作的提前条件是否满足如果满足执行一种命令不满足则执行另一种指令 条件测试类型&#xff1a; 整型测试字符测试文字测试 整数测试&#xff1a;比较两个整数谁大谁小&#xff0c;是否相等&#xff1b; 二元测试&#xff1a; num1 操作符 num2 -eq: 等于…

Spring boot 更改启动LOGO

在resources目录下创建banner.txt文件&#xff0c;然后编辑对应的图案即可 注释工具 Spring Boot Version: ${spring-boot.version},-.___,---.__ /|\ __,---,___,- \ -.____,- | -.____,- // -., | ~\ /~ | …