campctf2023-chall-tis256

Zachtronics TIS100-inspired reversing challenge for CampCTF 2023
git clone https://git.sinitax.com/sinitax/campctf2023-chall-tis256
Log | Files | Refs | Submodules | README | sfeed.txt

asm.py (5680B)


      1import random
      2
      3random.seed(133713371337)
      4
      5asm = """
      6    not r0
      7    mov r0, r2
      8    ldb r0, r3 # lsfr
      9    jnz chk_loop
     10
     11lsfr_advance:
     12    # bit 0
     13    mov r3, r0
     14    shr r3, 1
     15    shl r0, 7
     16    xor r0, r3
     17
     18    # bit 3
     19    mov r3, r0
     20    shr r0, 2
     21    shl r0, 7
     22    xor r0, r3
     23
     24    # bit 6
     25    mov r3, r0
     26    shr r0, 5
     27    shl r0, 7
     28    xor r0, r3
     29
     30    # if != 0, jmp, else
     31    # fallthrough to same location
     32    jnz lsfr_advance_ret
     33
     34chk_loop:
     35    jnz lsfr_advance
     36lsfr_advance_ret:
     37
     38    xor r0, r0
     39    not r0
     40    shr r0, 3
     41    and r3, r0
     42
     43    xor r1, r1
     44    inc r1
     45    shl r1, 7
     46    add r1, r0
     47    ldb r0, r1
     48
     49    jnz check_val
     50check_val_ret:
     51
     52    mov r1, r0
     53    jnz chk_loop
     54
     55    not r0
     56    jnz end1
     57
     58check_val:
     59    inp r0
     60    xor r3, r1
     61    xor r0, r1
     62    not r1
     63    and r1, r2
     64    mov r0, r1
     65
     66    xor r0, r0
     67    not r0
     68    jnz check_val2
     69check_val2_ret:
     70    jnz check_val_ret
     71
     72end1:
     73    jnz end2
     74
     75check_val2:
     76    xor r0, r0
     77    inc r0
     78    shl r0, 2
     79    inc r0
     80    shl r0, 1
     81    xor r0, r1
     82
     83    jnz check_val2_ret
     84
     85end2:
     86    mov r2, r0
     87    not r0
     88    jnz print_fail
     89    not r0
     90    jnz print_ok
     91
     92print_fail:
     93    xor r2, r2
     94    not r2
     95    shl r2, 3
     96    jnz print
     97
     98print_ok:
     99    xor r2, r2
    100    not r2
    101    shl r2, 4
    102
    103print:
    104    xor r1, r1
    105    inc r1
    106print_loop:
    107    ldb r2, r0
    108    jnz print_cont
    109    hlt
    110print_cont:
    111    out r0
    112    add r1, r2
    113    jnz print_loop
    114
    115"""
    116
    117flag = b"ALLES!{T3553L4T3!}\x0a"
    118
    119data = {}
    120
    121# ensure lsfr hits different indexes
    122seed_ok = False
    123lsfr_init = None
    124for lsfr_init in range(0x6f, 256):
    125    reg = lsfr_init
    126    data.clear()
    127    seed_ok = True
    128    vals = []
    129    for i,c in enumerate(flag):
    130        b = (reg >> 6) & 0x01
    131        b ^= (reg >> 3) & 0x01
    132        b ^= (reg >> 0) & 0x01
    133        reg = (b << 7) | (reg >> 1)
    134        vals.append(reg)
    135        addr = 0x80 + (reg & 0x1f)
    136        vals.append(addr)
    137        if addr in data:
    138            seed_ok = False
    139        data[addr] = c ^ reg
    140    if seed_ok:
    141        print("vals", [hex(v) for v in vals])
    142        break
    143assert(seed_ok)
    144
    145for i,b in enumerate(b"ok\n\x00"):
    146    data[0xf0+i] = b
    147
    148for i,b in enumerate(b"fail\n\x00"):
    149    data[0xf8+i] = b
    150
    151data[0xff] = lsfr_init
    152
    153bytecode = []
    154labels = {}
    155for l in asm.split("\n"):
    156    l = l.replace(",", " ").strip().lower()
    157    if ":" in l:
    158        name = l.split(":")[0]
    159        labels[name] = len(bytecode)
    160        continue
    161    elif l == "" or l.strip().startswith("#"):
    162        continue
    163    cmd, ops = l.split()[0], l.split()[1:3]
    164    if cmd == "jnz":
    165        name, = ops
    166        bytecode.append(name)
    167    elif cmd == "shl":
    168        r1,sh = int(ops[0][1:]), int(ops[1])
    169        assert(r1 >= 0 and r1 < 4)
    170        assert(sh >= 0 and sh < 8)
    171        bytecode.append(0b01000000 | (r1 << 3) | sh)
    172    elif cmd == "shr":
    173        r1,sh = int(ops[0][1:]), int(ops[1])
    174        assert(r1 >= 0 and r1 < 4)
    175        assert(sh >= 0 and sh < 8)
    176        bytecode.append(0b01100000 | (r1 << 3) | sh)
    177    elif cmd == "mov":
    178        r1,r2 = [int(o[1:]) for o in ops]
    179        assert(r1 >= 0 and r1 < 4)
    180        assert(r2 >= 0 and r2 < 4)
    181        bytecode.append(0b00100000 | (r1 << 2) | r2)
    182    elif cmd == "swp":
    183        r1,r2 = [int(o[1:]) for o in ops]
    184        assert(r1 >= 0 and r1 < 4)
    185        assert(r2 >= 0 and r2 < 4)
    186        bytecode.append(0b00110000 | (r1 << 2) | r2)
    187    elif cmd == "add":
    188        r1,r2 = [int(o[1:]) for o in ops]
    189        assert(r1 >= 0 and r1 < 4)
    190        assert(r2 >= 0 and r2 < 4)
    191        bytecode.append(0b10000000 | (r1 << 2) | r2)
    192    elif cmd == "sub":
    193        r1,r2 = [int(o[1:]) for o in ops]
    194        assert(r1 >= 0 and r1 < 4)
    195        assert(r2 >= 0 and r2 < 4)
    196        bytecode.append(0b10010000 | (r1 << 2) | r2)
    197    elif cmd == "xor":
    198        r1,r2 = [int(o[1:]) for o in ops]
    199        assert(r1 >= 0 and r1 < 4)
    200        assert(r2 >= 0 and r2 < 4)
    201        bytecode.append(0b10100000 | (r1 << 2) | r2)
    202    elif cmd == "and":
    203        r1,r2 = [int(o[1:]) for o in ops]
    204        assert(r1 >= 0 and r1 < 4)
    205        assert(r2 >= 0 and r2 < 4)
    206        bytecode.append(0b10110000 | (r1 << 2) | r2)
    207    elif cmd == "ldb":
    208        r1,r2 = [int(o[1:]) for o in ops]
    209        assert(r1 >= 0 and r1 < 4)
    210        assert(r2 >= 0 and r2 < 4)
    211        bytecode.append(0b11000000 | (r1 << 2) | r2)
    212    elif cmd == "stb":
    213        r1,r2 = [int(o[1:]) for o in ops]
    214        assert(r1 >= 0 and r1 < 4)
    215        assert(r2 >= 0 and r2 < 4)
    216        bytecode.append(0b11010000 | (r1 << 2) | r2)
    217    elif cmd == "inp":
    218        reg, = [int(o[1:]) for o in ops]
    219        assert(reg >= 0 and reg < 4)
    220        bytecode.append(0b11100000 | reg)
    221    elif cmd == "out":
    222        reg, = [int(o[1:]) for o in ops]
    223        assert(reg >= 0 and reg < 4)
    224        bytecode.append(0b11100100 | reg)
    225    elif cmd == "not":
    226        reg, = [int(o[1:]) for o in ops]
    227        assert(reg >= 0 and reg < 4)
    228        bytecode.append(0b11101000 | reg)
    229    elif cmd == "inc":
    230        reg, = [int(o[1:]) for o in ops]
    231        assert(reg >= 0 and reg < 4)
    232        bytecode.append(0b11110000 | reg)
    233    #elif cmd == "dec":
    234    #    reg, = [int(o[1:]) for o in ops]
    235    #    assert(reg >= 0 and reg < 4)
    236    #    bytecode.append(0b11110100 | reg)
    237    elif cmd == "hlt":
    238        bytecode.append(0b11111111)
    239    else:
    240        print(cmd)
    241        assert(False)
    242
    243for i,b in enumerate(bytecode):
    244    if type(b) == str:
    245        off = labels[b] - (i + 1) + 16
    246        print(b, off)
    247        assert(off >= 0 and off < 32)
    248        bytecode[i] = 0b00000000 | off
    249
    250print(len(bytecode), bytecode)
    251bytecode += random.randbytes(0x100 - len(bytecode))
    252for p,v in data.items():
    253    bytecode[p] = v
    254
    255for i in range(4):
    256    with open(f"build/memory{i+1}.bin", "wb+") as f:
    257        f.write(bytes(bytecode[i*64:i*64+64]))
    258