PoseidonCTF 2020 - VM & VM Reloaded (1000+1000pt)

YASL reversing

August 9, 2020
rev bytecode

Overview

I got the only solves for VM and VM Reloaded. Both were VM reversing challenges that used yasl-lang.

VM (1000pt)

Rev

Files:

Overview:

The binary is quite large and we can see references to a lot of strings that don’t seem to familiar like pure, consteval, END OF FILE, collections.set, …

This VM language seemed like something too big to have been created just for a ctf challenge so I started googling these keywords to find a match. Eventually I found yasl-lang which contains a bytecode interpreter written in C.

After some comparison to VM.c we find that the binary is initializing a VM object with custom bytecode and then calling vm_run. The bytecode is located at 0x31480 (in both binaries).

I wasn’t able to find a YASL disassembler so I wrote my own:

# from opcodes.h
ops = '''
O_HALT = 0x0F, // halt
O_NCONST = 0x01, // push literal undef onto stack
O_BCONST_F = 0x08, // push literal false onto stack
O_BCONST_T = 0x09, // push literal true onto stack
O_FCONST = 0x0A, // push function literal onto stack
O_CCONST = 0x0B, // push closure literal onto stack

O_ICONST = 0x10, // push next 8 bytes onto stack as integer constant
O_ICONST_M1 = 0x11, // push -1 onto stack
O_ICONST_0 = 0x12, // push 0 onto stack
O_ICONST_1 = 0x13, // push 1 onto stack
O_ICONST_2 = 0x14, // push 2 onto stack
O_ICONST_3 = 0x15, // push 3 onto stack
O_ICONST_4 = 0x16, // push 4 onto stack
O_ICONST_5 = 0x17, // push 5 onto stack
O_DCONST = 0x1A, // push next 8 bytes onto stack as float constant
O_DCONST_0 = 0x1B, // push 0.0 onto stack
O_DCONST_1 = 0x1C, // push 1.0 onto stack
O_DCONST_2 = 0x1D, // push 2.0 onto stack

O_ICONST_B1 = 0x20, // push literal integer that only requires 1 byte onto stack.
O_ICONST_B2 = 0x21, // TODO
O_ICONST_B4 = 0x22, // TODO
O_ICONST_B8 = 0x23, // synonym for O_ICONST

O_BOR = 0x40, // bitwise or
O_BXOR = 0x41, // bitwise xor
O_BAND = 0x42, // bitwise and
O_BANDNOT = 0x43, // bitwise and, with bitwise not on right operand
O_BNOT = 0x44, // bit negation
O_BSL = 0x45, // bitwise left shift
O_BSR = 0x46, // bitwise right shift

O_ASS = 0x50, // assert

O_MATCH = 0x51, // match

O_ADD = 0x60, // add two numbers
O_SUB = 0x61, // subtract two numbers
O_MUL = 0x62, // multiply two integers
O_EXP = 0x63, // exponentiation
O_FDIV = 0x64, // divide two numbers (return float)
O_IDIV = 0x65, // divide two ints (return int)
O_MOD = 0x66, // modulo two integers
O_NEG = 0x67, // negate a number
O_POS = 0x68, // positive of a number
O_NOT = 0x69, // negate a boolean
O_LEN = 0x6A, // get length
O_CNCT = 0x6B, // concat two strings or lists

O_LT = 0x70, // less than
O_LE = 0x71, // less than or equal
O_GT = 0x72, // greater than
O_GE = 0x73, // greater than or equal
O_EQ = 0x74, // equality
O_ID = 0x76, // identity

O_SET = 0x80, // sets field.
O_GET = 0x88, // gets field.
O_SLICE = 0x8A, // slice of list or str

O_EXPORT = 0x90, // export

O_NEWSTR = 0x9B, // make new String and push it onto stack (length (8 bytes), string (length bytes))
O_NEWTABLE = 0x9C, // make new HashTable and push it onto stack
O_NEWLIST = 0x9D, // make new List and push it onto stack
O_SCONST = 0x9E, // load a string literal from constant table.
O_SCONST_INIT = 0x9F, // initialize the constant table with a string literal.

O_CRET = 0xA0, // return from closure.

O_END = 0xB0, // indicate end of list on stack.
O_DUP = 0xB8, // duplicate top value of stack
O_INCSP = 0xBE,
O_POP = 0xBF, // pop top of stack

O_BR_8 = 0xC0, // branch unconditionally (takes next 8 bytes as jump length)
O_BRF_8 = 0xC1, // branch if condition is falsey (takes next 8 bytes as jump length)
O_BRT_8 = 0xC2, // branch if condition is truthy (takes next 8 bytes as jump length)
O_BRN_8 = 0xC3, // branch if condition is not undef (takes next 8 bytes as jump length)

O_INITFOR = 0xD0, // initialises for-loop in VM
O_ENDCOMP = 0xD1, // end list / table comprehension
O_ENDFOR = 0xD2, // end for-loop in VM
O_ITER_1 = 0xD3, // iterate to next, 1 var
O_ITER_2 = 0xD5, // iterate to next, 2 var

O_INIT_MC = 0xE7,
O_INIT_CALL = 0xE8, // set up function call
O_CALL = 0xE9, // function call
O_RET = 0xEA, // return from function

O_GSTORE_8 = 0xF0, // from string
O_GLOAD_8 = 0xF1, // from string
O_USTORE = 0xF2, // load upvalue
O_ULOAD = 0xF3, // store upvalue
O_LSTORE = 0xF4, // store top of stack as local at addr
O_LLOAD = 0xF5, // load local from addr
O_PRINT = 0xFF,  // print
'''
ops = ops.strip().split('\n')
ops = [x for x in ops if len(x.strip()) > 0]
ops = [(x.split(' ')[0], x.split('= ')[1].split(',')[0]) for x in ops]
op_map = {int(num[2:],16):nm for nm,num in ops}
def getint(code, addr, sz=8):
  v = 0
  for i in range(sz):
    v += code[i+addr] << (i * 8)
    
  if v & (0x80 << ((sz-1)*8)):
    v -= (1 << (sz * 8))
  return v

CONSTANTS = {}
r = 0x18
for i in range(0x10e):
  v = getint(bytecode, r, 8)
  s = bytecode[r+8:r+8+v]
  CONSTANTS[i] = s
  r += 8 + v
    
def getop(code, pc):
  op = code[pc]
  if not op in op_map:
    return 'unk %02x' % op, 1
    
  bname = op_map[op]
    
  if bname == 'O_GLOAD_8':
    return '%s (%s)' % (bname, CONSTANTS[getint(code, pc+1)]), 9
  elif bname in ['O_NEWSTR', 'O_INIT_MC']:
    return '%s (%s)' % (bname, CONSTANTS[getint(code, pc+1)]), 9
  elif bname == 'O_ICONST_B1':
    return '%s (%d)' % (bname, getint(code, pc+1, 1)), 2
  elif bname == 'O_ICONST_B2':
    return '%s (%d)' % (bname, getint(code, pc+1, 2)), 3
  elif bname == 'O_ICONST_B4':
    return '%s (%d)' % (bname, getint(code, pc+1, 4)), 5
  elif bname == 'O_ICONST_B8':
    return '%s (%d)' % (bname, getint(code, pc+1, 8)), 9
  elif bname == 'O_FCONST':
    v = getint(code, pc+1)
    return '%s (%d) -> (%04X)' % (bname, v, pc+9), 9+v
  elif bname == 'O_CCONST':
    v = getint(code, pc+1)
    n = getint(code, pc+9, 1)
    return '%s (%d) -> (%04X)' % (bname, v, pc+9), 9+v+n
  elif bname in ['O_LLOAD', 'O_LSTORE', 'O_ULOAD']:
    return '%s (%d)' % (bname, getint(code, pc+1, 1)), 2
  elif bname == 'O_BRF_8':
    v = getint(code, pc+1)
    return '%s -> %04X' % (bname, pc+8+v), 9
  elif bname == 'O_BR_8':
    v = getint(code, pc+1)
    return '%s -> %04X' % (bname, pc+9+v), 9+v
  elif bname == 'O_HALT':
    return bname, 0
  else:
    return bname, 1
    
def disasm(code, pc):
  seen = set()
  while True:
    if pc in seen:
      break
        
    seen.add(pc)
    name, size = getop(code, pc)
    print('%04X :: %s' % (pc, name))
    pc += size
    
    if size == 0:
      break

There are still a few issues with branch targets and O_CCONST sizes but it was easy enough to ignore the unk instructions.

Using this, I was able to extract the following bytecode:

0A18 :: O_GLOAD_8 (b'io')
0A21 :: O_NEWSTR (b'stdin')
0A2A :: O_GET
0A2B :: O_END
0A2C :: O_NEWSTR (b'\x00')
0A35 :: O_ICONST_0
0A36 :: O_NEWSTR (b'\x01')
0A3F :: O_ICONST_1
0A40 :: O_NEWSTR (b'\x02')
0A49 :: O_ICONST_2
0A4A :: O_NEWSTR (b'\x03')
0A53 :: O_ICONST_3
0A54 :: O_NEWSTR (b'\x04')
0A5D :: O_ICONST_4
0A5E :: O_NEWSTR (b'\x05')
0A67 :: O_ICONST_5
0A68 :: O_NEWSTR (b'\x06')
0A71 :: O_ICONST_B1 (6)
0A73 :: O_NEWSTR (b'\x07')
0A7C :: O_ICONST_B1 (7)
0A7E :: O_NEWSTR (b'\x08')
0A87 :: O_ICONST_B1 (8)
0A89 :: O_NEWSTR (b'\t')
0A92 :: O_ICONST_B1 (9)
0A94 :: O_NEWSTR (b'\n')
0A9D :: O_ICONST_B1 (10)
0A9F :: O_NEWSTR (b'\x0b')
0AA8 :: O_ICONST_B1 (11)
0AAA :: O_NEWSTR (b'\x0c')
0AB3 :: O_ICONST_B1 (12)
0AB5 :: O_NEWSTR (b'\r')
0ABE :: O_ICONST_B1 (13)
0AC0 :: O_NEWSTR (b'\x0e')
0AC9 :: O_ICONST_B1 (14)
0ACB :: O_NEWSTR (b'\x0f')
0AD4 :: O_ICONST_B1 (15)
0AD6 :: O_NEWSTR (b'\x10')
0ADF :: O_ICONST_B1 (16)
0AE1 :: O_NEWSTR (b'\x11')
0AEA :: O_ICONST_B1 (17)
0AEC :: O_NEWSTR (b'\x12')
0AF5 :: O_ICONST_B1 (18)
0AF7 :: O_NEWSTR (b'\x13')
0B00 :: O_ICONST_B1 (19)
0B02 :: O_NEWSTR (b'\x14')
0B0B :: O_ICONST_B1 (20)
0B0D :: O_NEWSTR (b'\x15')
0B16 :: O_ICONST_B1 (21)
0B18 :: O_NEWSTR (b'\x16')
0B21 :: O_ICONST_B1 (22)
0B23 :: O_NEWSTR (b'\x17')
0B2C :: O_ICONST_B1 (23)
0B2E :: O_NEWSTR (b'\x18')
0B37 :: O_ICONST_B1 (24)
0B39 :: O_NEWSTR (b'\x19')
0B42 :: O_ICONST_B1 (25)
0B44 :: O_NEWSTR (b'\x1a')
0B4D :: O_ICONST_B1 (26)
0B4F :: O_NEWSTR (b'\x1b')
0B58 :: O_ICONST_B1 (27)
0B5A :: O_NEWSTR (b'\x1c')
0B63 :: O_ICONST_B1 (28)
0B65 :: O_NEWSTR (b'\x1d')
0B6E :: O_ICONST_B1 (29)
0B70 :: O_NEWSTR (b'\x1e')
0B79 :: O_ICONST_B1 (30)
0B7B :: O_NEWSTR (b'\x1f')
0B84 :: O_ICONST_B1 (31)
0B86 :: O_NEWSTR (b' ')
0B8F :: O_ICONST_B1 (32)
0B91 :: O_NEWSTR (b'!')
0B9A :: O_ICONST_B1 (33)
0B9C :: O_NEWSTR (b'"')
0BA5 :: O_ICONST_B1 (34)
0BA7 :: O_NEWSTR (b'#')
0BB0 :: O_ICONST_B1 (35)
0BB2 :: O_NEWSTR (b'$')
0BBB :: O_ICONST_B1 (36)
0BBD :: O_NEWSTR (b'%')
0BC6 :: O_ICONST_B1 (37)
0BC8 :: O_NEWSTR (b'&')
0BD1 :: O_ICONST_B1 (38)
0BD3 :: O_NEWSTR (b"'")
0BDC :: O_ICONST_B1 (39)
0BDE :: O_NEWSTR (b'(')
0BE7 :: O_ICONST_B1 (40)
0BE9 :: O_NEWSTR (b')')
0BF2 :: O_ICONST_B1 (41)
0BF4 :: O_NEWSTR (b'*')
0BFD :: O_ICONST_B1 (42)
0BFF :: O_NEWSTR (b'+')
0C08 :: O_ICONST_B1 (43)
0C0A :: O_NEWSTR (b',')
0C13 :: O_ICONST_B1 (44)
0C15 :: O_NEWSTR (b'-')
0C1E :: O_ICONST_B1 (45)
0C20 :: O_NEWSTR (b'.')
0C29 :: O_ICONST_B1 (46)
0C2B :: O_NEWSTR (b'/')
0C34 :: O_ICONST_B1 (47)
0C36 :: O_NEWSTR (b'0')
0C3F :: O_ICONST_B1 (48)
0C41 :: O_NEWSTR (b'1')
0C4A :: O_ICONST_B1 (49)
0C4C :: O_NEWSTR (b'2')
0C55 :: O_ICONST_B1 (50)
0C57 :: O_NEWSTR (b'3')
0C60 :: O_ICONST_B1 (51)
0C62 :: O_NEWSTR (b'4')
0C6B :: O_ICONST_B1 (52)
0C6D :: O_NEWSTR (b'5')
0C76 :: O_ICONST_B1 (53)
0C78 :: O_NEWSTR (b'6')
0C81 :: O_ICONST_B1 (54)
0C83 :: O_NEWSTR (b'7')
0C8C :: O_ICONST_B1 (55)
0C8E :: O_NEWSTR (b'8')
0C97 :: O_ICONST_B1 (56)
0C99 :: O_NEWSTR (b'9')
0CA2 :: O_ICONST_B1 (57)
0CA4 :: O_NEWSTR (b':')
0CAD :: O_ICONST_B1 (58)
0CAF :: O_NEWSTR (b';')
0CB8 :: O_ICONST_B1 (59)
0CBA :: O_NEWSTR (b'<')
0CC3 :: O_ICONST_B1 (60)
0CC5 :: O_NEWSTR (b'=')
0CCE :: O_ICONST_B1 (61)
0CD0 :: O_NEWSTR (b'>')
0CD9 :: O_ICONST_B1 (62)
0CDB :: O_NEWSTR (b'?')
0CE4 :: O_ICONST_B1 (63)
0CE6 :: O_NEWSTR (b'@')
0CEF :: O_ICONST_B1 (64)
0CF1 :: O_NEWSTR (b'A')
0CFA :: O_ICONST_B1 (65)
0CFC :: O_NEWSTR (b'B')
0D05 :: O_ICONST_B1 (66)
0D07 :: O_NEWSTR (b'C')
0D10 :: O_ICONST_B1 (67)
0D12 :: O_NEWSTR (b'D')
0D1B :: O_ICONST_B1 (68)
0D1D :: O_NEWSTR (b'E')
0D26 :: O_ICONST_B1 (69)
0D28 :: O_NEWSTR (b'F')
0D31 :: O_ICONST_B1 (70)
0D33 :: O_NEWSTR (b'G')
0D3C :: O_ICONST_B1 (71)
0D3E :: O_NEWSTR (b'H')
0D47 :: O_ICONST_B1 (72)
0D49 :: O_NEWSTR (b'I')
0D52 :: O_ICONST_B1 (73)
0D54 :: O_NEWSTR (b'J')
0D5D :: O_ICONST_B1 (74)
0D5F :: O_NEWSTR (b'K')
0D68 :: O_ICONST_B1 (75)
0D6A :: O_NEWSTR (b'L')
0D73 :: O_ICONST_B1 (76)
0D75 :: O_NEWSTR (b'M')
0D7E :: O_ICONST_B1 (77)
0D80 :: O_NEWSTR (b'N')
0D89 :: O_ICONST_B1 (78)
0D8B :: O_NEWSTR (b'O')
0D94 :: O_ICONST_B1 (79)
0D96 :: O_NEWSTR (b'P')
0D9F :: O_ICONST_B1 (80)
0DA1 :: O_NEWSTR (b'Q')
0DAA :: O_ICONST_B1 (81)
0DAC :: O_NEWSTR (b'R')
0DB5 :: O_ICONST_B1 (82)
0DB7 :: O_NEWSTR (b'S')
0DC0 :: O_ICONST_B1 (83)
0DC2 :: O_NEWSTR (b'T')
0DCB :: O_ICONST_B1 (84)
0DCD :: O_NEWSTR (b'U')
0DD6 :: O_ICONST_B1 (85)
0DD8 :: O_NEWSTR (b'V')
0DE1 :: O_ICONST_B1 (86)
0DE3 :: O_NEWSTR (b'W')
0DEC :: O_ICONST_B1 (87)
0DEE :: O_NEWSTR (b'X')
0DF7 :: O_ICONST_B1 (88)
0DF9 :: O_NEWSTR (b'Y')
0E02 :: O_ICONST_B1 (89)
0E04 :: O_NEWSTR (b'Z')
0E0D :: O_ICONST_B1 (90)
0E0F :: O_NEWSTR (b'[')
0E18 :: O_ICONST_B1 (91)
0E1A :: O_NEWSTR (b'\\')
0E23 :: O_ICONST_B1 (92)
0E25 :: O_NEWSTR (b']')
0E2E :: O_ICONST_B1 (93)
0E30 :: O_NEWSTR (b'^')
0E39 :: O_ICONST_B1 (94)
0E3B :: O_NEWSTR (b'_')
0E44 :: O_ICONST_B1 (95)
0E46 :: O_NEWSTR (b'`')
0E4F :: O_ICONST_B1 (96)
0E51 :: O_NEWSTR (b'a')
0E5A :: O_ICONST_B1 (97)
0E5C :: O_NEWSTR (b'b')
0E65 :: O_ICONST_B1 (98)
0E67 :: O_NEWSTR (b'c')
0E70 :: O_ICONST_B1 (99)
0E72 :: O_NEWSTR (b'd')
0E7B :: O_ICONST_B1 (100)
0E7D :: O_NEWSTR (b'e')
0E86 :: O_ICONST_B1 (101)
0E88 :: O_NEWSTR (b'f')
0E91 :: O_ICONST_B1 (102)
0E93 :: O_NEWSTR (b'g')
0E9C :: O_ICONST_B1 (103)
0E9E :: O_NEWSTR (b'h')
0EA7 :: O_ICONST_B1 (104)
0EA9 :: O_NEWSTR (b'i')
0EB2 :: O_ICONST_B1 (105)
0EB4 :: O_NEWSTR (b'j')
0EBD :: O_ICONST_B1 (106)
0EBF :: O_NEWSTR (b'k')
0EC8 :: O_ICONST_B1 (107)
0ECA :: O_NEWSTR (b'l')
0ED3 :: O_ICONST_B1 (108)
0ED5 :: O_NEWSTR (b'm')
0EDE :: O_ICONST_B1 (109)
0EE0 :: O_NEWSTR (b'n')
0EE9 :: O_ICONST_B1 (110)
0EEB :: O_NEWSTR (b'o')
0EF4 :: O_ICONST_B1 (111)
0EF6 :: O_NEWSTR (b'p')
0EFF :: O_ICONST_B1 (112)
0F01 :: O_NEWSTR (b'q')
0F0A :: O_ICONST_B1 (113)
0F0C :: O_NEWSTR (b'r')
0F15 :: O_ICONST_B1 (114)
0F17 :: O_NEWSTR (b's')
0F20 :: O_ICONST_B1 (115)
0F22 :: O_NEWSTR (b't')
0F2B :: O_ICONST_B1 (116)
0F2D :: O_NEWSTR (b'u')
0F36 :: O_ICONST_B1 (117)
0F38 :: O_NEWSTR (b'v')
0F41 :: O_ICONST_B1 (118)
0F43 :: O_NEWSTR (b'w')
0F4C :: O_ICONST_B1 (119)
0F4E :: O_NEWSTR (b'x')
0F57 :: O_ICONST_B1 (120)
0F59 :: O_NEWSTR (b'y')
0F62 :: O_ICONST_B1 (121)
0F64 :: O_NEWSTR (b'z')
0F6D :: O_ICONST_B1 (122)
0F6F :: O_NEWSTR (b'{')
0F78 :: O_ICONST_B1 (123)
0F7A :: O_NEWSTR (b'|')
0F83 :: O_ICONST_B1 (124)
0F85 :: O_NEWSTR (b'}')
0F8E :: O_ICONST_B1 (125)
0F90 :: O_NEWSTR (b'~')
0F99 :: O_ICONST_B1 (126)
0F9B :: O_NEWSTR (b'\x7f')
0FA4 :: O_ICONST_B1 (127)
0FA6 :: O_NEWSTR (b'\x80')
0FAF :: O_ICONST_B8 (128)
0FB8 :: O_NEWSTR (b'\x81')
0FC1 :: O_ICONST_B8 (129)
0FCA :: O_NEWSTR (b'\x82')
0FD3 :: O_ICONST_B8 (130)
0FDC :: O_NEWSTR (b'\x83')
0FE5 :: O_ICONST_B8 (131)
0FEE :: O_NEWSTR (b'\x84')
0FF7 :: O_ICONST_B8 (132)
1000 :: O_NEWSTR (b'\x85')
1009 :: O_ICONST_B8 (133)
1012 :: O_NEWSTR (b'\x86')
101B :: O_ICONST_B8 (134)
1024 :: O_NEWSTR (b'\x87')
102D :: O_ICONST_B8 (135)
1036 :: O_NEWSTR (b'\x88')
103F :: O_ICONST_B8 (136)
1048 :: O_NEWSTR (b'\x89')
1051 :: O_ICONST_B8 (137)
105A :: O_NEWSTR (b'\x8a')
1063 :: O_ICONST_B8 (138)
106C :: O_NEWSTR (b'\x8b')
1075 :: O_ICONST_B8 (139)
107E :: O_NEWSTR (b'\x8c')
1087 :: O_ICONST_B8 (140)
1090 :: O_NEWSTR (b'\x8d')
1099 :: O_ICONST_B8 (141)
10A2 :: O_NEWSTR (b'\x8e')
10AB :: O_ICONST_B8 (142)
10B4 :: O_NEWSTR (b'\x8f')
10BD :: O_ICONST_B8 (143)
10C6 :: O_NEWSTR (b'\x90')
10CF :: O_ICONST_B8 (144)
10D8 :: O_NEWSTR (b'\x91')
10E1 :: O_ICONST_B8 (145)
10EA :: O_NEWSTR (b'\x92')
10F3 :: O_ICONST_B8 (146)
10FC :: O_NEWSTR (b'\x93')
1105 :: O_ICONST_B8 (147)
110E :: O_NEWSTR (b'\x94')
1117 :: O_ICONST_B8 (148)
1120 :: O_NEWSTR (b'\x95')
1129 :: O_ICONST_B8 (149)
1132 :: O_NEWSTR (b'\x96')
113B :: O_ICONST_B8 (150)
1144 :: O_NEWSTR (b'\x97')
114D :: O_ICONST_B8 (151)
1156 :: O_NEWSTR (b'\x98')
115F :: O_ICONST_B8 (152)
1168 :: O_NEWSTR (b'\x99')
1171 :: O_ICONST_B8 (153)
117A :: O_NEWSTR (b'\x9a')
1183 :: O_ICONST_B8 (154)
118C :: O_NEWSTR (b'\x9b')
1195 :: O_ICONST_B8 (155)
119E :: O_NEWSTR (b'\x9c')
11A7 :: O_ICONST_B8 (156)
11B0 :: O_NEWSTR (b'\x9d')
11B9 :: O_ICONST_B8 (157)
11C2 :: O_NEWSTR (b'\x9e')
11CB :: O_ICONST_B8 (158)
11D4 :: O_NEWSTR (b'\x9f')
11DD :: O_ICONST_B8 (159)
11E6 :: O_NEWSTR (b'\xa0')
11EF :: O_ICONST_B8 (160)
11F8 :: O_NEWSTR (b'\xa1')
1201 :: O_ICONST_B8 (161)
120A :: O_NEWSTR (b'\xa2')
1213 :: O_ICONST_B8 (162)
121C :: O_NEWSTR (b'\xa3')
1225 :: O_ICONST_B8 (163)
122E :: O_NEWSTR (b'\xa4')
1237 :: O_ICONST_B8 (164)
1240 :: O_NEWSTR (b'\xa5')
1249 :: O_ICONST_B8 (165)
1252 :: O_NEWSTR (b'\xa6')
125B :: O_ICONST_B8 (166)
1264 :: O_NEWSTR (b'\xa7')
126D :: O_ICONST_B8 (167)
1276 :: O_NEWSTR (b'\xa8')
127F :: O_ICONST_B8 (168)
1288 :: O_NEWSTR (b'\xa9')
1291 :: O_ICONST_B8 (169)
129A :: O_NEWSTR (b'\xaa')
12A3 :: O_ICONST_B8 (170)
12AC :: O_NEWSTR (b'\xab')
12B5 :: O_ICONST_B8 (171)
12BE :: O_NEWSTR (b'\xac')
12C7 :: O_ICONST_B8 (172)
12D0 :: O_NEWSTR (b'\xad')
12D9 :: O_ICONST_B8 (173)
12E2 :: O_NEWSTR (b'\xae')
12EB :: O_ICONST_B8 (174)
12F4 :: O_NEWSTR (b'\xaf')
12FD :: O_ICONST_B8 (175)
1306 :: O_NEWSTR (b'\xb0')
130F :: O_ICONST_B8 (176)
1318 :: O_NEWSTR (b'\xb1')
1321 :: O_ICONST_B8 (177)
132A :: O_NEWSTR (b'\xb2')
1333 :: O_ICONST_B8 (178)
133C :: O_NEWSTR (b'\xb3')
1345 :: O_ICONST_B8 (179)
134E :: O_NEWSTR (b'\xb4')
1357 :: O_ICONST_B8 (180)
1360 :: O_NEWSTR (b'\xb5')
1369 :: O_ICONST_B8 (181)
1372 :: O_NEWSTR (b'\xb6')
137B :: O_ICONST_B8 (182)
1384 :: O_NEWSTR (b'\xb7')
138D :: O_ICONST_B8 (183)
1396 :: O_NEWSTR (b'\xb8')
139F :: O_ICONST_B8 (184)
13A8 :: O_NEWSTR (b'\xb9')
13B1 :: O_ICONST_B8 (185)
13BA :: O_NEWSTR (b'\xba')
13C3 :: O_ICONST_B8 (186)
13CC :: O_NEWSTR (b'\xbb')
13D5 :: O_ICONST_B8 (187)
13DE :: O_NEWSTR (b'\xbc')
13E7 :: O_ICONST_B8 (188)
13F0 :: O_NEWSTR (b'\xbd')
13F9 :: O_ICONST_B8 (189)
1402 :: O_NEWSTR (b'\xbe')
140B :: O_ICONST_B8 (190)
1414 :: O_NEWSTR (b'\xbf')
141D :: O_ICONST_B8 (191)
1426 :: O_NEWSTR (b'\xc0')
142F :: O_ICONST_B8 (192)
1438 :: O_NEWSTR (b'\xc1')
1441 :: O_ICONST_B8 (193)
144A :: O_NEWSTR (b'\xc2')
1453 :: O_ICONST_B8 (194)
145C :: O_NEWSTR (b'\xc3')
1465 :: O_ICONST_B8 (195)
146E :: O_NEWSTR (b'\xc4')
1477 :: O_ICONST_B8 (196)
1480 :: O_NEWSTR (b'\xc5')
1489 :: O_ICONST_B8 (197)
1492 :: O_NEWSTR (b'\xc6')
149B :: O_ICONST_B8 (198)
14A4 :: O_NEWSTR (b'\xc7')
14AD :: O_ICONST_B8 (199)
14B6 :: O_NEWSTR (b'\xc8')
14BF :: O_ICONST_B8 (200)
14C8 :: O_NEWSTR (b'\xc9')
14D1 :: O_ICONST_B8 (201)
14DA :: O_NEWSTR (b'\xca')
14E3 :: O_ICONST_B8 (202)
14EC :: O_NEWSTR (b'\xcb')
14F5 :: O_ICONST_B8 (203)
14FE :: O_NEWSTR (b'\xcc')
1507 :: O_ICONST_B8 (204)
1510 :: O_NEWSTR (b'\xcd')
1519 :: O_ICONST_B8 (205)
1522 :: O_NEWSTR (b'\xce')
152B :: O_ICONST_B8 (206)
1534 :: O_NEWSTR (b'\xcf')
153D :: O_ICONST_B8 (207)
1546 :: O_NEWSTR (b'\xd0')
154F :: O_ICONST_B8 (208)
1558 :: O_NEWSTR (b'\xd1')
1561 :: O_ICONST_B8 (209)
156A :: O_NEWSTR (b'\xd2')
1573 :: O_ICONST_B8 (210)
157C :: O_NEWSTR (b'\xd3')
1585 :: O_ICONST_B8 (211)
158E :: O_NEWSTR (b'\xd4')
1597 :: O_ICONST_B8 (212)
15A0 :: O_NEWSTR (b'\xd5')
15A9 :: O_ICONST_B8 (213)
15B2 :: O_NEWSTR (b'\xd6')
15BB :: O_ICONST_B8 (214)
15C4 :: O_NEWSTR (b'\xd7')
15CD :: O_ICONST_B8 (215)
15D6 :: O_NEWSTR (b'\xd8')
15DF :: O_ICONST_B8 (216)
15E8 :: O_NEWSTR (b'\xd9')
15F1 :: O_ICONST_B8 (217)
15FA :: O_NEWSTR (b'\xda')
1603 :: O_ICONST_B8 (218)
160C :: O_NEWSTR (b'\xdb')
1615 :: O_ICONST_B8 (219)
161E :: O_NEWSTR (b'\xdc')
1627 :: O_ICONST_B8 (220)
1630 :: O_NEWSTR (b'\xdd')
1639 :: O_ICONST_B8 (221)
1642 :: O_NEWSTR (b'\xde')
164B :: O_ICONST_B8 (222)
1654 :: O_NEWSTR (b'\xdf')
165D :: O_ICONST_B8 (223)
1666 :: O_NEWSTR (b'\xe0')
166F :: O_ICONST_B8 (224)
1678 :: O_NEWSTR (b'\xe1')
1681 :: O_ICONST_B8 (225)
168A :: O_NEWSTR (b'\xe2')
1693 :: O_ICONST_B8 (226)
169C :: O_NEWSTR (b'\xe3')
16A5 :: O_ICONST_B8 (227)
16AE :: O_NEWSTR (b'\xe4')
16B7 :: O_ICONST_B8 (228)
16C0 :: O_NEWSTR (b'\xe5')
16C9 :: O_ICONST_B8 (229)
16D2 :: O_NEWSTR (b'\xe6')
16DB :: O_ICONST_B8 (230)
16E4 :: O_NEWSTR (b'\xe7')
16ED :: O_ICONST_B8 (231)
16F6 :: O_NEWSTR (b'\xe8')
16FF :: O_ICONST_B8 (232)
1708 :: O_NEWSTR (b'\xe9')
1711 :: O_ICONST_B8 (233)
171A :: O_NEWSTR (b'\xea')
1723 :: O_ICONST_B8 (234)
172C :: O_NEWSTR (b'\xeb')
1735 :: O_ICONST_B8 (235)
173E :: O_NEWSTR (b'\xec')
1747 :: O_ICONST_B8 (236)
1750 :: O_NEWSTR (b'\xed')
1759 :: O_ICONST_B8 (237)
1762 :: O_NEWSTR (b'\xee')
176B :: O_ICONST_B8 (238)
1774 :: O_NEWSTR (b'\xef')
177D :: O_ICONST_B8 (239)
1786 :: O_NEWSTR (b'\xf0')
178F :: O_ICONST_B8 (240)
1798 :: O_NEWSTR (b'\xf1')
17A1 :: O_ICONST_B8 (241)
17AA :: O_NEWSTR (b'\xf2')
17B3 :: O_ICONST_B8 (242)
17BC :: O_NEWSTR (b'\xf3')
17C5 :: O_ICONST_B8 (243)
17CE :: O_NEWSTR (b'\xf4')
17D7 :: O_ICONST_B8 (244)
17E0 :: O_NEWSTR (b'\xf5')
17E9 :: O_ICONST_B8 (245)
17F2 :: O_NEWSTR (b'\xf6')
17FB :: O_ICONST_B8 (246)
1804 :: O_NEWSTR (b'\xf7')
180D :: O_ICONST_B8 (247)
1816 :: O_NEWSTR (b'\xf8')
181F :: O_ICONST_B8 (248)
1828 :: O_NEWSTR (b'\xf9')
1831 :: O_ICONST_B8 (249)
183A :: O_NEWSTR (b'\xfa')
1843 :: O_ICONST_B8 (250)
184C :: O_NEWSTR (b'\xfb')
1855 :: O_ICONST_B8 (251)
185E :: O_NEWSTR (b'\xfc')
1867 :: O_ICONST_B8 (252)
1870 :: O_NEWSTR (b'\xfd')
1879 :: O_ICONST_B8 (253)
1882 :: O_NEWSTR (b'\xfe')
188B :: O_ICONST_B8 (254)
1894 :: O_NEWSTR (b'\xff')
189D :: O_ICONST_B8 (255)
18A6 :: O_NEWTABLE
18A7 :: O_FCONST (83) -> (18B0)
1903 :: O_CCONST (73) -> (190C)
1957 :: O_END
1958 :: O_ICONST_B8 (203)
1961 :: O_ICONST_B8 (158)
196A :: O_ICONST_B8 (179)
1973 :: O_ICONST_B1 (72)
1975 :: O_ICONST_B8 (242)
197E :: O_ICONST_B8 (149)
1987 :: O_ICONST_B8 (175)
1990 :: O_ICONST_B1 (67)
1992 :: O_ICONST_B8 (224)
199B :: O_ICONST_B8 (184)
19A4 :: O_ICONST_B8 (159)
19AD :: O_ICONST_B1 (69)
19AF :: O_ICONST_B8 (244)
19B8 :: O_ICONST_B8 (129)
19C1 :: O_ICONST_B8 (165)
19CA :: O_ICONST_B1 (114)
19CC :: O_ICONST_B8 (226)
19D5 :: O_ICONST_B8 (158)
19DE :: O_ICONST_B8 (181)
19E7 :: O_ICONST_B1 (114)
19E9 :: O_ICONST_B8 (255)
19F2 :: O_ICONST_B8 (152)
19FB :: O_ICONST_B8 (164)
1A04 :: O_ICONST_B1 (67)
1A06 :: O_ICONST_B8 (239)
1A0F :: O_ICONST_B8 (174)
1A18 :: O_ICONST_B8 (178)
1A21 :: O_ICONST_B1 (30)
1A23 :: O_ICONST_B8 (237)
1A2C :: O_ICONST_B8 (148)
1A35 :: O_ICONST_B8 (243)
1A3E :: O_ICONST_B1 (95)
1A40 :: O_ICONST_B8 (232)
1A49 :: O_ICONST_B8 (148)
1A52 :: O_ICONST_B8 (159)
1A5B :: O_ICONST_B1 (89)
1A5D :: O_ICONST_B8 (243)
1A66 :: O_ICONST_B8 (152)
1A6F :: O_ICONST_B8 (179)
1A78 :: O_ICONST_B1 (114)
1A7A :: O_ICONST_B8 (249)
1A83 :: O_ICONST_B8 (132)
1A8C :: O_ICONST_B8 (180)
1A95 :: O_ICONST_B1 (114)
1A97 :: O_ICONST_B8 (238)
1AA0 :: O_ICONST_B8 (130)
1AA9 :: O_ICONST_B8 (165)
1AB2 :: O_ICONST_B1 (73)
1AB4 :: O_ICONST_B8 (196)
1ABD :: O_ICONST_B8 (144)
1AC6 :: O_ICONST_B8 (159)
1ACF :: O_ICONST_B1 (94)
1AD1 :: O_ICONST_B8 (242)
1ADA :: O_ICONST_B8 (149)
1AE3 :: O_ICONST_B8 (165)
1AEC :: O_ICONST_B1 (114)
1AEE :: O_ICONST_B8 (248)
1AF7 :: O_ICONST_B8 (153)
1B00 :: O_ICONST_B8 (161)
1B09 :: O_ICONST_B1 (67)
1B0B :: O_ICONST_B8 (245)
1B14 :: O_ICONST_B8 (148)
1B1D :: O_ICONST_B8 (172)
1B26 :: O_ICONST_B1 (114)
1B28 :: O_ICONST_B8 (250)
1B31 :: O_ICONST_B8 (133)
1B3A :: O_ICONST_B8 (180)
1B43 :: O_ICONST_B1 (25)
1B45 :: O_ICONST_B8 (248)
1B4E :: O_ICONST_B8 (154)
1B57 :: O_ICONST_B8 (189)
1B60 :: O_NEWLIST
1B61 :: O_NEWSTR (b'Input the flag: ')
1B6A :: O_PRINT
1B6B :: O_LLOAD (0)
1B6D :: O_NEWSTR (b'read')
1B76 :: O_GET
1B77 :: O_INIT_CALL
1B78 :: O_LLOAD (0)
1B7A :: O_CALL
1B7B :: O_LLOAD (2)
1B7D :: O_INIT_CALL
1B7E :: O_CALL
1B7F :: O_LLOAD (3)
1B81 :: O_INIT_CALL
1B82 :: O_LLOAD (5)
1B84 :: O_LLOAD (6)
1B86 :: O_CALL
1B87 :: O_CCONST (81) -> (1B90)
1BE2 :: unk 04
1BE3 :: O_LLOAD (8)
1BE5 :: O_INIT_CALL
1BE6 :: O_LLOAD (7)
1BE8 :: O_CALL
1BE9 :: O_LLOAD (9)
1BEB :: O_BRF_8 -> 1C06
1BF4 :: O_NEWSTR (b'Yes you got the flag! Submit with the input')
1BFD :: O_PRINT
1BFE :: O_BR_8 -> 1C11
1C06 :: unk 00
1C07 :: O_NEWSTR (b'The password is wrong!')
1C10 :: O_PRINT
1C11 :: O_HALT

We can analyze how the code works by keeping track of what is stored at each stack position. First we have a io.stdin lookup:

0: io.stdin

Then it pushes a hex conversion table:

1: hextable

Then a function and a closure:

2: func1 (18B0)
3: func2 (190C)

Then a smaller array:

4: arr

Then it prints Input the flag: and performs: io.stdin.read(io.stdin):

5: user_input

Next it executes func1()

6: func1()

Then it executes func2(#5, #6) which in this case is func2(user_input, func1()):

7: func2(user_input, func1())

It pushes another function:

8: func3 (1B90)

Then calls it on the result of the previous expression:

9: func3(func2(user_input, func1()))

If this returns true, it prints: Yes you got the flag! Submit with the input otherwise it prints: The password is wrong!.

We can use the same disassembler to disassemble func1, func2 and func3:

func1:

0000 :: O_END
0001 :: O_ICONST_B1 (73)
0003 :: O_ICONST_B1 (35)
0005 :: O_ICONST_B1 (18)
0007 :: O_ICONST_B8 (255)
0010 :: O_NEWLIST
0011 :: O_END
0012 :: O_NEWLIST
0013 :: O_ICONST_B8 (210)
001C :: O_LLOAD (0)
001E :: O_INITFOR
001F :: O_END
0020 :: O_ITER_1
0021 :: O_BRF_8 -> 0046
002A :: O_LSTORE (3)
002C :: O_LLOAD (1)
002E :: O_INIT_MC (b'push')
0037 :: O_LLOAD (3)
0039 :: O_LLOAD (2)
003B :: O_BXOR
003C :: O_CALL
003D :: O_POP
003E :: O_BR_8 -> 0020
0046 :: O_PRINT
0047 :: O_ENDFOR
0048 :: O_POP
0049 :: O_LLOAD (1)
004B :: O_RET

Again, we can track the stack frame. First it pushes a size 4 array:

0: [255, 18, 35, 73]

Then an empty array:

1: []

Then a constant:

2: 210

Next it uses a for loop. I ended up half reading the source for how this works and half guessing. Essentially, in YASL you can call O_INITFOR on an iterable and then each call to O_ITER_1 will return the next object or false. This value is also pushed to the stack:

3: val

Then it loads the #1 array and calls O_INIT_MC (which I assume means “initialize method call”).

It computes val ^ 210 and pushes that to the array.

Once the loop is complete, it returns the #1 array.

In python, this could be:

def func1():
  return [210^x for x in [255, 18, 35, 73]]

func2

0000 :: O_END
0001 :: O_NEWLIST
0002 :: O_ICONST_0
0003 :: O_LLOAD (0)
0005 :: O_INITFOR
0006 :: O_END
0007 :: O_ITER_1
0008 :: O_BRF_8 -> 003B
0011 :: O_LSTORE (4)
0013 :: O_LLOAD (2)
0015 :: O_INIT_MC (b'push')
001E :: O_ULOAD (0)
0020 :: O_LLOAD (4)
0022 :: O_GET
0023 :: O_LLOAD (1)
0025 :: O_LLOAD (3)
0027 :: O_ICONST_3
0028 :: O_BAND
0029 :: O_GET
002A :: O_BXOR
002B :: O_CALL
002C :: O_POP
002D :: O_LLOAD (3)
002F :: O_ICONST_1
0030 :: O_ADD
0031 :: O_LSTORE (3)
0033 :: O_BR_8 -> 0007
003B :: O_PRINT
003C :: O_ENDFOR
003D :: O_POP
003E :: O_LLOAD (2)
0040 :: O_RET

Function 2 takes two arguments, and I just assumed these are pushed to the local stack before execution. So we start with:

0: user_input
1: func1() -- [45, 192, 241, 155]

Then an empty list and a constant:

2: []
3: 0

We see another for loop, this time over the user_input array. The value is stored in #4:

4: user_byte

Then we see: #up0[#4]. I assume that O_ULOAD (0) is referencing the hex array from the stack frame above this (athough the index doesn’t match). So this is the equivalent of python ord().

Next we see #1[#3 & 3] which is indexing into the value computed by func1 treating it as a circular buffer. This value is xor’ed with the user byte and pushed to #2.

Finally, #3 is incremented.

In python, we get:

def func2(user_input, arr):
  i = 0
  out = []
  for c in user_input:
    out.push(ord(c) ^ arr[i&3])
    i += 1
  return out

func3

0000 :: O_LLOAD (0)
0002 :: O_LEN
0003 :: O_ULOAD (0)
0005 :: O_LEN
0006 :: O_EQ
0007 :: O_NOT
0008 :: O_BRF_8 -> 0012
0011 :: O_BCONST_F
0012 :: O_RET
0013 :: O_ICONST_0
0014 :: O_LLOAD (0)
0016 :: O_INITFOR
0017 :: O_END
0018 :: O_ITER_1
0019 :: O_BRF_8 -> 0046
0022 :: O_LSTORE (2)
0024 :: O_LLOAD (2)
0026 :: O_ULOAD (0)
0028 :: O_LLOAD (1)
002A :: O_GET
002B :: O_EQ
002C :: O_NOT
002D :: O_BRF_8 -> 0037
0036 :: O_BCONST_F
0037 :: O_RET
0038 :: O_LLOAD (1)
003A :: O_ICONST_1
003B :: O_ADD
003C :: O_LSTORE (1)
003E :: O_BR_8 -> 0018
0046 :: O_PRINT
0047 :: O_ENDFOR
0048 :: O_POP
0049 :: O_BCONST_T
004A :: O_RET

We start with a single array argument:

0: func2(...)

Then we compute the length of this array and the length of an array in the stack frame above. I assume this is the #4 array. These need to match.

Next, another for loop:

1: 0
2: val

Here it checks if val == #up0[#1]. If not, it fails. Then it increments the counter at #1 and continues.

If all checks pass, it returns true.

So it is clear, to get the flag we can just do:

arr = [203, 158, 179, 72, 242, 149, 175, 67, 224, 184, 159, 69, 244, 129, 165, 114, 226, 158, 181, 114, 255, 152, 164, 67, 239, 174, 178, 30, 237, 148, 243, 95, 232, 148, 159, 89, 243, 152, 179, 114, 249, 132, 180, 114, 238, 130, 165, 73, 196, 144, 159, 94, 242, 149, 165, 114, 248, 153, 161, 67, 245, 148, 172, 114, 250, 133, 180, 25, 248, 154, 189]
key = [155, 241, 192, 45]
print(''.join([chr(x^y) for x,y in zip(arr,v*20)]))
Poseidon{I_hope_you_didnt_r3ve3rse_this_but_used_a_side_channel_att4ck}

Whoops!

VM Reloaded

The next binary was another very similar VM challenge:

0A30 :: O_GLOAD_8 (b'io')
0A39 :: O_NEWSTR (b'stdin')
0A42 :: O_GET
0A43 :: O_END
0A44 :: O_NEWSTR (b'\x00')
0A4D :: O_ICONST_0
0A4E :: O_NEWSTR (b'\x01')
0A57 :: O_ICONST_1
0A58 :: O_NEWSTR (b'\x02')
0A61 :: O_ICONST_2
0A62 :: O_NEWSTR (b'\x03')
0A6B :: O_ICONST_3
0A6C :: O_NEWSTR (b'\x04')
0A75 :: O_ICONST_4
0A76 :: O_NEWSTR (b'\x05')
0A7F :: O_ICONST_5
0A80 :: O_NEWSTR (b'\x06')
0A89 :: O_ICONST_B1 (6)
0A8B :: O_NEWSTR (b'\x07')
0A94 :: O_ICONST_B1 (7)
0A96 :: O_NEWSTR (b'\x08')
0A9F :: O_ICONST_B1 (8)
0AA1 :: O_NEWSTR (b'\t')
0AAA :: O_ICONST_B1 (9)
0AAC :: O_NEWSTR (b'\n')
0AB5 :: O_ICONST_B1 (10)
0AB7 :: O_NEWSTR (b'\x0b')
0AC0 :: O_ICONST_B1 (11)
0AC2 :: O_NEWSTR (b'\x0c')
0ACB :: O_ICONST_B1 (12)
0ACD :: O_NEWSTR (b'\r')
0AD6 :: O_ICONST_B1 (13)
0AD8 :: O_NEWSTR (b'\x0e')
0AE1 :: O_ICONST_B1 (14)
0AE3 :: O_NEWSTR (b'\x0f')
0AEC :: O_ICONST_B1 (15)
0AEE :: O_NEWSTR (b'\x10')
0AF7 :: O_ICONST_B1 (16)
0AF9 :: O_NEWSTR (b'\x11')
0B02 :: O_ICONST_B1 (17)
0B04 :: O_NEWSTR (b'\x12')
0B0D :: O_ICONST_B1 (18)
0B0F :: O_NEWSTR (b'\x13')
0B18 :: O_ICONST_B1 (19)
0B1A :: O_NEWSTR (b'\x14')
0B23 :: O_ICONST_B1 (20)
0B25 :: O_NEWSTR (b'\x15')
0B2E :: O_ICONST_B1 (21)
0B30 :: O_NEWSTR (b'\x16')
0B39 :: O_ICONST_B1 (22)
0B3B :: O_NEWSTR (b'\x17')
0B44 :: O_ICONST_B1 (23)
0B46 :: O_NEWSTR (b'\x18')
0B4F :: O_ICONST_B1 (24)
0B51 :: O_NEWSTR (b'\x19')
0B5A :: O_ICONST_B1 (25)
0B5C :: O_NEWSTR (b'\x1a')
0B65 :: O_ICONST_B1 (26)
0B67 :: O_NEWSTR (b'\x1b')
0B70 :: O_ICONST_B1 (27)
0B72 :: O_NEWSTR (b'\x1c')
0B7B :: O_ICONST_B1 (28)
0B7D :: O_NEWSTR (b'\x1d')
0B86 :: O_ICONST_B1 (29)
0B88 :: O_NEWSTR (b'\x1e')
0B91 :: O_ICONST_B1 (30)
0B93 :: O_NEWSTR (b'\x1f')
0B9C :: O_ICONST_B1 (31)
0B9E :: O_NEWSTR (b' ')
0BA7 :: O_ICONST_B1 (32)
0BA9 :: O_NEWSTR (b'!')
0BB2 :: O_ICONST_B1 (33)
0BB4 :: O_NEWSTR (b'"')
0BBD :: O_ICONST_B1 (34)
0BBF :: O_NEWSTR (b'#')
0BC8 :: O_ICONST_B1 (35)
0BCA :: O_NEWSTR (b'$')
0BD3 :: O_ICONST_B1 (36)
0BD5 :: O_NEWSTR (b'%')
0BDE :: O_ICONST_B1 (37)
0BE0 :: O_NEWSTR (b'&')
0BE9 :: O_ICONST_B1 (38)
0BEB :: O_NEWSTR (b"'")
0BF4 :: O_ICONST_B1 (39)
0BF6 :: O_NEWSTR (b'(')
0BFF :: O_ICONST_B1 (40)
0C01 :: O_NEWSTR (b')')
0C0A :: O_ICONST_B1 (41)
0C0C :: O_NEWSTR (b'*')
0C15 :: O_ICONST_B1 (42)
0C17 :: O_NEWSTR (b'+')
0C20 :: O_ICONST_B1 (43)
0C22 :: O_NEWSTR (b',')
0C2B :: O_ICONST_B1 (44)
0C2D :: O_NEWSTR (b'-')
0C36 :: O_ICONST_B1 (45)
0C38 :: O_NEWSTR (b'.')
0C41 :: O_ICONST_B1 (46)
0C43 :: O_NEWSTR (b'/')
0C4C :: O_ICONST_B1 (47)
0C4E :: O_NEWSTR (b'0')
0C57 :: O_ICONST_B1 (48)
0C59 :: O_NEWSTR (b'1')
0C62 :: O_ICONST_B1 (49)
0C64 :: O_NEWSTR (b'2')
0C6D :: O_ICONST_B1 (50)
0C6F :: O_NEWSTR (b'3')
0C78 :: O_ICONST_B1 (51)
0C7A :: O_NEWSTR (b'4')
0C83 :: O_ICONST_B1 (52)
0C85 :: O_NEWSTR (b'5')
0C8E :: O_ICONST_B1 (53)
0C90 :: O_NEWSTR (b'6')
0C99 :: O_ICONST_B1 (54)
0C9B :: O_NEWSTR (b'7')
0CA4 :: O_ICONST_B1 (55)
0CA6 :: O_NEWSTR (b'8')
0CAF :: O_ICONST_B1 (56)
0CB1 :: O_NEWSTR (b'9')
0CBA :: O_ICONST_B1 (57)
0CBC :: O_NEWSTR (b':')
0CC5 :: O_ICONST_B1 (58)
0CC7 :: O_NEWSTR (b';')
0CD0 :: O_ICONST_B1 (59)
0CD2 :: O_NEWSTR (b'<')
0CDB :: O_ICONST_B1 (60)
0CDD :: O_NEWSTR (b'=')
0CE6 :: O_ICONST_B1 (61)
0CE8 :: O_NEWSTR (b'>')
0CF1 :: O_ICONST_B1 (62)
0CF3 :: O_NEWSTR (b'?')
0CFC :: O_ICONST_B1 (63)
0CFE :: O_NEWSTR (b'@')
0D07 :: O_ICONST_B1 (64)
0D09 :: O_NEWSTR (b'A')
0D12 :: O_ICONST_B1 (65)
0D14 :: O_NEWSTR (b'B')
0D1D :: O_ICONST_B1 (66)
0D1F :: O_NEWSTR (b'C')
0D28 :: O_ICONST_B1 (67)
0D2A :: O_NEWSTR (b'D')
0D33 :: O_ICONST_B1 (68)
0D35 :: O_NEWSTR (b'E')
0D3E :: O_ICONST_B1 (69)
0D40 :: O_NEWSTR (b'F')
0D49 :: O_ICONST_B1 (70)
0D4B :: O_NEWSTR (b'G')
0D54 :: O_ICONST_B1 (71)
0D56 :: O_NEWSTR (b'H')
0D5F :: O_ICONST_B1 (72)
0D61 :: O_NEWSTR (b'I')
0D6A :: O_ICONST_B1 (73)
0D6C :: O_NEWSTR (b'J')
0D75 :: O_ICONST_B1 (74)
0D77 :: O_NEWSTR (b'K')
0D80 :: O_ICONST_B1 (75)
0D82 :: O_NEWSTR (b'L')
0D8B :: O_ICONST_B1 (76)
0D8D :: O_NEWSTR (b'M')
0D96 :: O_ICONST_B1 (77)
0D98 :: O_NEWSTR (b'N')
0DA1 :: O_ICONST_B1 (78)
0DA3 :: O_NEWSTR (b'O')
0DAC :: O_ICONST_B1 (79)
0DAE :: O_NEWSTR (b'P')
0DB7 :: O_ICONST_B1 (80)
0DB9 :: O_NEWSTR (b'Q')
0DC2 :: O_ICONST_B1 (81)
0DC4 :: O_NEWSTR (b'R')
0DCD :: O_ICONST_B1 (82)
0DCF :: O_NEWSTR (b'S')
0DD8 :: O_ICONST_B1 (83)
0DDA :: O_NEWSTR (b'T')
0DE3 :: O_ICONST_B1 (84)
0DE5 :: O_NEWSTR (b'U')
0DEE :: O_ICONST_B1 (85)
0DF0 :: O_NEWSTR (b'V')
0DF9 :: O_ICONST_B1 (86)
0DFB :: O_NEWSTR (b'W')
0E04 :: O_ICONST_B1 (87)
0E06 :: O_NEWSTR (b'X')
0E0F :: O_ICONST_B1 (88)
0E11 :: O_NEWSTR (b'Y')
0E1A :: O_ICONST_B1 (89)
0E1C :: O_NEWSTR (b'Z')
0E25 :: O_ICONST_B1 (90)
0E27 :: O_NEWSTR (b'[')
0E30 :: O_ICONST_B1 (91)
0E32 :: O_NEWSTR (b'\\')
0E3B :: O_ICONST_B1 (92)
0E3D :: O_NEWSTR (b']')
0E46 :: O_ICONST_B1 (93)
0E48 :: O_NEWSTR (b'^')
0E51 :: O_ICONST_B1 (94)
0E53 :: O_NEWSTR (b'_')
0E5C :: O_ICONST_B1 (95)
0E5E :: O_NEWSTR (b'`')
0E67 :: O_ICONST_B1 (96)
0E69 :: O_NEWSTR (b'a')
0E72 :: O_ICONST_B1 (97)
0E74 :: O_NEWSTR (b'b')
0E7D :: O_ICONST_B1 (98)
0E7F :: O_NEWSTR (b'c')
0E88 :: O_ICONST_B1 (99)
0E8A :: O_NEWSTR (b'd')
0E93 :: O_ICONST_B1 (100)
0E95 :: O_NEWSTR (b'e')
0E9E :: O_ICONST_B1 (101)
0EA0 :: O_NEWSTR (b'f')
0EA9 :: O_ICONST_B1 (102)
0EAB :: O_NEWSTR (b'g')
0EB4 :: O_ICONST_B1 (103)
0EB6 :: O_NEWSTR (b'h')
0EBF :: O_ICONST_B1 (104)
0EC1 :: O_NEWSTR (b'i')
0ECA :: O_ICONST_B1 (105)
0ECC :: O_NEWSTR (b'j')
0ED5 :: O_ICONST_B1 (106)
0ED7 :: O_NEWSTR (b'k')
0EE0 :: O_ICONST_B1 (107)
0EE2 :: O_NEWSTR (b'l')
0EEB :: O_ICONST_B1 (108)
0EED :: O_NEWSTR (b'm')
0EF6 :: O_ICONST_B1 (109)
0EF8 :: O_NEWSTR (b'n')
0F01 :: O_ICONST_B1 (110)
0F03 :: O_NEWSTR (b'o')
0F0C :: O_ICONST_B1 (111)
0F0E :: O_NEWSTR (b'p')
0F17 :: O_ICONST_B1 (112)
0F19 :: O_NEWSTR (b'q')
0F22 :: O_ICONST_B1 (113)
0F24 :: O_NEWSTR (b'r')
0F2D :: O_ICONST_B1 (114)
0F2F :: O_NEWSTR (b's')
0F38 :: O_ICONST_B1 (115)
0F3A :: O_NEWSTR (b't')
0F43 :: O_ICONST_B1 (116)
0F45 :: O_NEWSTR (b'u')
0F4E :: O_ICONST_B1 (117)
0F50 :: O_NEWSTR (b'v')
0F59 :: O_ICONST_B1 (118)
0F5B :: O_NEWSTR (b'w')
0F64 :: O_ICONST_B1 (119)
0F66 :: O_NEWSTR (b'x')
0F6F :: O_ICONST_B1 (120)
0F71 :: O_NEWSTR (b'y')
0F7A :: O_ICONST_B1 (121)
0F7C :: O_NEWSTR (b'z')
0F85 :: O_ICONST_B1 (122)
0F87 :: O_NEWSTR (b'{')
0F90 :: O_ICONST_B1 (123)
0F92 :: O_NEWSTR (b'|')
0F9B :: O_ICONST_B1 (124)
0F9D :: O_NEWSTR (b'}')
0FA6 :: O_ICONST_B1 (125)
0FA8 :: O_NEWSTR (b'~')
0FB1 :: O_ICONST_B1 (126)
0FB3 :: O_NEWSTR (b'\x7f')
0FBC :: O_ICONST_B1 (127)
0FBE :: O_NEWSTR (b'\x80')
0FC7 :: O_ICONST_B8 (128)
0FD0 :: O_NEWSTR (b'\x81')
0FD9 :: O_ICONST_B8 (129)
0FE2 :: O_NEWSTR (b'\x82')
0FEB :: O_ICONST_B8 (130)
0FF4 :: O_NEWSTR (b'\x83')
0FFD :: O_ICONST_B8 (131)
1006 :: O_NEWSTR (b'\x84')
100F :: O_ICONST_B8 (132)
1018 :: O_NEWSTR (b'\x85')
1021 :: O_ICONST_B8 (133)
102A :: O_NEWSTR (b'\x86')
1033 :: O_ICONST_B8 (134)
103C :: O_NEWSTR (b'\x87')
1045 :: O_ICONST_B8 (135)
104E :: O_NEWSTR (b'\x88')
1057 :: O_ICONST_B8 (136)
1060 :: O_NEWSTR (b'\x89')
1069 :: O_ICONST_B8 (137)
1072 :: O_NEWSTR (b'\x8a')
107B :: O_ICONST_B8 (138)
1084 :: O_NEWSTR (b'\x8b')
108D :: O_ICONST_B8 (139)
1096 :: O_NEWSTR (b'\x8c')
109F :: O_ICONST_B8 (140)
10A8 :: O_NEWSTR (b'\x8d')
10B1 :: O_ICONST_B8 (141)
10BA :: O_NEWSTR (b'\x8e')
10C3 :: O_ICONST_B8 (142)
10CC :: O_NEWSTR (b'\x8f')
10D5 :: O_ICONST_B8 (143)
10DE :: O_NEWSTR (b'\x90')
10E7 :: O_ICONST_B8 (144)
10F0 :: O_NEWSTR (b'\x91')
10F9 :: O_ICONST_B8 (145)
1102 :: O_NEWSTR (b'\x92')
110B :: O_ICONST_B8 (146)
1114 :: O_NEWSTR (b'\x93')
111D :: O_ICONST_B8 (147)
1126 :: O_NEWSTR (b'\x94')
112F :: O_ICONST_B8 (148)
1138 :: O_NEWSTR (b'\x95')
1141 :: O_ICONST_B8 (149)
114A :: O_NEWSTR (b'\x96')
1153 :: O_ICONST_B8 (150)
115C :: O_NEWSTR (b'\x97')
1165 :: O_ICONST_B8 (151)
116E :: O_NEWSTR (b'\x98')
1177 :: O_ICONST_B8 (152)
1180 :: O_NEWSTR (b'\x99')
1189 :: O_ICONST_B8 (153)
1192 :: O_NEWSTR (b'\x9a')
119B :: O_ICONST_B8 (154)
11A4 :: O_NEWSTR (b'\x9b')
11AD :: O_ICONST_B8 (155)
11B6 :: O_NEWSTR (b'\x9c')
11BF :: O_ICONST_B8 (156)
11C8 :: O_NEWSTR (b'\x9d')
11D1 :: O_ICONST_B8 (157)
11DA :: O_NEWSTR (b'\x9e')
11E3 :: O_ICONST_B8 (158)
11EC :: O_NEWSTR (b'\x9f')
11F5 :: O_ICONST_B8 (159)
11FE :: O_NEWSTR (b'\xa0')
1207 :: O_ICONST_B8 (160)
1210 :: O_NEWSTR (b'\xa1')
1219 :: O_ICONST_B8 (161)
1222 :: O_NEWSTR (b'\xa2')
122B :: O_ICONST_B8 (162)
1234 :: O_NEWSTR (b'\xa3')
123D :: O_ICONST_B8 (163)
1246 :: O_NEWSTR (b'\xa4')
124F :: O_ICONST_B8 (164)
1258 :: O_NEWSTR (b'\xa5')
1261 :: O_ICONST_B8 (165)
126A :: O_NEWSTR (b'\xa6')
1273 :: O_ICONST_B8 (166)
127C :: O_NEWSTR (b'\xa7')
1285 :: O_ICONST_B8 (167)
128E :: O_NEWSTR (b'\xa8')
1297 :: O_ICONST_B8 (168)
12A0 :: O_NEWSTR (b'\xa9')
12A9 :: O_ICONST_B8 (169)
12B2 :: O_NEWSTR (b'\xaa')
12BB :: O_ICONST_B8 (170)
12C4 :: O_NEWSTR (b'\xab')
12CD :: O_ICONST_B8 (171)
12D6 :: O_NEWSTR (b'\xac')
12DF :: O_ICONST_B8 (172)
12E8 :: O_NEWSTR (b'\xad')
12F1 :: O_ICONST_B8 (173)
12FA :: O_NEWSTR (b'\xae')
1303 :: O_ICONST_B8 (174)
130C :: O_NEWSTR (b'\xaf')
1315 :: O_ICONST_B8 (175)
131E :: O_NEWSTR (b'\xb0')
1327 :: O_ICONST_B8 (176)
1330 :: O_NEWSTR (b'\xb1')
1339 :: O_ICONST_B8 (177)
1342 :: O_NEWSTR (b'\xb2')
134B :: O_ICONST_B8 (178)
1354 :: O_NEWSTR (b'\xb3')
135D :: O_ICONST_B8 (179)
1366 :: O_NEWSTR (b'\xb4')
136F :: O_ICONST_B8 (180)
1378 :: O_NEWSTR (b'\xb5')
1381 :: O_ICONST_B8 (181)
138A :: O_NEWSTR (b'\xb6')
1393 :: O_ICONST_B8 (182)
139C :: O_NEWSTR (b'\xb7')
13A5 :: O_ICONST_B8 (183)
13AE :: O_NEWSTR (b'\xb8')
13B7 :: O_ICONST_B8 (184)
13C0 :: O_NEWSTR (b'\xb9')
13C9 :: O_ICONST_B8 (185)
13D2 :: O_NEWSTR (b'\xba')
13DB :: O_ICONST_B8 (186)
13E4 :: O_NEWSTR (b'\xbb')
13ED :: O_ICONST_B8 (187)
13F6 :: O_NEWSTR (b'\xbc')
13FF :: O_ICONST_B8 (188)
1408 :: O_NEWSTR (b'\xbd')
1411 :: O_ICONST_B8 (189)
141A :: O_NEWSTR (b'\xbe')
1423 :: O_ICONST_B8 (190)
142C :: O_NEWSTR (b'\xbf')
1435 :: O_ICONST_B8 (191)
143E :: O_NEWSTR (b'\xc0')
1447 :: O_ICONST_B8 (192)
1450 :: O_NEWSTR (b'\xc1')
1459 :: O_ICONST_B8 (193)
1462 :: O_NEWSTR (b'\xc2')
146B :: O_ICONST_B8 (194)
1474 :: O_NEWSTR (b'\xc3')
147D :: O_ICONST_B8 (195)
1486 :: O_NEWSTR (b'\xc4')
148F :: O_ICONST_B8 (196)
1498 :: O_NEWSTR (b'\xc5')
14A1 :: O_ICONST_B8 (197)
14AA :: O_NEWSTR (b'\xc6')
14B3 :: O_ICONST_B8 (198)
14BC :: O_NEWSTR (b'\xc7')
14C5 :: O_ICONST_B8 (199)
14CE :: O_NEWSTR (b'\xc8')
14D7 :: O_ICONST_B8 (200)
14E0 :: O_NEWSTR (b'\xc9')
14E9 :: O_ICONST_B8 (201)
14F2 :: O_NEWSTR (b'\xca')
14FB :: O_ICONST_B8 (202)
1504 :: O_NEWSTR (b'\xcb')
150D :: O_ICONST_B8 (203)
1516 :: O_NEWSTR (b'\xcc')
151F :: O_ICONST_B8 (204)
1528 :: O_NEWSTR (b'\xcd')
1531 :: O_ICONST_B8 (205)
153A :: O_NEWSTR (b'\xce')
1543 :: O_ICONST_B8 (206)
154C :: O_NEWSTR (b'\xcf')
1555 :: O_ICONST_B8 (207)
155E :: O_NEWSTR (b'\xd0')
1567 :: O_ICONST_B8 (208)
1570 :: O_NEWSTR (b'\xd1')
1579 :: O_ICONST_B8 (209)
1582 :: O_NEWSTR (b'\xd2')
158B :: O_ICONST_B8 (210)
1594 :: O_NEWSTR (b'\xd3')
159D :: O_ICONST_B8 (211)
15A6 :: O_NEWSTR (b'\xd4')
15AF :: O_ICONST_B8 (212)
15B8 :: O_NEWSTR (b'\xd5')
15C1 :: O_ICONST_B8 (213)
15CA :: O_NEWSTR (b'\xd6')
15D3 :: O_ICONST_B8 (214)
15DC :: O_NEWSTR (b'\xd7')
15E5 :: O_ICONST_B8 (215)
15EE :: O_NEWSTR (b'\xd8')
15F7 :: O_ICONST_B8 (216)
1600 :: O_NEWSTR (b'\xd9')
1609 :: O_ICONST_B8 (217)
1612 :: O_NEWSTR (b'\xda')
161B :: O_ICONST_B8 (218)
1624 :: O_NEWSTR (b'\xdb')
162D :: O_ICONST_B8 (219)
1636 :: O_NEWSTR (b'\xdc')
163F :: O_ICONST_B8 (220)
1648 :: O_NEWSTR (b'\xdd')
1651 :: O_ICONST_B8 (221)
165A :: O_NEWSTR (b'\xde')
1663 :: O_ICONST_B8 (222)
166C :: O_NEWSTR (b'\xdf')
1675 :: O_ICONST_B8 (223)
167E :: O_NEWSTR (b'\xe0')
1687 :: O_ICONST_B8 (224)
1690 :: O_NEWSTR (b'\xe1')
1699 :: O_ICONST_B8 (225)
16A2 :: O_NEWSTR (b'\xe2')
16AB :: O_ICONST_B8 (226)
16B4 :: O_NEWSTR (b'\xe3')
16BD :: O_ICONST_B8 (227)
16C6 :: O_NEWSTR (b'\xe4')
16CF :: O_ICONST_B8 (228)
16D8 :: O_NEWSTR (b'\xe5')
16E1 :: O_ICONST_B8 (229)
16EA :: O_NEWSTR (b'\xe6')
16F3 :: O_ICONST_B8 (230)
16FC :: O_NEWSTR (b'\xe7')
1705 :: O_ICONST_B8 (231)
170E :: O_NEWSTR (b'\xe8')
1717 :: O_ICONST_B8 (232)
1720 :: O_NEWSTR (b'\xe9')
1729 :: O_ICONST_B8 (233)
1732 :: O_NEWSTR (b'\xea')
173B :: O_ICONST_B8 (234)
1744 :: O_NEWSTR (b'\xeb')
174D :: O_ICONST_B8 (235)
1756 :: O_NEWSTR (b'\xec')
175F :: O_ICONST_B8 (236)
1768 :: O_NEWSTR (b'\xed')
1771 :: O_ICONST_B8 (237)
177A :: O_NEWSTR (b'\xee')
1783 :: O_ICONST_B8 (238)
178C :: O_NEWSTR (b'\xef')
1795 :: O_ICONST_B8 (239)
179E :: O_NEWSTR (b'\xf0')
17A7 :: O_ICONST_B8 (240)
17B0 :: O_NEWSTR (b'\xf1')
17B9 :: O_ICONST_B8 (241)
17C2 :: O_NEWSTR (b'\xf2')
17CB :: O_ICONST_B8 (242)
17D4 :: O_NEWSTR (b'\xf3')
17DD :: O_ICONST_B8 (243)
17E6 :: O_NEWSTR (b'\xf4')
17EF :: O_ICONST_B8 (244)
17F8 :: O_NEWSTR (b'\xf5')
1801 :: O_ICONST_B8 (245)
180A :: O_NEWSTR (b'\xf6')
1813 :: O_ICONST_B8 (246)
181C :: O_NEWSTR (b'\xf7')
1825 :: O_ICONST_B8 (247)
182E :: O_NEWSTR (b'\xf8')
1837 :: O_ICONST_B8 (248)
1840 :: O_NEWSTR (b'\xf9')
1849 :: O_ICONST_B8 (249)
1852 :: O_NEWSTR (b'\xfa')
185B :: O_ICONST_B8 (250)
1864 :: O_NEWSTR (b'\xfb')
186D :: O_ICONST_B8 (251)
1876 :: O_NEWSTR (b'\xfc')
187F :: O_ICONST_B8 (252)
1888 :: O_NEWSTR (b'\xfd')
1891 :: O_ICONST_B8 (253)
189A :: O_NEWSTR (b'\xfe')
18A3 :: O_ICONST_B8 (254)
18AC :: O_NEWSTR (b'\xff')
18B5 :: O_ICONST_B8 (255)
18BE :: O_NEWTABLE
18BF :: O_NEWSTR (b'Welcome to VM Reloaded')
18C8 :: O_PRINT
18C9 :: O_NEWSTR (b'Input the flag: ')
18D2 :: O_PRINT
18D3 :: O_LLOAD (0)
18D5 :: O_NEWSTR (b'read')
18DE :: O_GET
18DF :: O_INIT_CALL
18E0 :: O_LLOAD (0)
18E2 :: O_CALL
18E3 :: O_END
18E4 :: O_NEWLIST
18E5 :: O_LLOAD (2)
18E7 :: O_INITFOR
18E8 :: O_END
18E9 :: O_ITER_1
18EA :: O_BRF_8 -> 190F
18F3 :: O_LSTORE (4)
18F5 :: O_LLOAD (3)
18F7 :: O_INIT_MC (b'push')
1900 :: O_LLOAD (1)
1902 :: O_LLOAD (4)
1904 :: O_GET
1905 :: O_CALL
1906 :: O_POP
1907 :: O_BR_8 -> 18E9

190F :: O_PRINT
1910 :: O_ENDFOR
1911 :: O_LLOAD (3)
1913 :: O_LEN
1914 :: O_ICONST_B1 (65)
1916 :: O_EQ
1917 :: O_NOT
1918 :: O_BRF_8 -> 1933
1921 :: O_NEWSTR (b'The flag is wrong')
192A :: O_PRINT
192B :: O_BR_8 -> 2C7B

1933 :: unk 00
1934 :: O_LLOAD (3)
1936 :: O_ICONST_0
1937 :: O_GET
1938 :: O_LLOAD (3)
193A :: O_ICONST_B1 (57)
193C :: O_GET
193D :: O_BXOR
193E :: O_LLOAD (3)
1940 :: O_ICONST_0
1941 :: O_GET
1942 :: O_LLOAD (3)
1944 :: O_ICONST_B1 (57)
1946 :: O_GET
1947 :: O_BAND
1948 :: O_ICONST_1
1949 :: O_BSL
194A :: O_ADD
194B :: O_ICONST_B8 (174)
1954 :: O_BOR
1955 :: O_LLOAD (3)
1957 :: O_ICONST_0
1958 :: O_GET
1959 :: O_LLOAD (3)
195B :: O_ICONST_B1 (57)
195D :: O_GET
195E :: O_BXOR
195F :: O_LLOAD (3)
1961 :: O_ICONST_0
1962 :: O_GET
1963 :: O_LLOAD (3)
1965 :: O_ICONST_B1 (57)
1967 :: O_GET
1968 :: O_BAND
1969 :: O_ICONST_1
196A :: O_BSL
196B :: O_ADD
196C :: O_ICONST_B8 (174)
1975 :: O_BAND
1976 :: O_ADD
1977 :: O_ICONST_B8 (255)
1980 :: O_BAND
1981 :: O_ICONST_B1 (84)
1983 :: O_EQ
1984 :: O_DUP
1985 :: O_BRF_8 -> 2C4A
198E :: O_POP
198F :: O_LLOAD (3)
1991 :: O_ICONST_1
1992 :: O_GET
1993 :: O_LLOAD (3)
1995 :: O_ICONST_B1 (61)
...

2C7B :: O_HALT

This bytecode is a bit bulkier.

It sets up a hextable like previously and reads input the same way. And then it starts doing a bunch of checks on the flag:

First it checks if the length is 65. Then it starts comparing bytes. For example, here is one logical block:

1934 :: O_LLOAD (3)
1936 :: O_ICONST_0
1937 :: O_GET
1938 :: O_LLOAD (3)
193A :: O_ICONST_B1 (57)
193C :: O_GET
193D :: O_BXOR
193E :: O_LLOAD (3)
1940 :: O_ICONST_0
1941 :: O_GET
1942 :: O_LLOAD (3)
1944 :: O_ICONST_B1 (57)
1946 :: O_GET
1947 :: O_BAND
1948 :: O_ICONST_1
1949 :: O_BSL
194A :: O_ADD
194B :: O_ICONST_B8 (174)
1954 :: O_BOR
1955 :: O_LLOAD (3)
1957 :: O_ICONST_0
1958 :: O_GET
1959 :: O_LLOAD (3)
195B :: O_ICONST_B1 (57)
195D :: O_GET
195E :: O_BXOR
195F :: O_LLOAD (3)
1961 :: O_ICONST_0
1962 :: O_GET
1963 :: O_LLOAD (3)
1965 :: O_ICONST_B1 (57)
1967 :: O_GET
1968 :: O_BAND
1969 :: O_ICONST_1
196A :: O_BSL
196B :: O_ADD
196C :: O_ICONST_B8 (174)
1975 :: O_BAND
1976 :: O_ADD
1977 :: O_ICONST_B8 (255)
1980 :: O_BAND
1981 :: O_ICONST_B1 (84)
1983 :: O_EQ
1984 :: O_DUP
1985 :: O_BRF_8 -> 2C4A

It is computing:

a = ((d[0] ^ d[57]) + ((d[0] & d[57]) << 1)) | 174
b = ((d[0] ^ d[57]) + ((d[0] & d[57]) << 1)) & 174
if (a + b) & 0xff != 84:
  return False

My first attempt was to see if all of these blocks follow the same pattern. If so, it would be simple to plug into a SAT solver like z3 and get a solution.

Unfortunately, the blocks vary quite a bit and doing it by hand would be really painful.

So I wrote a symbolic executor in z3 to automatically pull out the expressions. This runs on the dissasembly printout generated by disasm:

stack = []

d = [BitVec('d%d' % i, 8) for i in range(65)]
s = Solver()

for line in pat:
    t = line.split(':: ')[1]
    op = t.split(' ')[0]
    
    if op == 'O_LLOAD':
        stack.append(d)
    elif op in ['O_ICONST_0', 'O_ICONST_1', 'O_ICONST_2', 'O_ICONST_3', 'O_ICONST_4', 'O_ICONST_5']:
        stack.append(int(op.split('CONST_')[1]))
    elif op == 'O_GET':
        idx = stack.pop(-1)
        arr = stack.pop(-1)
        stack.append(arr[idx])
    elif op in ['O_ICONST_B8', 'O_ICONST_B1']:
        stack.append(int(t.split('(')[1][:-1]))
    elif op == 'O_ADD':
        a = stack.pop(-1)
        b = stack.pop(-1)
        stack.append(a+b)
    elif op == 'O_BAND':
        a = stack.pop(-1)
        b = stack.pop(-1)
        stack.append(a&b)
    elif op == 'O_BXOR':
        a = stack.pop(-1)
        b = stack.pop(-1)
        stack.append(a^b)
    elif op == 'O_BOR':
        a = stack.pop(-1)
        b = stack.pop(-1)
        stack.append(a|b)
    elif op == 'O_MUL':
        a = stack.pop(-1)
        b = stack.pop(-1)
        stack.append(a*b)
    elif op == 'O_BSL':
        shamt = stack.pop(-1)
        v = stack.pop(-1)
        stack.append(v << shamt)
    elif op == 'O_POP':
        stack.pop(-1)
    elif op == 'O_DUP':
        stack.append(stack[-1])
    elif op == 'O_BRF_8':
        v = stack.pop(-1)
        s.add(v == 0)
    elif op == 'O_EQ':
        a = stack.pop(-1)
        b = stack.pop(-1)
        stack.append(a^b)
    else:
        print('unk', t)
        break
        
print(s.check())
m = s.model()
print(''.join([chr(m[d[i]].as_long()) for i in range(len(d))]))

We can simplify some components. For instance, in this bytecode section O_LLOAD only ever loads the flag array onto the stack. And here we want to avoid every O_BRF_8 jump, so we can assert that the previous comparison has to be zero.

This quickly prints the flag:

Poseidon{yay_you_simplified_the_mba_expressi0ns_from_the_VM_c0de}

Conclusion

Overall I thought this was a fun challenge. I’m not sure why it didn’t have any other solves, perhaps finding YASL was a barrier for some.

It was definitely interesting trying to reverse a more complete bytecode language than the smaller custom kinds you usually see in a ctf challenge.

comments powered by Disqus