QVM32 solution and virtualization-obfuscation

Don't read this post if you're still in the processes of solving the previous challenge posted here. This post contains the solution and general information on virtualized code. Virtualization-obfuscations are techniques I've seen many times implemented in various malwares and rarely some software. Many times seen implemented in some reverse engineering challenges. Qvm32 the challenge I posted does follow a similar concept with an extra obfuscation part added to harden the process of reverse engineering the virtual machine or the bytecode interruptor. To understand what's going on and how those byte codes are processed. The second obfuscation applied to this challenge was on the LLVM level by playing with LLVM-IR to do a 32bit adder obfuscation ideas implemented from the paper "Obfuscation of steel: meet my kryptonite" by Axel Souchet. Unfourtnatly, REing this part is out of the scope of this post. That'll be your job as a reverse engineer to deobfuscate it if possible and/or to understand what's going on. Another thing to discuss is that I have added some randomized number of instructions to be executed this is to prevent prediction of the password via weakness in the comparision algorithim that can help you count the executed instructions meaning if more instruction executed when X is input then X is true if XY causes more instructions than XZ then XY is correct and so on. This is possible via tracing the instructions executed and counting them or using PIN dynamic binary instrumentation tool from Intel that have builtin modules to do insutrction counting, so the extra random number of instructions executed will make this type of attacks fail (?).

The rest of this post assume that you have understood and/or deobfuscated the 32bit adder in the code, otherwise things will not look the identical.

If you noticed the byte codes processed by the interp. are

uint8_t mem[] =  
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x55xe1x01x00x00x03x0dx0c"
    "x55xe2x00x00x00x00xf2x0c"
    "x33xbbx00x00x00x00xf2"
    "xddxbbx00x13"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00x37"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00xd3"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00x3d"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00xc0"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00xde"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00xab"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00xad"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00x1d"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00xea"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00x13"
    "xccxbbx00x00x00x00x01"
    "xddxbbx00x37"
    "xccxbbx00x00x00x00x01"
    "xaaxbbx00x00x00x00xf2"
    "x33xbbx01x00x00x00x7a"
    "xaaxbbx02x00x00x00xf3"
    "x33xbbx03x00x00x00x60"
    "x66xabx00x01"
    "x99xbbx00x00x02xf9"
    "x66xabx02x03"
    "x99xbbx00x00x02xf9"
    "xaaxbbx00x00x00x00xf4"
    "x33xbbx01x00x00x00xb2"
    "xaaxbbx02x00x00x00xf5"
    "x33xbbx03x00x00x00x4e"
    "x66xabx00x01"
    "x99xbbx00x00x02xf9"
    "x66xabx02x03"
    "x99xbbx00x00x02xf9"
    "xaaxbbx00x00x00x00xf6"
    "x33xbbx01x00x00x00xb4"
    "xaaxbbx02x00x00x00xf7"
    "x33xbbx03x00x00x00xbb"
    "x66xabx00x01"
    "x99xbbx00x00x02xf9"
    "x66xabx02x03"
    "x99xbbx00x00x02xf9"
    "xaaxbbx00x00x00x00xf8"
    "x33xbbx01x00x00x00xe6"
    "xaaxbbx02x00x00x00xf9"
    "x33xbbx03x00x00x00xd4"
    "x66xabx00x01"
    "x99xbbx00x00x02xf9"
    "x66xabx02x03"
    "x99xbbx00x00x02xf9"
    "xaaxbbx00x00x00x00xfa"
    "x33xbbx01x00x00x00x49"
    "xaaxbbx02x00x00x00xfb"
    "x33xbbx03x00x00x00x83"
    "x66xabx00x01"
    "x99xbbx00x00x02xf9"
    "x66xabx02x03"
    "x99xbbx00x00x02xf9"
    "xaaxbbx00x00x00x00xfc"
    "x33xbbx01x00x00x00x7e"
    "xaaxbbx02x00x00x00xfd"
    "x33xbbx03x00x00x00x52"
    "x66xabx00x01"
    "x99xbbx00x00x02xf9"
    "x66xabx02x03"
    "x99xbbx00x00x02xf9"
    "x55xe1x01x00x00x03x09x04"
    "x55xe0xff"
    "x55xe1x01x00x00x03x02x07"
    "FAILEDn"
    "WINn"
    "ENTER PASS : "
    "xee"
    ;

This is the code processed by the interruptor it's not x86 code or anything it's just some stuff that the interruptor understands and process them as it understands them. The design of the virtual machine is very simple 'not counting all the obufscation in the code'.

The machine have 4 general registers r1,r2,r3,r4. Also two more rs for stack and ri for instructions. A stack exist in the virtual machine but not used by the crackme bytecode. Flags are used for jnz and cmp the flag consist of 3 values either FE (flag equal), FG (flag greater), and FL (flag less).

There are 13 opcodes supported by the interp. They're

  RET   = 0x11, // opcode
  CALL  = 0x22, // opcode
  MOV   = 0x33, // mov reg, reg|imm
  PUSH  = 0x44, // push reg|imm
  ECALL = 0x55, // ecall imm
  CMP   = 0x66, // cmp reg, reg|imm
  JMP   = 0x77, // jmp imm
  JZ    = 0x88, // jz imm
  JNZ   = 0x99, // jnz imm
  MOVP  = 0xaa, // movp reg, imm
  AND   = 0xbb, // and reg, reg|imm
  ADD   = 0xcc, // add reg, reg|imm
  XORP  = 0xdd // xorp reg, reg|imm

Ecall is an external call outside of the VM and it supports a small number of syscalls. Currently supported are sysexit (0xe0), syswrite (0xe1) and sys_read (0xe2).

In general the interp. will take first byte if it's an opcode then it will process the next byte which is either 0xAB which refers that the opcode is of type "OPCODE reg,reg || OPCODE reg" or will take 0xBB which refers that the opcode is of type "OPCODE reg, imm || OPCODE imm".  I have explained how each opcode work as comments in the above opcode list. You'll be able to get all this knowledge by looking at how X opcode is treated and then giving each one a name.

Now if you have finished understanding the VM then you can understand what the bytecodes mean and then find the password for the crackme. The translation of the bytecodes are.

// Get user input
write(0x01, "ENTER PASS :", 0x0c);  
read(0x00, 0x000000f2, 0x0c);

//XOR input with our xor key
MOV [IMM] r1 0x000000f2  
XOR [IMM] r1 0x13  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0x37  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0xd3  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0x3d  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0xc0  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0xde  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0xab  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0xad  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0x1d  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0xea  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0x13  
ADD [IMM] r1 0x00000001  
XOR [IMM] r1 0x37  
ADD [IMM] r1 0x00000001

// Do Comparisions
MOVP r1, 0x000000f2  
MOV r2, 0x000000..  
MOVP r3, 0x000000f3  
MOV r4, 0x000000..  
CMP r1, r2  
JNZ failed  
CMP r3, r4  
JNZ failed  
MOVP r1, 0x000000f4  
MOV r2, 0x000000..  
MOVP r3, 0x000000f5  
MOV r4, 0x000000..  
CMP r1, r2  
JNZ failed  
CMP r3, r4  
JNZ failed  
MOVP r1, 0x000000f6  
MOV r2, 0x000000..  
MOVP r3, 0x000000f7  
MOV r4, 0x000000..  
CMP r1, r2  
JNZ failed  
CMP r3, r4  
JNZ failed  
MOVP r1, 0x000000f8  
MOV r2, 0x000000..  
MOVP r3, 0x000000f9  
MOV r4, 0x000000..  
CMP r1, r2  
JNZ failed  
CMP r3, r4  
JNZ failed  
MOVP r1, 0x000000fa  
MOV r2, 0x000000..  
MOVP r3, 0x000000fb  
MOV r4, 0x000000..  
CMP r1, r2  
JNZ failed  
CMP r3, r4  
JNZ failed  
MOVP r1, 0x000000fc  
MOV r2, 0x000000..  
MOVP r3, 0x000000fd  
MOV r4, 0x000000..  
CMP r1, r2  
JNZ failed  
CMP r3, r4  
JNZ failed

// Win if not failed and exit
write(0x01, "WINn", 0x4)  
EXIT(ff);  
write(0x01, "FAILEDn", 0x0c);  

Now this is what we want to figure out how to find the password. This code when processed by the interp. it will do an ECALL calling sys_write passing it with stdout and the address of the string "Enter password" from the vmmem and then it will take 0x0c from stdin which is the password. Then each byte of the password is xored with each byte of our xor key with length 0x0c "same length as the password" then the result is compared with a hardcoded values if any of the values doesn't match the input xored with key then it jumps to "failed" prints "failed" otherwise it will continue until it reached the printing "win" and exits. So we know the hardcoded xor key and the hardcoded key that's compared with the result then a simple xoring with key will reveal the password which is "iWasteMyTime" lol :P

Here's the source code of the VM. I warn you this is one of the worst codes I've written might hurt your eyes :D. If you add/improve/..etc to the code I'm interested to look at your version. Enjoy.

// - qnix

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>

#define STACK_MAX 1024
#define END_VM    0xee
#define COPY_GD(x) x = (x << 8) | g_data[++regs[RI]]; 
                   x = (x << 8) | g_data[++regs[RI]]; 
                   x = (x << 8) | g_data[++regs[RI]]; 
                   x = (x << 8) | g_data[++regs[RI]];
// Debugging macros
#ifdef NDEBUG
#define SHOW_REG(x)
#define SHOW_FLAG
#define SHOW_REGS
#define SHOW_STACK(x)
#define DEBUG(M, ...)
#else
#define DEBUG(M, ...) printf(M, ##__VA_ARGS__)
#define SHOW_REG(x) if(x == R1) { printf("R1 "); } 
                          else if(x == R2) { printf("R2 "); } else if(x == R3) { printf("R3 "); } 
                          else if(x == R4) { printf("R4 "); } else if(x == RS) { printf("RS "); } 
                          else if(x == RI) { printf("RI "); } else { printf(" UNKOWN REGISTER "); }
#define SHOW_FLAG if(flags == FL) printf("FLAG LESSn"); 
                            else if(flags == FG) printf("FLAG GREATERn"); 
                            else if(flags == FE) printf("FLAG EQUALn");
#define SHOW_REGS printf("R1=%.8xn", regs[R1]); 
                  printf("R2=%.8xn", regs[R2]); 
                  printf("R3=%.8xn", regs[R3]); 
                  printf("R4=%.8xn", regs[R4]); 
                  printf("RS=%.8xn", regs[RS]); 
                  printf("RI=%.8xn", regs[RI]);
#define SHOW_STACK(x) { int i; for(i=0;i<x;i++) printf("stack[%d] = %.8xn", i, stack[i]); }
#endif

int32_t *stack  = NULL;  
int32_t regs[6] = { 0 };  
int32_t flags   = 0;

enum OPCODES {  
/  RET   = 0x11,
  CALL  = 0x22,
  MOV   = 0x33,
  PUSH  = 0x44,
  ECALL = 0x55,
  CMP   = 0x66,
  JMP   = 0x77,
  JZ    = 0x88,
  JNZ   = 0x99,
  MOVP  = 0xaa,
  AND   = 0xbb,
  ADD   = 0xcc,
  XORP  = 0xdd
};

enum OPCODE_PARAM {  
    REG = 0xAB,
    IMM = 0xBB
};

enum REGS {  
    R1 = 0x00,
    R2 = 0x01,
    R3 = 0x02,
    R4 = 0x03,
    RS = 0x04,
    RI = 0x05
};

enum FLAGS {  
    FU = 0xf0,
    FE = 0xf1,
    FG = 0xf2,
    FL = 0xf3
};

enum SYSCALLS {  
  SYS_EXIT  = 0xe0,    // param exit_code=2
  SYS_WRITE = 0xe1,    // param fd=1size_t=1 value to write at r1
  SYS_READ  = 0xe2    // param fd=1,void*=2,size_t=3
};

uint8_t g_data[5000] =  
    // just some region we use for whatever.
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
    // Get password
    "x55xe1x01x00x00x03x0dx0c"    // write(0x01, "ENTER PASS :", 0x0c);
    "x55xe2x00x00x00x00xf2x0c"    // read(0x00, &"ENTER PASS :", 0x0c);
    // XOR input with our xor key
    "x33xbbx00x00x00x00xf2"    // MOV [IMM] r1 0x000000f2
    "xddxbbx00x13"        // XOR [IMM] r1 0x13
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00x37"        // XOR [IMM] r1 0x37
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00xd3"        // XOR [IMM] r1 0xd3
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00x3d"        // XOR [IMM] r1 0x3d
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00xc0"        // XOR [IMM] r1 0xc0
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00xde"        // XOR [IMM] r1 0xde
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00xab"        // XOR [IMM] r1 0xab
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00xad"        // XOR [IMM] r1 0xad
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00x1d"        // XOR [IMM] r1 0x1d
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00xea"        // XOR [IMM] r1 0xea
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00x13"        // XOR [IMM] r1 0x13
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001
    "xddxbbx00x37"        // XOR [IMM] r1 0x37
    "xccxbbx00x00x00x00x01"    // ADD [IMM] r1 0x00000001

    // Do Comparisions
    "xaaxbbx00x00x00x00xf2"    // MOVP r1, 0x000000f2
    "x33xbbx01x00x00x00x7a"    // MOV r2, 0x000000..
    "xaaxbbx02x00x00x00xf3"    // MOVP r3, 0x000000f3
    "x33xbbx03x00x00x00x60"    // MOV r4, 0x000000..
    "x66xabx00x01"                // CMP r1, r2
    "x99xbbx00x00x02xf9" // JNZ failed
    "x66xabx02x03"        // CMP r3, r4
    "x99xbbx00x00x02xf9" // JNZ failed
    "xaaxbbx00x00x00x00xf4"  // MOVP r1, 0x000000f4
    "x33xbbx01x00x00x00xb2"  // MOV r2, 0x000000..
    "xaaxbbx02x00x00x00xf5"  // MOVP r3, 0x000000f5
    "x33xbbx03x00x00x00x4e"  // MOV r4, 0x000000..
    "x66xabx00x01"        // CMP r1, r2
    "x99xbbx00x00x02xf9" // JNZ failed
    "x66xabx02x03"        // CMP r3, r4
    "x99xbbx00x00x02xf9" // JNZ failed
    "xaaxbbx00x00x00x00xf6"  // MOVP r1, 0x000000f6
    "x33xbbx01x00x00x00xb4"  // MOV r2, 0x000000..
    "xaaxbbx02x00x00x00xf7"  // MOVP r3, 0x000000f7
    "x33xbbx03x00x00x00xbb"  // MOV r4, 0x000000..
    "x66xabx00x01"        // CMP r1, r2
    "x99xbbx00x00x02xf9" // JNZ failed
    "x66xabx02x03"        // CMP r3, r4
    "x99xbbx00x00x02xf9" // JNZ failed
    "xaaxbbx00x00x00x00xf8"  // MOVP r1, 0x000000f8
    "x33xbbx01x00x00x00xe6"  // MOV r2, 0x000000..
    "xaaxbbx02x00x00x00xf9"  // MOVP r3, 0x000000f9
    "x33xbbx03x00x00x00xd4"  // MOV r4, 0x000000..
    "x66xabx00x01"        // CMP r1, r2
    "x99xbbx00x00x02xf9" // JNZ failed
    "x66xabx02x03"        // CMP r3, r4
    "x99xbbx00x00x02xf9" // JNZ failed
    "xaaxbbx00x00x00x00xfa"  // MOVP r1, 0x000000fa
    "x33xbbx01x00x00x00x49"  // MOV r2, 0x000000..
    "xaaxbbx02x00x00x00xfb"  // MOVP r3, 0x000000fb
    "x33xbbx03x00x00x00x83"  // MOV r4, 0x000000..
    "x66xabx00x01"        // CMP r1, r2
    "x99xbbx00x00x02xf9" // JNZ failed
    "x66xabx02x03"        // CMP r3, r4
    "x99xbbx00x00x02xf9" // JNZ failed
    "xaaxbbx00x00x00x00xfc"  // MOVP r1, 0x000000fc
    "x33xbbx01x00x00x00x7e"  // MOV r2, 0x000000..
    "xaaxbbx02x00x00x00xfd"  // MOVP r3, 0x000000fd
    "x33xbbx03x00x00x00x52"  // MOV r4, 0x000000..
    "x66xabx00x01"        // CMP r1, r2
    "x99xbbx00x00x02xf9" // JNZ failed
    "x66xabx02x03"        // CMP r3, r4
    "x99xbbx00x00x02xf9" // JNZ failed
    // Win if not failed
    "x55xe1x01x00x00x03x09x04" // write(0x01, "WINn", 0x4)
    "x55xe0xff"        // EXIT(ff);
    // FAILED_INPUT
    "x55xe1x01x00x00x03x02x07"  // write(0x01, "FAILEDn", 0x0c);
    "FAILEDn"
    "WINn"
    "ENTER PASS : "
    "xee"
    ;

int main(int argc, char **argv)  
{

    stack = (int32_t *) malloc(sizeof(int32_t) * STACK_MAX);
  // int r=0;
  // for(r=0;r<rand()%100;r++)
    if (!stack) return -1;
    SHOW_REGS;

    while (g_data[regs[RI]] != END_VM) {
        switch (g_data[regs[RI]]) {
        case RET:
            break;
        case CALL:
            break;
        case MOV:
            {
                DEBUG("[%.8x] MOV ", regs[RI]);
                uint8_t mov_type = g_data[++regs[RI]];
                if (mov_type == REG) {
                    DEBUG(" [REG] ");
                    uint8_t dst = g_data[++regs[RI]];
                    uint8_t src = g_data[++regs[RI]];
                    regs[dst] = regs[src];
                    SHOW_REG(dst);
                    SHOW_REG(src);
                    DEBUG("n");
                } else if (mov_type == IMM) {
                    DEBUG(" [IMM] ");
                    uint8_t dst = g_data[++regs[RI]];
                    SHOW_REG(dst);
                    regs[dst] = 0;
                    COPY_GD(regs[dst]);
                    DEBUG("%.8xn", regs[dst]);
                } else {
                    DEBUG(" [!REG&!IMM]n");
                }
            }
            break;
        case PUSH:
            {
                DEBUG("[%.8x] PUSH ", regs[RI]);
                uint8_t push_type = g_data[++regs[RI]];
                if (push_type == REG) {
                    uint8_t r = g_data[++regs[RI]];
                    stack[regs[RS]++] = regs[r];
                    SHOW_REG(r);
                    DEBUG("n");
                } else if (push_type == IMM) {
                    int data = 0;
                    COPY_GD(data);
                    stack[regs[RS]++] = data;
                    DEBUG("%.8xn", data);
                } else {
                    DEBUG(" [!REG&!IMM]n");
                }
            }
            break;
        case ECALL:
            {
                DEBUG("[%.8x] ECALL ", regs[RI]);
                uint8_t syscall_n = g_data[++regs[RI]];
                switch (syscall_n) {
                case SYS_EXIT:
                    {
                        uint8_t exit_code =
                            g_data[++regs[RI]];
                        DEBUG("exit(%x)n", exit_code);
                        exit(exit_code);
                    }
                    break;
                case SYS_WRITE:
                    {
                        uint8_t fd = g_data[++regs[RI]];
                        int buffer_address = 0;
                        COPY_GD(buffer_address);
                        uint8_t sz = g_data[++regs[RI]];
                        DEBUG("write(%x, %.8x, %x)n",
                              fd, buffer_address, sz);
                        write(fd,
                              &g_data[buffer_address],
                              sz);
                    }
                    break;
                case SYS_READ:
                    {
                        uint8_t fd = g_data[++regs[RI]];
                        int buffer_address = 0;
                        COPY_GD(buffer_address);
                        uint8_t sz = g_data[++regs[RI]];
                        int ret =
                            read(fd,
                             &g_data
                             [buffer_address], sz);
                        DEBUG
                            ("read(%x, %.8x, %x) -> read %d bytesn",
                             fd,
                             &g_data[buffer_address],
                             sz, ret);
                    }
                    break;
                default:
                    DEBUG(" unkown syscall %xn",
                          syscall_n);
                    break;
                }
            }
            break;
        case CMP:
            {
                DEBUG("[%.8x] CMP ", regs[RI]);
                uint8_t cmp_type = g_data[++regs[RI]];
                if (cmp_type == REG) {
                    uint8_t dst = g_data[++regs[RI]];
                    uint8_t src = g_data[++regs[RI]];
                    SHOW_REG(dst);
                    SHOW_REG(src);
                    DEBUG("n");
                    if (regs[dst] == regs[src])
                        flags = FE;
                    else if (regs[dst] < regs[src])
                        flags = FL;
                    else if (regs[dst] > regs[src])
                        flags = FG;
                } else if (cmp_type == IMM) {
                    int dst = 0;
                    int src = 0;
                    COPY_GD(dst);
                    COPY_GD(src);
                    DEBUG("%.8x %.8xn", dst, src);
                    if (dst == src)
                        flags = FE;
                    else if (dst < src)
                        flags = FL;
                    else if (dst > src)
                        flags = FG;
                } else {
                    DEBUG(" [!REG&!IMM]n");
                }
                SHOW_FLAG;
            }
            break;
        case JMP:
            {
                DEBUG("[%.8x] JMP ", regs[RI]);
                uint8_t jmp_type = g_data[++regs[RI]];
                if (jmp_type == IMM) {
                    DEBUG(" [IMM] ");
                    int v = 0;
                    COPY_GD(v);
                    regs[RI] = v;
                    DEBUG(" -> %.8xn", v);
                } else {
                    DEBUG(" [!IMM]n");
                }
            }
            break;
        case JZ:
            DEBUG("[%.8x] JZ ", regs[RI]);
            SHOW_FLAG;
            uint8_t jz_type = g_data[++regs[RI]];
            if (jz_type == IMM) {
                DEBUG(" [IMM] ");
                if (flags == FE) {
                    int v = 0;
                    COPY_GD(v);
                    regs[RI] = v;
                    DEBUG(" -> %.8xn", v);
                } else {
                    DEBUG(" condition not metn");
          // get rid of the next 4 bytes
          // assuming there are 4 bytes
                    regs[RI]++;
                    regs[RI]++;
                    regs[RI]++;
                    regs[RI]++;
                }
            } else {
                DEBUG(" ![IMM]n");
            }
            break;
        case JNZ:
            DEBUG("[%.8x] JNZ ", regs[RI]);
            SHOW_FLAG;
            uint8_t jnz_type = g_data[++regs[RI]];
            if (jnz_type == IMM) {
                if (flags != FE) {
                    int v = 0;
                    COPY_GD(v);
                    regs[RI] = v;
                    DEBUG(" -> %.8xn", v);
                } else {
                    DEBUG(" condition not metn");
          // get rid of the next 4 bytes
          // assuming there are 4 bytes
                    regs[RI]++;
                    regs[RI]++;
                    regs[RI]++;
                    regs[RI]++;
                }
            } else {
                DEBUG(" ![IMM]n");
            }
            break;
        case MOVP:
            {
                DEBUG("[%.8x] MOVP ", regs[RI]);
                uint8_t movp_type = g_data[++regs[RI]];
                if (movp_type == IMM) {
                    DEBUG(" [IMM] ");
                    uint8_t dst = g_data[++regs[RI]];
                    int idx = 0;
                    COPY_GD(idx);
                    regs[dst] = g_data[idx];
                    SHOW_REG(dst);
                    DEBUG(" %.8xn", idx);
                    SHOW_REGS;
                } else {
                    DEBUG(" [!IMM]n");
                }
            }
            break;
        case AND:
            DEBUG("[%.8x] AND ", regs[RI]);
            uint8_t and_type = g_data[++regs[RI]];
            if (and_type == REG) {
                DEBUG(" [REG] ");
                uint8_t dst = g_data[++regs[RI]];
                uint8_t src = g_data[++regs[RI]];
                regs[dst] &= regs[src];
                SHOW_REG(dst);
                SHOW_REG(src);
                DEBUG("n");
            } else if (and_type == IMM) {
                DEBUG(" [IMM] ");
                uint8_t dst = g_data[++regs[RI]];
                int val = 0;
                COPY_GD(val);
                regs[dst] &= val;
                SHOW_REG(dst);
                DEBUG(" %.8xn", val);
            } else {
                DEBUG(" [!reg|!imm]n");
            }
            break;
        case ADD:
            DEBUG("[%.8x] ADD ", regs[RI]);
            uint8_t add_type = g_data[++regs[RI]];
            if (add_type == REG) {
                uint8_t dst = g_data[++regs[RI]];
                uint8_t src = g_data[++regs[RI]];
                regs[dst] += regs[src];
                SHOW_REG(dst);
                SHOW_REG(src);
                DEBUG("n");
            } else if (add_type == IMM) {
                uint8_t dst = g_data[++regs[RI]];
                int val = 0;
                COPY_GD(val);
                regs[dst] += val;
                SHOW_REG(dst);
                DEBUG(" %.8xn", val);
            } else {
                DEBUG(" [!reg|!imm]n");
            }
            break;
        case XORP:
            {
                DEBUG("[%.8x] XORP ", regs[RI]);
                uint8_t xor_type = g_data[++regs[RI]];
                if (xor_type == REG) {
                    DEBUG(" [REG] ");
                    uint8_t dst = g_data[++regs[RI]];
                    uint8_t src = g_data[++regs[RI]];
                    g_data[regs[dst]] ^= regs[src];
                    SHOW_REG(dst);
                    SHOW_REG(src);
                    DEBUG("n");
                } else if (xor_type == IMM) {
                    DEBUG(" [IMM] ");
                    uint8_t dst = g_data[++regs[RI]];
                    uint8_t val = g_data[++regs[RI]];
                    SHOW_REG(dst);
                    DEBUG(" %xn", val);
                    g_data[regs[dst]] ^= val;
                } else {
                    DEBUG(" [!reg|!imm]n");
                }

            }
            break;
        default:
            DEBUG("[%.8x] Unkown opcode %xn", regs[RI],
                  g_data[regs[RI]]);
            break;
        }
        regs[RI]++;
    }

    SHOW_STACK(12);
    SHOW_REGS;

    free(stack);
    return 0;
}
}}