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:
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:
- Introduction to Modern Code Virtualization
- Tutorial for Building and Reverse Engineering Simple Virtual Machine Protection
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:
The following is Python implementation of the code fragment:
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.
Python implementation:
Instruction Set
VM_decode executes a different code block based on the fetched opcode:
click here for larger version.
-
If
opcodeis 1, it writes the secondoperandto the data section at position pointed by the firstoperand(MOV VM_data[operand_1], operand_2). -
If
opcodeis 2, it saves the data pointed by the firstoperandinto the global variable, I renamed it asR0- register 0 (MOV R0, operand_2). -
If
opcodeis 3, itXORsdata pointed by the firstoperandwithR0(XOR VM_data[operand_1], R0).
That’s whole instruction set, so simple.
Python implementation of VM_decode function:
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}