Boston Key Party CTF 2014 – Reversing 300 – VM

Bài này cũng khá lâu rồi, hôm trước có bạn muốn xem lại nên mình post lại vào đây.

this vm needs a license to run. We don’t have the license!

The executable file is a simple vm engine.

vm-2fbed3f5a894d56be6b2ba328f9e2411: ELF 64-bit LSB executable, x86-64, version1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped

The program copies the encrypted vm code to local buffer in main() function and passes the pointer to this buffer to vm Engine.

        push    rbp
        mov     rbp, rsp
        sub     rsp, 3B0h               ; 
        lea     rsi, vm_code            ; vm_code_buffer
        mov     rdx, 390h               ; vm_code_size
        lea     rax, [rbp+dest]         ; 
        mov     [rbp+var_4], 0
        mov     [rbp+var_C], 0
        mov     rdi, rax                ; 
        call    _memcpy                 ; 
        lea     rdi, [rbp+dest]         ; 
        call    ExecuteVm               ; 
        mov     [rbp+var_3A4], eax
        cmp     [rbp+var_3A4], -1       ; Exit
        jnz     loc_401671              ; 
        lea     rdi, format             ; 'Error!',0Ah,0
        mov     al, 0
        call    _printf                 ; 
        mov     [rbp+var_3A8], eax
loc_401671:                             ; 
        mov     eax, [rbp+var_4]
        add     rsp, 3B0h               ; 
        pop     rbp
        retn                            ;


vm engine

Vm engine is a stack based machine. At the beginning (0x00400D7F) of ExecuteVm() function, the program initializes vm stack pointer(stack_pointer), vm instruction pointer(vm_ip), flag and key to decrypt.

mov     [rbp+stack_pointer], -1
mov     [rbp+vm_ip], 0
mov     [rbp+var_23], 0
mov     rdi, ds:qword_401890
mov     [rbp+key], rdi
mov     rdi, ds:qword_401898
mov     [rbp+var_148], rdi
mov     al, ds:byte_4018A0
mov     [rbp+var_140], al
mov     ecx, ds:g_VM_flag
and     ecx, not 1              ;
mov     ds:g_VM_flag, ecx
mov     ecx, ds:g_VM_flag
and     ecx, not 2              ;
mov     ds:g_VM_flag, ecx

fetch() function (0x400AD0) is used to read one byte from vm_buffer and decode it. Decoded value is used to identify instruction.

fetch   proc near                       ;
instruction_value= byte ptr -0Dh
vm_ip   = dword ptr -0Ch
ins_buffer= qword ptr -8

        mov     [rsp+ins_buffer], rdi
        mov     [rsp+vm_ip], esi
        mov     [rsp+instruction_value], 0
        cmp     [rsp+vm_ip], 0          ; Compare Two Operands
        jle     loc_400B06              ; Jump if Less or Equal (ZF=1 | SF!=OF)

        mov     eax, [rsp+vm_ip]
        sub     eax, 1                  ; Integer Subtraction
        movsxd  rcx, eax                ; Move with Sign-Extend Doubleword
        mov     rdx, [rsp+ins_buffer]
        mov     sil, [rdx+rcx]
        mov     [rsp+instruction_value], sil

        mov     rax, [rsp+ins_buffer]
        movsxd  rcx, [rsp+vm_ip]        ; Move with Sign-Extend Doubleword
        mov     dl, [rax+rcx]
        mov     sil, [rsp+instruction_value]
        imul    rax, rcx, 2E8BA2E9h     ; Signed Multiply
        mov     rdi, rax
        shr     rdi, 3Fh ; '?'          ; Shift Logical Right
        mov     r8d, edi
        sar     rax, 21h ; '!'          ; Shift Arithmetic Right
        mov     r9d, eax
        add     r9d, r8d                ; = ecx / 11
        imul    r8d, r9d, 11            ; Signed Multiply
        mov     r9d, ecx
        sub     r9d, r8d                ; = ecx % 11
        xor     dl, sil                 ; 
        movsx   r8d, dl                 ; 
        xor     r8d, r9d                ; 
        mov     dl, r8b
        mov     [rsp+instruction_value], dl
        movsx   eax, [rsp+instruction_value] ; 
        retn                            ; 

fetch   endp

This function has 2 parameters :

int fetch(char *vm_ins_buffer, int vm_ip);
if (vm_ip == 0)
    return vm_ins_buffer[vm_ip];
    return vm_ins_buffer[vm_ip] ^ vm_ins_buffer[vm_ip-1] ^ (vm_ip % 11);


Notice that this vm engine has an instruction to decrypt vm code by itself (opcode=0x44), (address = 0x0400E96)

          jmp     $+5                     ; Jump
          mov     eax, [rbp+var_158]
          sub     eax, 44h ; 'D'          ; Integer Subtraction
          mov     [rbp+var_174], eax
          jz      DecryptCode             ; Jump if Zero (ZF=1)

          jmp     Invalid                 ; Jump

 DecryptCode:                            ; 
          lea     rdi, [rbp+key]          ; key
          mov     eax, [rbp+vm_ip]
          add     eax, 1                  ; Add
          mov     [rbp+vm_ip], eax
          mov     rsi, [rbp+vm_ins_buffer]
          mov     edx, [rbp+vm_ip]
          call    DecryptMainVmCode       ; Get size and decrypt buffer

          mov     eax, [rbp+vm_ip]
          add     eax, 3                  ; Add
          mov     [rbp+vm_ip], eax
          jmp     end_instruction         ; Jump

Following this instruction, I look at the main decrypt stub:

  • Algorithm is aes_128_ofb.
  • Read 4 bytes which follow opcode. These bytes are the size of encrypted code.
  • The default key to decrypt is {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F}. This key is changed after reading each character in license.drm.
  • The initialization vector is {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F}
  • Patch vm code after decrypting.
  • The length of this instruction is 5 bytes.

Read license file

getc() instruction (0x000401046)

VM_GetChar proc near                    ; 
         mov     rdi, [rbp-18h]          ; 
         call    _fgetc                  ; 

         mov     cl, al
         mov     [rbp-21h], cl
         movsx   eax, byte ptr [rbp-21h] ;
         cmp     eax, 0FFFFFFFFh         ;
         jnz     loc_40106F              ;

         mov     dword ptr [rbp-4], 1
         jmp     loc_4015ED              ; Jump

 ; ---------------------------------------------------------------------------

 loc_40106F:                             ;
         mov     al, [rbp-21h]
         movsx   rcx, byte ptr [rbp-23h] ; 
         mov     [rbp+rcx-150h], al      ; move the new byte to the ending of key buffer
         mov     al, [rbp-23h]
         add     al, 1                   ; Add
         mov     [rbp-23h], al
         mov     edx, [rbp-1Ch]
         add     edx, 1                  ; Add
         mov     [rbp-1Ch], edx
         mov     al, [rbp-21h]
         movsxd  rcx, dword ptr [rbp-1Ch] ; 
         mov     [rbp+rcx-130h], al      ; put the read byte on the top of stack
         jmp     end_instruction         ; 

 VM_GetChar endp

This function reads one byte from license file, appends to key buffer and pushes it into stack.

Because code is encrypted by input characters, you must find the right characters before continuing to disassemble.

VM instruction table (address = 0x0401710)

.rodata:0000000000401710 vm_ins  dq offset VM_Nop                ; 
.rodata:0000000000401718         dq offset VM_OpenFile
.rodata:0000000000401720         dq offset VM_push
.rodata:0000000000401728         dq offset VM_pop
.rodata:0000000000401730         dq offset VM_Reset
.rodata:0000000000401738         dq offset Invalid
.rodata:0000000000401740         dq offset Invalid
.rodata:0000000000401748         dq offset Invalid
.rodata:0000000000401750         dq offset Invalid
.rodata:0000000000401758         dq offset Invalid
.rodata:0000000000401760         dq offset Invalid
.rodata:0000000000401768         dq offset Invalid
.rodata:0000000000401770         dq offset Invalid
.rodata:0000000000401778         dq offset Invalid
.rodata:0000000000401780         dq offset Invalid
.rodata:0000000000401788         dq offset Invalid
.rodata:0000000000401790         dq offset Invalid
.rodata:0000000000401798         dq offset VM_Exit
.rodata:00000000004017A0         dq offset Invalid
.rodata:00000000004017A8         dq offset VM_swap_nibbles_and_xchg
.rodata:00000000004017B0         dq offset Invalid
.rodata:00000000004017B8         dq offset Invalid
.rodata:00000000004017C0         dq offset Invalid
.rodata:00000000004017C8         dq offset Invalid
.rodata:00000000004017D0         dq offset Invalid
.rodata:00000000004017D8         dq offset VM_GetChar
.rodata:00000000004017E0         dq offset Invalid
.rodata:00000000004017E8         dq offset VM_not
.rodata:00000000004017F0         dq offset VM_jmp
.rodata:00000000004017F8         dq offset VM_je
.rodata:0000000000401800         dq offset VM_jne
.rodata:0000000000401808         dq offset Invalid
.rodata:0000000000401810         dq offset VM_zero
.rodata:0000000000401818         dq offset Invalid
.rodata:0000000000401820         dq offset VM_putchar
.rodata:0000000000401828         dq offset VM_swap_nibbles
.rodata:0000000000401830         dq offset VM_obfucate
.rodata:0000000000401838         dq offset VM_xchg
.rodata:0000000000401840         dq offset VM_xor_curr_pos_with_2_pow_byte
.rodata:0000000000401848         dq offset Invalid
.rodata:0000000000401850         dq offset Invalid
.rodata:0000000000401858         dq offset Invalid
.rodata:0000000000401860         dq offset VM_xor
unsigned char VM_obfucate( unsigned char c)
    char a = 0;
    a |= ((((c &0xFF)& 1) << 7) & 0xFF); a |= ((((c &0xFF) & 0x10) >> 1) & 0xFF);
    a |= ((((c &0xFF) & 0x8) << 1)&0xFF); a |= ((((c &0xFF) & 0x40) >> 5) & 0xFF);
    a |= ((((c &0xFF) & 4) << 3)&0xFF); a |= ((((c &0xFF) & 0x80) >> 7)&0xFF);
    a |= ((((c &0xFF) & 0x2) << 5) & 0xFF); a |= ((((c &0xFF) & 0x20) >> 3) & 0xFF);

    return a;


# Name:        RE300: VM disassembler
# Purpose:     Boston Key Party CTF 2014 writeup
# Author:      Peter from
# Created:     01/03/2014
# Copyright:   (c) PiggyBird CTF Team 2014

import struct

def VM_Nop(vm_buf, ip):
    print "nop"
    return 1

def VM_OpenFile(vm_buf, ip):
    print "open_file"
    return 1

def VM_push(vm_buf, ip):
    print "push ", "0x"+vm_buf[ip+1].encode('hex')
    return 2

def VM_pop(vm_buf, ip):
    print "pop"
    return 1

def VM_Reset(vm_buf, ip):
    print "reset"
    return 1

def VM_Invalid(vm_buf, ip):
    print "Error!"
    return 1

def VM_Exit(vm_buf, ip):
    print "Exit"
    return 1

def VM_swap_nibbles_and_xchg(vm_buf, ip):
    print "swap_nibbles_and_xchg [top], [top-0x" +vm_buf[ip+1].encode('hex')+ "]"
    return 2

def VM_GetChar(vm_buf, ip):
    print "getc()"
    return 1

def VM_not(vm_buf, ip):
    print "not"
    return 1

def VM_jmp(vm_buf, ip):
    print "jmp ", hex(ip + 2 + ord(vm_buf[ip+1]))
    return 2

def VM_je(vm_buf, ip):
    print "je  ", hex(ip + 2 + ord(vm_buf[ip+1]))
    return 2

def VM_jne(vm_buf, ip):
    print "jne ", hex(ip + 2 + ord(vm_buf[ip+1]))
    return 2

def VM_zero(vm_buf, ip):
    print "push  0x00"
    return 1

def VM_putchar(vm_buf, ip):
    print "putchar()"
    return 1

def VM_swap_nibbles(vm_buf, ip):
    print "swap nibbles"
    return 1

def VM_obfucate(vm_buf, ip):
    print "obfucate"
    return 1

def VM_xchg(vm_buf, ip):
    print "xchg [top], [top - 0x"+vm_buf[ip+1].encode('hex')+"]"
    return 2

def VM_xor_curr_pos_with_2_pow_byte(vm_buf, ip):
    print "xor ", "pow(2, 0x"+vm_buf[ip+1].encode('hex')+")"
    return 2

def VM_xor(vm_buf, ip):
    print "xor ", "0x"+vm_buf[ip+1].encode('hex')
    return 2

def VM_add(vm_buf, ip):
    print "add ", "0x"+vm_buf[ip+1].encode('hex')
    return 2

def VM_sub(vm_buf, ip):
    print "sub ", "0x"+vm_buf[ip+1].encode('hex')
    return 2

def VM_cmp(vm_buf, ip):
    print "cmp ", "0x"+vm_buf[ip+1].encode('hex')
    return 2

def VM_decrypt(vm_buf, ip):
    buf_size = struct.unpack("<I", vm_buf[ip+1:ip+5])[0]
    print "decrypt: address="+ hex(ip+5) + ", size=" + hex(buf_size)
    return 5

opcodes = {0 : VM_Nop,
           1 : VM_OpenFile,
           2 : VM_push,
           3 : VM_pop,
           4 : VM_Reset,
           5 : VM_Invalid,
           6 : VM_Invalid,
           7 : VM_Invalid,
           8 : VM_Invalid,
           9 : VM_Invalid,
           10 : VM_Invalid,
           11 : VM_Invalid,
           12 : VM_Invalid,
           13 : VM_Invalid,
           14 : VM_Invalid,
           15 : VM_Invalid,
           16 : VM_Invalid,
           17 : VM_Exit,
           18 : VM_Invalid,
           19 : VM_swap_nibbles_and_xchg,
           20 : VM_Invalid,
           21 : VM_Invalid,
           22 : VM_Invalid,
           23 : VM_Invalid,
           24 : VM_Invalid,
           25 : VM_GetChar,
           26 : VM_Invalid,
           27 : VM_not,
           28 : VM_jmp,
           29 : VM_je,
           30 : VM_jne,
           31 : VM_Invalid,
           32 : VM_zero,
           33 : VM_Invalid,
           34 : VM_putchar,
           35 : VM_swap_nibbles,
           36 : VM_obfucate,
           37 : VM_xchg,
           38 : VM_xor_curr_pos_with_2_pow_byte,
           39 : VM_Invalid,
           40 : VM_Invalid,
           41 : VM_Invalid,
           42 : VM_xor,

def disassembler(vm_buf):
    ''' disassembler '''
    print "vm code size = "+hex(len(vm_buf))+" bytes."
    print "Addr \tCommand"
    print "-----\t-------"

    #i = 0x1F  # get bytes and check
    i = 0x0
    while 1:
        if i > len(vm_buf) - 1:
        print hex(i), "\t", # address
        op = ord(vm_buf[i])

        if op == 0x44:
            i += VM_decrypt(vm_buf, i)

        if op == 0x2b:

            i += VM_add(vm_buf, i)

        if op == 0x2d:
            i += VM_sub(vm_buf, i)

        if op == 0x3d:
            i += VM_cmp(vm_buf, i)

            if (op < 0x4E) or ((op - 0x4e) > len(opcodes)):
                print "Oops! Invalid Opcode, value="+vm_buf[i].encode('hex')
                i += 1

            i += opcodes[op - 0x4e](vm_buf, i)

def main():

    # decode VM code from memory
    encoded = encoded.decode('hex')

    i = 0
    for c in encoded :
        if i == 0:
            decoded += c
            decoded += chr(ord(encoded[i - 1]) ^ ord(c) ^((i % 11) & 0xFF))

        i  = i + 1


if __name__ == '__main__':


vm code size = 0x38f bytes.
Addr Command
----- -------
0x0 push 0x6c
0x2 push 0x69
0x4 push 0x63
0x6 push 0x65
0x8 push 0x6e
0xa push 0x73
0xc push 0x65
0xe push 0x2e
0x10 push 0x64
0x12 push 0x72
0x14 push 0x6d
0x16 push 0x00
0x17 push 0x0c
0x19 open_file
0x1a decrypt: address=0x1f, size=0x371
0x1f getc()
0x20 xor 0xaa
0x22 getc()
0x23 xor 0xbb
0x25 getc()
0x26 xor 0xcc
0x28 xchg [top], [top - 0x01]
0x2a getc()
0x2b xor 0xdd
0x2d xchg [top], [top - 0x03]
0x2f cmp 0xf5
0x31 jne 0x3e
0x33 cmp 0x9c
0x35 jne 0x3f
0x37 cmp 0x47
0x39 jne 0x3f
0x3b cmp 0x01
0x3d je 0x52
0x3f push 0x0a
0x41 push 0x21
0x43 push 0x65
0x45 push 0x70
0x47 push 0x6f
0x49 push 0x4e
0x4b putchar()
0x4c putchar()
0x4d putchar()
0x4e putchar()
0x4f putchar()
0x50 putchar()
0x51 Exit
0x52 push 0x0a
0x54 push 0x3f
0x56 push 0x74
0x58 push 0x69
0x5a push 0x20
0x5c push 0x74
0x5e push 0x27
0x60 push 0x6e
0x62 push 0x73
0x64 push 0x61
0x66 push 0x77
0x68 push 0x20
0x6a push 0x2c
0x6c push 0x79
0x6e push 0x73
0x70 push 0x61
0x72 push 0x65
0x74 push 0x20
0x76 push 0x73
0x78 push 0x61
0x7a push 0x77
0x7c push 0x20
0x7e push 0x74
0x80 push 0x61
0x82 push 0x68
0x84 push 0x54
0x86 push 0x20
0x88 push 0x21
0x8a push 0x65
0x8c push 0x74
0x8e push 0x65
0x90 push 0x6c
0x92 push 0x70
0x94 push 0x6d
0x96 push 0x6f
0x98 push 0x63
0x9a push 0x20
0x9c push 0x31
0x9e push 0x20
0xa0 push 0x65
0xa2 push 0x67
0xa4 push 0x61
0xa6 push 0x74
0xa8 push 0x53
0xaa putchar()
0xab putchar()
0xac putchar()
0xad putchar()
0xae putchar()
0xaf putchar()
0xb0 putchar()
0xb1 putchar()
0xb2 putchar()
0xb3 putchar()
0xb4 putchar()
0xb5 putchar()
0xb6 putchar()
0xb7 putchar()
0xb8 putchar()
0xb9 putchar()
0xba putchar()
0xbb putchar()
0xbc putchar()
0xbd putchar()
0xbe putchar()
0xbf putchar()
0xc0 putchar()
0xc1 putchar()
0xc2 putchar()
0xc3 putchar()
0xc4 putchar()
0xc5 putchar()
0xc6 putchar()
0xc7 putchar()
0xc8 putchar()
0xc9 putchar()
0xca putchar()
0xcb putchar()
0xcc putchar()
0xcd putchar()
0xce putchar()
0xcf putchar()
0xd0 putchar()
0xd1 putchar()
0xd2 putchar()
0xd3 putchar()
0xd4 putchar()
0xd5 putchar()
0xd6 decrypt: address=0xdb, size=0x2b1
0xdb getc()
0xdc not
0xdd xor 0xee
0xdf getc()
0xe0 not
0xe1 xor 0xdd
0xe3 getc()
0xe4 not
0xe5 xor 0xcc
0xe7 getc()
0xe8 not
0xe9 xor 0xaa
0xeb xchg [top], [top - 0x03]
0xed xchg [top], [top - 0x02]
0xef xchg [top], [top - 0x01]
0xf1 cmp 0x55
0xf3 jne 0x100
0xf5 cmp 0xcc
0xf7 jne 0x101
0xf9 cmp 0xf6
0xfb jne 0x101
0xfd cmp 0x07
0xff je 0x114
0x101 push 0x0a
0x103 push 0x21
0x105 push 0x65
0x107 push 0x70
0x109 push 0x6f
0x10b push 0x4e
0x10d putchar()
0x10e putchar()
0x10f putchar()
0x110 putchar()
0x111 putchar()
0x112 putchar()
0x113 Exit
0x114 push 0x0a
0x116 push 0x21
0x118 push 0x67
0x11a push 0x6e
0x11c push 0x69
0x11e push 0x76
0x120 push 0x6f
0x122 push 0x6d
0x124 push 0x20
0x126 push 0x70
0x128 push 0x65
0x12a push 0x65
0x12c push 0x4b
0x12e push 0x20
0x130 push 0x21
0x132 push 0x65
0x134 push 0x74
0x136 push 0x65
0x138 push 0x6c
0x13a push 0x70
0x13c push 0x6d
0x13e push 0x6f
0x140 push 0x63
0x142 push 0x20
0x144 push 0x32
0x146 push 0x20
0x148 push 0x65
0x14a push 0x67
0x14c push 0x61
0x14e push 0x74
0x150 push 0x53
0x152 putchar()
0x153 putchar()
0x154 putchar()
0x155 putchar()
0x156 putchar()
0x157 putchar()
0x158 putchar()
0x159 putchar()
0x15a putchar()
0x15b putchar()
0x15c putchar()
0x15d putchar()
0x15e putchar()
0x15f putchar()
0x160 putchar()
0x161 putchar()
0x162 putchar()
0x163 putchar()
0x164 putchar()
0x165 putchar()
0x166 putchar()
0x167 putchar()
0x168 putchar()
0x169 putchar()
0x16a putchar()
0x16b putchar()
0x16c putchar()
0x16d putchar()
0x16e putchar()
0x16f putchar()
0x170 putchar()
0x171 decrypt: address=0x176, size=0x211
0x176 getc()
0x177 obfucate
0x178 not
0x179 xor 0xfe
0x17b getc()
0x17c xor 0xe1
0x17e obfucate
0x17f xchg [top], [top - 0x01]
0x181 getc()
0x182 xor 0xde
0x184 obfucate
0x185 getc()
0x186 xor 0xad
0x188 obfucate
0x189 not
0x18a xchg [top], [top - 0x02]
0x18c xchg [top], [top - 0x01]
0x18e obfucate
0x18f xchg [top], [top - 0x02]
0x191 cmp 0x91
0x193 jne 0x1a0
0x195 cmp 0x5d
0x197 jne 0x1a1
0x199 cmp 0x61
0x19b jne 0x1a1
0x19d cmp 0xe6
0x19f je 0x1b4
0x1a1 push 0x0a
0x1a3 push 0x21
0x1a5 push 0x65
0x1a7 push 0x70
0x1a9 push 0x6f
0x1ab push 0x4e
0x1ad putchar()
0x1ae putchar()
0x1af putchar()
0x1b0 putchar()
0x1b1 putchar()
0x1b2 putchar()
0x1b3 Exit
0x1b4 push 0x0a
0x1b6 push 0x21
0x1b8 push 0x65
0x1ba push 0x72
0x1bc push 0x65
0x1be push 0x68
0x1c0 push 0x74
0x1c2 push 0x20
0x1c4 push 0x79
0x1c6 push 0x6c
0x1c8 push 0x72
0x1ca push 0x61
0x1cc push 0x65
0x1ce push 0x6e
0x1d0 push 0x20
0x1d2 push 0x65
0x1d4 push 0x72
0x1d6 push 0x61
0x1d8 push 0x20
0x1da push 0x75
0x1dc push 0x6f
0x1de push 0x59
0x1e0 push 0x20
0x1e2 push 0x21
0x1e4 push 0x65
0x1e6 push 0x74
0x1e8 push 0x65
0x1ea push 0x6c
0x1ec push 0x70
0x1ee push 0x6d
0x1f0 push 0x6f
0x1f2 push 0x63
0x1f4 push 0x20
0x1f6 push 0x33
0x1f8 push 0x20
0x1fa push 0x65
0x1fc push 0x67
0x1fe push 0x61
0x200 push 0x74
0x202 push 0x53
0x204 putchar()
0x205 putchar()
0x206 putchar()
0x207 putchar()
0x208 putchar()
0x209 putchar()
0x20a putchar()
0x20b putchar()
0x20c putchar()
0x20d putchar()
0x20e putchar()
0x20f putchar()
0x210 putchar()
0x211 putchar()
0x212 putchar()
0x213 putchar()
0x214 putchar()
0x215 putchar()
0x216 putchar()
0x217 putchar()
0x218 putchar()
0x219 putchar()
0x21a putchar()
0x21b putchar()
0x21c putchar()
0x21d putchar()
0x21e putchar()
0x21f putchar()
0x220 putchar()
0x221 putchar()
0x222 putchar()
0x223 putchar()
0x224 putchar()
0x225 putchar()
0x226 putchar()
0x227 putchar()
0x228 putchar()
0x229 putchar()
0x22a putchar()
0x22b putchar()
0x22c decrypt: address=0x231, size=0x151
0x231 getc()
0x232 xor 0xde
0x234 xor pow(2, 0x05)
0x236 xor pow(2, 0x03)
0x238 getc()
0x239 xor 0xad
0x23b swap nibbles
0x23c not
0x23d getc()
0x23e obfucate
0x23f xor pow(2, 0x02)
0x241 xor pow(2, 0x01)
0x243 xor 0xbe
0x245 getc()
0x246 swap_nibbles_and_xchg [top], [top-0x02]
0x248 xor 0xef
0x24a not
0x24b xchg [top], [top - 0x03]
0x24d xchg [top], [top - 0x02]
0x24f xchg [top], [top - 0x01]
0x251 cmp 0xb8
0x253 jne 0x260
0x255 cmp 0xbb
0x257 jne 0x261
0x259 cmp 0xc6
0x25b jne 0x261
0x25d cmp 0xf6
0x25f je 0x274
0x261 push 0x0a
0x263 push 0x21
0x265 push 0x65
0x267 push 0x70
0x269 push 0x6f
0x26b push 0x4e
0x26d putchar()
0x26e putchar()
0x26f putchar()
0x270 putchar()
0x271 putchar()
0x272 putchar()
0x273 Exit
0x274 push 0x0a
0x276 push 0x0a
0x278 push 0x2e
0x27a push 0x74
0x27c push 0x69
0x27e push 0x20
0x280 push 0x64
0x282 push 0x65
0x284 push 0x6b
0x286 push 0x69
0x288 push 0x6c
0x28a push 0x20
0x28c push 0x75
0x28e push 0x6f
0x290 push 0x79
0x292 push 0x20
0x294 push 0x65
0x296 push 0x70
0x298 push 0x6f
0x29a push 0x48
0x29c push 0x20
0x29e push 0x21
0x2a0 push 0x65
0x2a2 push 0x74
0x2a4 push 0x65
0x2a6 push 0x6c
0x2a8 push 0x70
0x2aa push 0x6d
0x2ac push 0x6f
0x2ae push 0x63
0x2b0 push 0x20
0x2b2 push 0x34
0x2b4 push 0x20
0x2b6 push 0x65
0x2b8 push 0x67
0x2ba push 0x61
0x2bc push 0x74
0x2be push 0x53
0x2c0 putchar()
0x2c1 putchar()
0x2c2 putchar()
0x2c3 putchar()
0x2c4 putchar()
0x2c5 putchar()
0x2c6 putchar()
0x2c7 putchar()
0x2c8 putchar()
0x2c9 putchar()
0x2ca putchar()
0x2cb putchar()
0x2cc putchar()
0x2cd putchar()
0x2ce putchar()
0x2cf putchar()
0x2d0 putchar()
0x2d1 putchar()
0x2d2 putchar()
0x2d3 putchar()
0x2d4 putchar()
0x2d5 putchar()
0x2d6 putchar()
0x2d7 putchar()
0x2d8 putchar()
0x2d9 putchar()
0x2da putchar()
0x2db putchar()
0x2dc putchar()
0x2dd putchar()
0x2de putchar()
0x2df putchar()
0x2e0 putchar()
0x2e1 putchar()
0x2e2 putchar()
0x2e3 putchar()
0x2e4 putchar()
0x2e5 putchar()
0x2e6 decrypt: address=0x2eb, size=0x91
0x2eb push 0x0a
0x2ed push 0x27
0x2ef push 0x4e
0x2f1 push 0x75
0x2f3 push 0x46
0x2f5 push 0x5f
0x2f7 push 0x73
0x2f9 push 0x49
0x2fb push 0x5f
0x2fd push 0x47
0x2ff push 0x6e
0x301 push 0x49
0x303 push 0x73
0x305 push 0x52
0x307 push 0x65
0x309 push 0x56
0x30b push 0x65
0x30d push 0x52
0x30f push 0x5f
0x311 push 0x6d
0x313 push 0x56
0x315 push 0x27
0x317 push 0x20
0x319 push 0x3a
0x31b push 0x79
0x31d push 0x65
0x31f push 0x6b
0x321 push 0x20
0x323 push 0x7a
0x325 push 0x61
0x327 push 0x68
0x329 push 0x20
0x32b push 0x6e
0x32d push 0x61
0x32f push 0x63
0x331 push 0x20
0x333 push 0x75
0x335 push 0x6f
0x337 push 0x79
0x339 push 0x20
0x33b push 0x77
0x33d push 0x6f
0x33f push 0x4e
0x341 putchar()
0x342 putchar()
0x343 putchar()
0x344 putchar()
0x345 putchar()
0x346 putchar()
0x347 putchar()
0x348 putchar()
0x349 putchar()
0x34a putchar()
0x34b putchar()
0x34c putchar()
0x34d putchar()
0x34e putchar()
0x34f putchar()
0x350 putchar()
0x351 putchar()
0x352 putchar()
0x353 putchar()
0x354 putchar()
0x355 putchar()
0x356 putchar()
0x357 putchar()
0x358 putchar()
0x359 putchar()
0x35a putchar()
0x35b putchar()
0x35c putchar()
0x35d putchar()
0x35e putchar()
0x35f putchar()
0x360 putchar()
0x361 putchar()
0x362 putchar()
0x363 putchar()
0x364 putchar()
0x365 putchar()
0x366 putchar()
0x367 putchar()
0x368 putchar()
0x369 putchar()
0x36a putchar()
0x36b putchar()
0x36c Exit
0x36d Oops! Invalid Opcode, value=7b
0x36e Oops! Invalid Opcode, value=7b
0x36f Oops! Invalid Opcode, value=7b
0x370 Oops! Invalid Opcode, value=7b
0x371 Oops! Invalid Opcode, value=7b
0x372 Oops! Invalid Opcode, value=7b
0x373 Oops! Invalid Opcode, value=7b
0x374 Oops! Invalid Opcode, value=7b
0x375 Oops! Invalid Opcode, value=7b
0x376 Oops! Invalid Opcode, value=7b
0x377 Oops! Invalid Opcode, value=7b
0x378 Oops! Invalid Opcode, value=7b
0x379 Oops! Invalid Opcode, value=7b
0x37a Oops! Invalid Opcode, value=7b
0x37b Oops! Invalid Opcode, value=3c
0x37c Oops! Invalid Opcode, value=3c
0x37d Oops! Invalid Opcode, value=7b
0x37e Oops! Invalid Opcode, value=7b
0x37f Oops! Invalid Opcode, value=7b
0x380 Oops! Invalid Opcode, value=7b
0x381 reset
0x382 reset
0x383 Oops! Invalid Opcode, value=7b
0x384 Oops! Invalid Opcode, value=7b
0x385 Oops! Invalid Opcode, value=7b
0x386 Oops! Invalid Opcode, value=3a
0x387 Oops! Invalid Opcode, value=3a
0x388 Oops! Invalid Opcode, value=7b
0x389 Oops! Invalid Opcode, value=7b
0x38a Oops! Invalid Opcode, value=7b
0x38b Oops! Invalid Opcode, value=ab
0x38c Oops! Invalid Opcode, value=ab
0x38d Oops! Invalid Opcode, value=7b
0x38e Oops! Invalid Opcode, value=7b

The program will read license.drm in current directory and check.
It takes me short time to write the disassembler, but I spend much time finding the correct values:

5F 27 8B DC E7 EE 66 52 3A 86 BF DB 30 39 00 EB
root@ubuntu:~/ctf/vm# ./vm-2fbed3f5a894d56be6b2ba328f9e2411
Stage 1 complete! That was easy, wasn't it?
Stage 2 complete! Keep moving!
Stage 3 complete! You are nearly there!
Stage 4 complete! Hope you liked it.

Now you can haz key: 'Vm_ReVeRsInG_Is_FuN'