skip to content
secrary[dot]com

Reverse Engineering a Simple VM Crackme by MalwareTech

/

Introduction

The crackme is created by @MalwareTechLab, you can download the sample from here.

Initial Analysis

We have three files, vm1.exe, ram.bin and README.txt:

1

Challenge Description

This malware has stolen a flag and encrypted it with a very simple encryption algorithm; unfortunately, the decrypter is coded in a custom 8-bit assembly language which it runs using a minimalistic virtual machine. Your job is to reverse engineer the malware and figure out the instruction set used by the VM, once you’ve done this you should write your own VM in Python which will run the decrypter and recover the flag.

Prerequisites

Before continuing, I recommend reading:

Analysis with IDA

VM Initialization

Let’s open the IDA and see what we have. The crackme copies 0x1FB bytes from VM_code into VM_ram (the copied data is the same as ram.bin file’s content) and calls VM_run:

2

The following is Python implementation of the code fragment:

3

Understanding the VM Architecture

VM Run Function

Inside VM_run, it fetches opcode and two operands and calls VM_decode. As we see it fetches opcode from the second part of VM_ram (actually from VM_ram + 0xff), we can guess that code section starts from there.

NOTE: For example, in MOV eax, 1 instruction, MOV is opcode, eax and 1 are operands.

4

Python implementation:

5

Instruction Set

VM_decode executes a different code block based on the fetched opcode:

6

click here for larger version.

  • If opcode is 1, it writes the second operand to the data section at position pointed by the first operand (MOV VM_data[operand_1], operand_2).

  • If opcode is 2, it saves the data pointed by the first operand into the global variable, I renamed it as R0 - register 0 (MOV R0, operand_2).

  • If opcode is 3, it XORs data pointed by the first operand with R0 (XOR VM_data[operand_1], R0).

That’s whole instruction set, so simple.

Python implementation of VM_decode function:

7

Implementation

Python Solution

Here’s the complete Python implementation of the virtual machine:

from enum import Enum
class VirtualMachine:
class Opcodes(Enum):
MOV_data = 1
MOV_reg = 2
XOR = 3
EXIT = 4
def __init__(self, ram):
self.R0 = 0 # register
self.VM_data = (bytearray)(ram[:0xff]) # data section
self.VM_code = ram[0xff:] # code section
def VM_run(self):
self.IP = 0
while True:
# fetch
self.opcode = self.VM_code[self.IP] # opcode
self.IP += 1
self.operand_1 = self.VM_code[self.IP] # first operand
self.IP += 1
self.operand_2 = self.VM_code[self.IP] # second operand
self.IP += 1
# decode
if self.VM_decode() == self.Opcodes.EXIT.value:
return
def VM_decode(self):
if self.opcode == self.Opcodes.MOV_data.value:
self.VM_data[self.operand_1] = self.operand_2
print("MOV VM_data[{}], {};".format(hex(self.operand_1), hex(self.operand_2)))
elif self.opcode == self.Opcodes.MOV_reg.value:
self.R0 = self.VM_data[self.operand_1]
print("MOV R0, VM_data[{}];".format(hex(self.operand_1)))
elif self.opcode == self.Opcodes.XOR.value:
self.VM_data[self.operand_1] ^= self.R0
print("XOR VM_data[{}], R0".format(hex(self.operand_1)))
else:
return self.Opcodes.EXIT.value
def main():
with open("ram.bin", 'rb') as f:
VM_ram = f.read()
vm = VirtualMachine(VM_ram)
vm.VM_run()
print("\n{}".format(vm.VM_data))
if __name__ == '__main__':
main()

Result

After running the Python implementation, we get the flag: FLAG{VMS-ARE-FOR-MALWARE}

8