2019 X-MAS CTF Discount VMProtect

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  printf("Enter password: ", a2, a3);
  memset(s, 0, 256uLL);
  fgets(s, 255, stdin);
  if ( s[0] )
    s[strlen(s) - 1] = 0;
  sub_400857();
  sub_400857();
  if ( byte_6025A0[0] == 1 )
    puts("Yay, you got the flag!");
  else
    puts("NOOOOOOOOOOOOOOO!");
  return 0LL;
}

VM Create

0x2361ca를 스택으로 푸시 한 다음 0x636465로 XOR한다. XOR 후의 값은 0x40087f이고 가상화 코드들이 존재한다.

.text:0000000000400857 sub_400857 proc near                    ; CODE XREF: main+76↓p
.text:0000000000400857                                         ; main+82↓p
.text:0000000000400857
.text:0000000000400857 var_38= qword ptr -38h
.text:0000000000400857 var_28= qword ptr -28h
.text:0000000000400857 var_8= dword ptr -8
.text:0000000000400857 var_4= dword ptr -4
.text:0000000000400857
.text:0000000000400857 ; __unwind {
.text:0000000000400857 push    rbp
.text:0000000000400858 mov     rbp, rsp
.text:000000000040085B sub     rsp, 30h
.text:000000000040085F mov     [rbp+var_28], rdi
.text:0000000000400863 mov     [rbp+var_8], 0
.text:000000000040086A mov     [rbp+var_4], 0
.text:0000000000400871 push    236C1Ah
.text:0000000000400876 xor     [rsp+38h+var_38], 636465h
.text:000000000040087E retn
.text:000000000040087E sub_400857 endp ; sp-analysis failed

stack을 이용해 값들을 연산한다.

// jumptable 00000000004008C3 default case
signed __int64 __usercall sub_40087F@<rax>(__int64 a1@<rbp>)
{
  int v1; // eax

  while ( 1 )
  {
    *(_BYTE *)(a1 - 23) = *(_BYTE *)((signed int)(*(_DWORD *)(a1 - 4))++ + *(_QWORD *)(a1 - 40));
    v1 = *(unsigned __int8 *)(a1 - 23);
    switch ( (unsigned int)off_400E38 )
    {
      case 0x30u:
        return 1LL;
      case 0x31u:
        *(_BYTE *)(a1 - 9) = *(_BYTE *)((signed int)(*(_DWORD *)(a1 - 4))++ + *(_QWORD *)(a1 - 40));
        *(_BYTE *)(a1 - 9) = byte_6025A0[*(unsigned __int8 *)(a1 - 9)];
        s[(*(_DWORD *)(a1 - 8))++ + 256] = *(_BYTE *)(a1 - 9);
        break;
      case 0x32u:
        s[*(signed int *)(a1 - 8) + 256] = s[*(_DWORD *)(a1 - 8) + 255];
        ++*(_DWORD *)(a1 - 8);
        break;
      case 0x33u:
        s[*(_DWORD *)(a1 - 8) + 255] = s[(unsigned __int8)s[*(_DWORD *)(a1 - 8) + 255]];
        break;
      case 0x34u:
        *(_BYTE *)(a1 - 10) = *(_BYTE *)((signed int)(*(_DWORD *)(a1 - 4))++ + *(_QWORD *)(a1 - 40));
        if ( !s[*(_DWORD *)(a1 - 8) + 255] )
          *(_DWORD *)(a1 - 4) = *(unsigned __int8 *)(a1 - 10);
        --*(_DWORD *)(a1 - 8);
        break;
      case 0x35u:
        *(_BYTE *)(a1 - 11) = s[*(_DWORD *)(a1 - 8) + 255];
        *(_BYTE *)(a1 - 11) = (*(_BYTE *)(a1 - 11) << 7) | ((signed int)*(unsigned __int8 *)(a1 - 11) >> 1);
        s[*(_DWORD *)(a1 - 8) + 255] = *(_BYTE *)(a1 - 11);
        break;
      case 0x36u:
        *(_BYTE *)(a1 - 12) = *(_BYTE *)((signed int)(*(_DWORD *)(a1 - 4))++ + *(_QWORD *)(a1 - 40));
        s[(*(_DWORD *)(a1 - 8))++ + 256] = *(_BYTE *)(a1 - 12);
        break;
      case 0x37u:
        *(_BYTE *)(a1 - 14) = s[*(_DWORD *)(a1 - 8) + 255];
        *(_BYTE *)(a1 - 13) = s[*(_DWORD *)(a1 - 8) + 254];
        *(_BYTE *)(a1 - 14) ^= *(_BYTE *)(a1 - 13);
        s[(*(_DWORD *)(a1 - 8))-- + 254] = *(_BYTE *)(a1 - 14);
        break;
      case 0x38u:
        *(_BYTE *)(a1 - 16) = s[*(_DWORD *)(a1 - 8) + 255];
        *(_BYTE *)(a1 - 15) = s[*(_DWORD *)(a1 - 8) + 254];
        *(_BYTE *)(a1 - 16) += *(_BYTE *)(a1 - 15);
        s[(*(_DWORD *)(a1 - 8))-- + 254] = *(_BYTE *)(a1 - 16);
        break;
      case 0x39u:
        *(_BYTE *)(a1 - 18) = s[*(_DWORD *)(a1 - 8) + 255];
        *(_BYTE *)(a1 - 17) = s[*(_DWORD *)(a1 - 8) + 254];
        *(_BYTE *)(a1 - 18) = *(_BYTE *)(a1 - 17) - *(_BYTE *)(a1 - 18);
        s[(*(_DWORD *)(a1 - 8))-- + 254] = *(_BYTE *)(a1 - 18);
        break;
      case 0x61u:
        *(_BYTE *)(a1 - 19) = s[*(_DWORD *)(a1 - 8) + 255];
        *(_BYTE *)(a1 - 19) = ~*(_BYTE *)(a1 - 19);
        s[*(_DWORD *)(a1 - 8) + 255] = *(_BYTE *)(a1 - 19);
        break;
      case 0x62u:
        if ( dword_602580 == 27 && ptrace(0, 0LL, 1LL, 0LL) == -1 )
        {
          puts("NOOOOOOOOOOOOOOO!");
          exit(0);
        }
        ++dword_602580;
        break;
      case 0x63u:
        byte_6025A0[0] = 1;
        strcpy(dest, &src);
        break;
      case 0x64u:
        *(_BYTE *)(a1 - 21) = *(_BYTE *)((signed int)(*(_DWORD *)(a1 - 4))++ + *(_QWORD *)(a1 - 40));
        *(_BYTE *)(a1 - 20) = s[(*(_DWORD *)(a1 - 8))-- + 255];
        byte_6025A0[*(unsigned __int8 *)(a1 - 21)] = *(_BYTE *)(a1 - 20);
        break;
      case 0x65u:
        *(_BYTE *)(a1 - 22) = s[*(_DWORD *)(a1 - 8) + 255];
        *(_BYTE *)(a1 - 22) = byte_6025A0[*(unsigned __int8 *)(a1 - 22)];
        s[*(_DWORD *)(a1 - 8) + 255] = *(_BYTE *)(a1 - 22);
        break;
      default:
        continue;
    }
  }
}

structure

pc = 0 # a1-4
rbp_8 = 0 # a1-8
state = [] # a1-40 -> unk = bytes code
register = [0]*256
s = [0]*1280
dword_602580 = 0
# s = input
def case48(): # exit
	print 'return 1;'

def case49(): # push register
	# global pc; global rbp_8; global state; global
	a = state[pc]
	pc += 1
	b = regsiter[a]
	s[rbp_8 + 256] = b & 0xff
	rbp_8 += 1
	print '[{}] s[rbp_8 + 256] = register[{}]'.format(pc,a)
	print '[{}] rbp_8++'.format(pc)

def case50():
	s[rbp_8 + 256] = s[rbp_8 + 255]
	rbp_8 += 1
	print '[{}] s[rbp_8 + 256] = s[rbp_8 + 255]'.format(pc)

def case51():
	s[rbp_8 + 255] = s[s[rbp_8 + 255]]
	print '[{}] s[rbp_8 + 255] = s[s[rbp_8 + 255]]'.format(pc)

def case52():
	a = state[pc]
	pc += 1
	if s[rbp_8 + 255] == 0:
		pc = a
		rbp_8 -= 1
	print '[{}] if s[rbp_8+255] == 0: pc = state[{}]'.format(pc,pc-1)
	print '[{}] rbp_8--'.format(pc)

def case53():
	a = s[rbp_8 + 255]
	b = (a <<  7) | (a >> 1)
	s[rbp_8 + 255] = b & 0xff
	print '[{}] s[rbp_8 + 255] = (s[rbp_8 + 255] << 7) | (s[rbp_8 + 255] >> 1)'.format(pc)

def case54():
	a = state[pc]
	pc += 1
	s[rbp_8 + 256] = a & 0xff
	rbp_8 += 1
	print '[{}] s[rbp_8 + 256] = state[{}]'.format(pc,pc-1)
	print '[{}] rbp_8++'.format(pc)

def case55():
	a = s[rbp_8 + 255]
	b = s[rbp_8 + 254]
	a ^= b
	s[rbp_8 + 254] = a & 0xff
	rbp_8 -= 1
	print '[{}] s[rbp_8 + 254] ^= s[rbp_8 + 255]'.format(pc)
	print '[{}] rbp_8--'.format(pc)

def case56():
	a = s[rbp_8 + 255]
	b = s[rbp_8 + 254]
	a += b
	s[rbp_8 + 254] = a & 0xff
	rbp_8 -= 1
	print '[{}] s[rbp_8 + 254] += s[rbp_8 + 255]'.format(pc)
	print '[{}] rbp_8--'.format(pc)

def case57():
	a = s[rbp_8 + 255]
	b = s[rbp_8 + 254]
	a = b - a
	s[rbp_8 + 254] = a & 0xff
	rbp_8 -= 1
	print '[{}] s[rbp_8 + 254] -= s[rbp_8 + 255]'.format(pc)
	print '[{}] rbp_8--'.format(pc)

def case97():
	a = s[rbp_8 + 255]
	a = ~a
	s[rbp_8 + 255] = a & 0xff
	print '[{}] s[rbp_8 + 255] = ~s[rbp_8 + 255]'.format(pc)

def case98():
	global dword_602580
	dword_602580 += 1
	print 'Anti DEBUG'
	print '[{}] dword_602580++'.format(pc)

def case99():
	register[0] = 1
	print 'strcpy'

def case100():
	a = state[pc]
	pc += 1
	b = s[rbp_8 + 255]
	rbp_8 -= 1
	register[a] = b & 0xff
	print '[{}] register[state[{}]] = s[rbp_8 + 255]'.format(pc.pc-1)
	print '[{}] rbp_8--'.format(pc)

def case101():
	a = s[rbp_8 + 255]
	b = register[a]
	s[rbp_8 + 255] = b & 0xff
	print '[{}] s[rbp_8 + 255] = register[s[rbp_8 + 255]]'.format(pc)

src로 strcpy해주고 연산들 해준다. 가상화 코드를 애뮬레이팅하면 아래와 같은식으로 나온다.

src = [0x18,0x72,0xa2,0xa4,0x9d,0x89,0x1f,0xa2,0x8d,0x9b,0x94,0x0d,0x6d,0x9b,0x95,0xec,0xec,0x12,0x9b,0x94,0x23,0x16,0x9b,0x6c,0x13,0x0e,0x6d,
0x0d,0x96,0x8d,0x0e,0x90,0x13,0x97,0x8a,0xbb,0xcf,0x64,0x7e,0xd3,0x1a,0x40,0x23,0xec,0xdf]
byte_6025A0[0] = 1
inpt = raw_input()
for i in range(35):
	a = inpt[i]
	a = ((a << 7) | (a >> 1))
	a = a & 0xff
	a ^= 99 
	a += 152
	a = a & 0xff
	a = ~a
	a = a & 0xff
	if a != src[i]:
		byte_6025A0[0] = 0
		break
if byte_6025A0[0] == 1:
	print "Yay, you got the flag!"
else:
	print "NOOOOOOOOOOOOOOO!"

BruteForce

solve.py

src = [0x18,0x72,0xa2,0xa4,0x9d,0x89,0x1f,0xa2,0x8d,0x9b,0x94,0x0d,0x6d,0x9b,0x95,0xec,0xec,0x12,0x9b,0x94,0x23,0x16,0x9b,0x6c,0x13,0x0e,0x6d,
0x0d,0x96,0x8d,0x0e,0x90,0x13,0x97,0x8a,0xbb,0xcf,0x64,0x7e,0xd3,0x1a,0x40,0x23,0xec,0xdf]
flag = ''
for i in range(len(src)):
	for j in range(128):
		a = ((j << 7) | (j >> 1))
		a &= 0xff
		a ^= 99
		a += 152
		a &= 0xff
		a = ~a
		a &= 0xff
		if a == src[i]:
			flag += chr(j)
			break
print flag

FLAG : X-MAS{VMs_ar3_c00l_aNd_1nt3resting}