Search This Blog

Showing posts with label software protection. Show all posts
Showing posts with label software protection. Show all posts

Wednesday, May 23, 2012

Passing Events to a Virtual Machine

The source code for this article may be found here.

Virtual machines and Software Frameworks are an initial part of our digital life. There are complex VM and simple Software Frameworks. These two articles (Simple Virtual Machine and Simple Runtime Framework by Example) show how easy it may be to implement one yourself. I did my best to describe the way VM code may interact with native code and the Operating System, however, the backwards interaction is still left unexplained. This article is going to fix this omission.

As usual - note for nerds:
The source code given in this article is for example purposes only. I know that this framework is far from being perfect, therefore, this article is not a howto or tutorial - just an explanation of principle. Error checks are omitted on purpose. You want to implement a real framework - do it yourself, including error checks.
By saying VM's code I do not refer to the implementation of the virtual machine, but to the pseudo code that runs inside it.


Architecture Overview
Needless to mention, that the ability to pass events/signals to a code executed by the virtual machine implies a more complex VM architecture. While all previous examples were based on a single function responsible for the execution, adding events means not only adding another function, but we will have to introduce threads to our implementation.

At least two threads are needed:
Fig.1
VM Architecture with Event Listener

  1. Actual VM - this thread is responsible for the execution of the VM's executable code and events queue dispatch (processor);
  2. Event Listener - this thread is responsible for collection of relevant events from the Operating Systems and adding them to the VM's event queue (listener).
You may see that the Core() function, in the attached source code, creates additional thread.







Event ListenerThis thread collects events from the Operating System (mouse move, key up/down, etc) and adds a new entry to the list of EVENT structures.

typedef struct _EVENT
{
   struct _EVENT* next_event; // Pointer to the next event in the queue
   int            code;       // Code of the event
   unsigned int   data;       // Either unsigned int data or the address of the buffer
                              // containing information to be passed to the handler
}EVENT;

The code for the listener is quite simple:

while(WAIT_TIMEOUT == WaitForSingleObject(processor_thread, 1))
{
   // Check for events from the OS
   if(event_present)
   {
      EnterCriticalSection(&cs);
      event = (EVENT*)malloc(sizeof(EVENT));
      event->code = whatever_code_is_needed;
      event->data = whatever_data_is_relevant;
      add_event(event_list, event);
      event->next_event = NULL;
      LeaveCriticalSection(&cs);
   }
}

The code is self explanatory enough.  First of all it checks for available events (this part is omitted and replaced by a comment). If there is a new event to pass to the VM, it adds it to the queue. While in this example, event collection is implemented as a loop, in real life, you may do it in a form of callbacks and use the loop above just to wait for the processor thread to exit.


Processor

Obviously, the "processor" thread is going to be a bit more complicated, then in the previous article (
Simple Runtime Framework by Example), as in addition to running the run_opcode(CPU**) function, it has to check for pending events and pass the control flow to the associated handler in the VM code.

typedef struct _EVENT_HANDLER
{
   struct _EVENT_HANDLER* next_handler; // Pointer to the next handler
   int                    event_code;   // Code of the event
   unsigned int           handler_base; // Address of the handler in the VM's code
}EVENT_HANDLER;

DWORD WINAPI RunningThread(void* param)
{
   CPU*            cpu = (CPU*)param;
   EVENT*          event;
   EVENT_HANDLER*  handler;

   do{
      EnterCriticalSection(&cs);
      if(NULL != events)
      {
         event = events;
         events = events->next_event;

         // Save current context by pushing VM registers to VM's stack
         
         cpu->regs[REG_A] = (unsigned int)event->code;
         cpu->regs[REG_B] = event->data;

         handler = handlers;
         while(NULL != handler && event->code != handler->event_code)
               handler = handler->next_handler;
         
         cpu->regs[REG_IP] = handler->handler_base;

         free(event);
      }
      LeaveCriticalSection(&cs);

   }while(0 != run_opcode(&cpu));
   return cpu->regs[REG_A];
}

We are almost done. Our framework already knows how to pass events to a correct handler in the VM's code. Two more things are yet uncovered - registering a handler and returning from a handler.


Returning from Handler

Due to the fact that Event Handler is not a regular routine, we cannot return from it using the regular
RET instruction, instead, let's introduce another instruction - IRET. As event actually interrupts the execution flow of the program, IRET - interrupt return is exactly what we need. The source code that handles this instruction is so simple, that there is no need to give it here in the text of the article. All it does is simply restoring the context of the VM's code by popping the registers previously pushed on stack.


Registering an Event Handler

The last thing left is to "teach" the program written in pseudo assembly to register a handler for a given event type. In order to do this, we need to add one simple system call -
SYS_ADD_LISTENER.  This system call accepts two parameters:
  1. Code of the event to handle;
  2. Address of the handler function.
loadi  A, 0             ;Code of the event
loadi  B, handler       ;Address of the handler subroutine
_int   sys_add_listener ;Register the handler


Example Code

The example code attached to this article is the implementation of all of the above. It does the following:
  1. Registers event handler;
  2. Enters an infinite loop printing out '.' every several milliseconds;
  3. The first thread waits a bit and generates an event;
  4. Event handler terminates the infinite loop and returns;
  5. The program prints out a message and exits.


I hope this post was helpful or, at least, interesting.

See you at the next.






Friday, March 2, 2012

Dynamic Code Encryption as an Anti Dump and Anti Reverse Engineering measure

Source code for this article may be found here.


There has been said and written too much on how software vendors do not protect their products, so let me skip this. Instead, in this article, I would like to concentrate on those relatively easy steps, which software vendors have to take in order to enhance their protection (using packers and protectors is good, but certainly not enough) by not letting the whole code appear in memory in readable form for a single moment.

Attack Vectors
Prior to dealing with "why attackers are able to x, y, z" let us map most frequent attack vectors in ascending order of their complexity.

Static Analysis - inspecting an executable in your favorite disassembler. It may be hard to believe, but majority of software products out there are vulnerable to static analysis, thus, showing us, that most of vendors do not care about proprietary algorithms' safety in addition to the fact, that they seem not to care about piracy  either (but they tend to cry about it all the time).

Dynamic Analysis - running an executable inside your favorite debugger. This is a direct consequence of the previous paragraph. If an attacker is able to see the whole code in the disassembler - he/she definitely can run it  in a debugger (even if this requires some minor patching).

Static Patching - this means changing the code located in the file of the executable. It may be changing one jump or adding a couple of dozens of bytes of attacker's own code in order to alter the way the program runs.

Dynamic Patching - similar to static patching in the idea behind the method. The only difference is, that dynamic patching is performed while the target executable is loaded into memory.

Dumping - saving the data in memory to a file on disk. This method may be very useful when examining a packed executable. Such memory dumps may be easily loaded into, for example, IDA and examined as if that was a regular executable (some additional actions may be required for better convenience, like rebasing the program or adjusting references to other modules).

In most cases, at least two of the aforementioned vectors would be present in time of attack.


Packers and Cryptors
Using different packers, cryptors and protectors is quite a known practice among software vendors. The problem with this is, that few of them go beyond packing the code in file and fully unpacking it in memory and, sometimes, protecting the packer itself. By saying "go beyond" I mean any implementation of anti debugging methods of any kind. Besides, such utilities do not prevent an attacker from obtaining a memory dump good enough to deal with. One or two check the consistency of the code, which may (yes - may, as it not necessarily can) prevent patching the code, but every wall has a door and it only matters how much effort opening that door may require. Bottom line is, that these types of protection may only be useful in preventing static analysis, but only if there is no relevant unpacker or decrypter.


Protectors
This is "the next step" in the evolution of packers. These provide a bit more options and tools to estimate how secure the environment is. In addition to packing the code, they also utilize code consistency checks, anti debugging tricks, license verification, etc. Protectors are good countermeasures to the first three (or even four) attack vectors. However, even if certain protector has some anti patching heuristics, it is only good as long as it (heuristics) is not reversed and either patched or fooled in any other way. 

Despite all the "good" in protectors, even such powerful tools are not able to do much in order to prevent an attacker from obtaining a memory dump, which may be obtained by either using ReadProcessMemory or injecting a DLL and dumping "from inside" while suspending all other threads.


Anything Else?
Yes, there are some basic protections provided by the operating system, like session separation, for example, which prevents creation of remote threads (used with DLL injection), but those are hardly worth even mentioning here.

The picture drawn here appears to be sad and hopeless enough. However, there are several good methods to add more protection to a software product and more pain in some parts of the body to attackers.


Code Obfuscation
While this methods is widely used by protectors and, sometimes, by packers and cryptors (unfortunately, in most cases, for protecting themselves only) it seems to be almost totally unknown to the rest of software vendors. In my opinion, branching the code more than it is usually needed may not be considered as code obfuscation, it may rather be called an attempt to obfuscate an algorithm. The situation is such, that even implementation of something similar to this would be a significant improvement in vendors' efforts to protect their products.


Hiding the Code
Software vendors repeatedly fail at understanding two facts - popular means more vulnerable (in regard of commercial solutions) and the fact that there is no magic cure and they have to put some additional effort into protecting their products.

One of the options, which I would like to cover here, is dynamic encryption of executable code. This method promises that only certain parts of the code would be present in memory in readable (possible to disassemble) form, while the rest of the code (and preferably data) is encrypted.

I am still sure - the best way to explain something is explanation by example. The small piece of C code described below is intended to show the principle of dynamic code encryption. It contains several functions in addition to main - the first is the one (the target) we are going to protect. It does nothing special, just calculates the factorial of 10 and prints it out. The main function invokes a decrypter in order to decrypt the target, calls the target (thus, displaying the factorial of 10) and, finally invokes cryptor to encrypt the target back (hide it).

The code may be compiled for both Linux (using gcc) or Windows (using mingw32). It uses obfuscation code from here.


Target Function
Our target function is quite simple (it only calculates factorial for hardcoded number):

void func()
{
   __asm__ __volatile__("enc_start:");
   {  /* Braces are used here as we do not want IDA to track parameters */
      int i, f = 10;
      for( i = 9; i > 0; i--)
         f *= i;
      printf("10! = %d\n\n", f);
   }
   __asm__ __volatile__("enc_end:");
}

You noticed the labels in the beginning and in the end of the function body? These labels are only used for getting the start address of the region to be decrypted/encrypted and calculating it's length. Due to the fact that these labels are no processed by the C preprocessor, but are passed to assembler, they are accessible from other functions by default. The rest of the code is enclosed by braces in order to put all the actions related to variables i and f in the encrypted part of the function. This is what it looks like, before being decrypted:


Although, in attached code, the initial encryption is performed upon program start, in reality, it should be done with, probably, a third party tool. You would only have to put some unique marking at the start and end of the region you want to encrypt. For example:


__asm__(".byte  0x0D, 0xF0, 0xAD, 0xDE");
void  func()
{
...
}
__asm__(".byte  0xAD, 0xDE, 0xAD, 0xDE");


Encryption Algorithm
Selection of encryption algorithm is totally up to you. In this particular case, the algorithm is quite primitive (it does not even require a key):

b  - byte
i  - position
for i = 0; i < length; i++
   b(i+1) = b(i+1) xor (b(i) rol 1)
b(0) = b(0) xor (b(length) rol 1)

Execution Flow
So, let us assume that the program started with the function already encrypted. As this is just an example, we can get to the business right away:

int main()
{
   unsigned int  addr, len;
   __asm__ __volatile__("movl  $enc_start, %0\n\t"\
                        "movl  $enc_end, %1\n\t"\
                        : "=r"(addr), "=r"(len));
   len -= addr;
   decode(addr, len);
   func();
   encode(addr, len);
   return 0;
}

The code above is self explanatory enough. There are, however, a couple of things needed to be mentioned. decode and encode functions should take care of modifying the access rights of the memory region they are going to operate on. The following code may be used:

#ifdef WIN32
#include <windows.h>
#define SETRWX(addr, len)   {\
                               DWORD attr;\
                               VirtualProtect((LPVOID)((addr) &~ 0xFFF),\
                                  (len) + ((addr) - ((addr) &~ 0xFFF)),\
                                  PAGE_EXECUTE_READWRITE,\
                                  &attr);\
                            }
#define SETROX(addr, len)   {\
                               DWORD attr;\
                               VirtualProtect((LPVOID)((addr) &~ 0xFFF),\
                                  (len) + ((addr) - ((addr) &~ 0xFFF)),\
                                  PAGE_EXECUTE_READ,\
                                  &attr);\
                            }
#else
#include <sys/mman.h>
#define SETRWX(addr, len)   mprotect((void*)((addr) &~ 0xFFF),\
                                     (len) + ((addr) - ((addr) &~ 0xFFF)),\
                                     PROT_READ | PROT_EXEC | PROT_WRITE)
#define SETROX(addr, len)   mprotect((void*)((addr) &~ 0xFFF),\
                                     (len) + ((addr) - ((addr) &~ 0xFFF)),\
                                     PROT_READ | PROT_EXEC)
#endif

This is the only platform dependent code in this sample.

Bottom Line
The example given above is really a simple one. Things would be at least a bit more complicated in real life. While there is only one encrypted function, imagine, that there are several encrypted functions. Some of them are encrypted without keys (like the one above) others require keys of different complexity. Several keys may be hardcoded (for those parts that were encrypted in order to draw attacker's attention away from the "real" thing), others should be computed on the fly.

Example:
Function A is encrypted without a key. When decrypted, it performs several operations and decrypts function B, which, in turn encrypts function A back and calculates a key for function C based on the binary content of function A (or A and B to prevent breakpoints) or even based on some other code in unrelated place.

Of course, there is no such thing as unbreakable protection. But the time it takes to break certain protection makes the difference. A company that produces software product which is cracked the next day may hardly benefit from all the hard work. On the other hand, it is totally possible to create protection schemes that would require months to be cracked.

I will try and cover additional possibilities and aspects of software protection in my future posts in a hope to at least try to change the situation.


Hope this post was helpful.
See you at the next!















Monday, February 27, 2012

Basics of Executable Code Obfuscation

Source code for this article may be found here.




The problem of software security has already been raised in my previous articles more that once. This article is not an exception. 

Majority of software vendors position themselves as number one in the industry, even though there are always more then 1 number 1. But what unites them all (well, almost all) in reality, it the fact that they all suffer from piracy, they all are aware of that and the last one - they all do almost nothing to change the situation. It surely impossible to totally defeat software piracy but it is definitely realistic, to make the "pirating" process a pain in the neck for pirates.

In this article, I would like to cover the basics of executable code obfuscation - a relatively simple technique, which is, unfortunately, rarely utilized by software vendors as they mostly rely on out of the box solutions. Those of you who have read my previous articles know what my attitude towards those solutions is (roughly saying - more public - more vulnerable), but let me make a  bit different statement here - no matter how good and complex the out of the box solution is, most of software vendors mistakenly assume, that the presence of a well known (for its complexity) software protection package alone is enough, without even trying to utilize it properly. There are cases, however, when proper utilization of third party protection tools is not possible (then why use them at all?) due to negative impact of program's execution (by either consuming too much time or causing faults of different kind).

The lack of ability to protect software product brings forth much more problems then a trivial software piracy. How about proprietary algorithms? The question I always want to ask software vendors is - do you really think that listing patents and copyright notice in the "About" section is enough to push an attacker back?

There are numerous methods of executable code protection, starting with static/dynamic encryption and up to complex virtual machines. However, this article is intended to briefly cover the most basic and at the same time so rare (in the world of legitimate software) method as executable code obfuscation. Although, it is hard to believe that this would really stop someone with modern reverse engineering tools from compromising your software product, but it would definitely make the process of static analysis and algorithm restoration a lot harder. In addition, it would draw attacker's attention away from other protections utilized (at least for some time, depending on attacker's skills).

Executable Code Obfuscation
This technique is intended to prevent static analysis of the executable code and reduce the possibility of algorithm restoration. Obfuscated code would initially appear as nonsense at first glance:


But at first glance only. Take a closer look (this is the beginning if a main function) and you would see that sub_401421 is not a separate function, but belongs to the previous code - it calculates the address of the next instruction to be executed, pushes it onto the stack as if it was a return address and executes ret performing jump.

This is all nice, but let's get to a simple example - the best way to understand how it works and, which is more important, how it may be implemented.

Trivial Example
As it comes from the title of this paragraph, the example code I want to show here is not intended to perform any meaningful task. It simply prints out "Hello!!!" several times, tests an obfuscated call to a library function "printf" and exits. The code is 32 bits, runs on both Linux and Windows and may be compiled with either GCC or MinGW C compilers. You would have to adjust it a bit for usage with MSVC, however. It contains three functions:
  1. constructor - responsible for encryption of addresses of functions that are going to be called by other parts of the code (which are "function #2" and "printf";
  2. my_func - this is the "function #2'; it actually prints out the "Hello!!!" string and accepts an amount of times to print the string and a pointer to the string to be printed;
  3. main - well, this is the main function.


Preparations
There are a couple of steps to be taken prior to writing the code itself - setup the addresses of the functions that are going to be used.

Declare three global variables of type unsigned int:

unsigned int  addr;    // Address of "my_func" function
unsigned int  printer; // Address of "printf" function
unsigned int  _mask;   // XOR mask for encryption of addresses

Next step is to implement constructor function (see this post about constructor functions in MSVC):


Getting it Done
Write the "my_func" function:


call2 is a macro that performs an obfuscated call and accepts the following parameters:
  • address - the address of the function to call;
  • param1 - first parameter of the callee;
  • param2 - second parameter of the callee.
Here is the definition of the call2 macro:

Those of you using Microsoft's C compiler (e.g. Microsoft Visual C++) should rewrite the Assembly code according to Intel syntax. Use this cheat sheet if you are not familiar with AT&T Assembly syntax.

Main
The main function in this example is quite simple but it seems to illustrate the concept:


The code above is self explanatory enough. 
Put all code together, build and run. This is what the output looks like:

Quite simple, isn't it? Take a look at the compiled result in IDA Pro, for example, and see that it has become harder to read and, even more important, to understand the code.

For nerds: yes, you have to include stdio.h header file.

Taking it Further
Just as I mentioned above, this is a trivial example, neither a tutorial nor instructions to follow. It is obvious, that obfuscation macros in production code should be at least a bit more complicated, then what is shown here.

You may also add some macros with "junk" code in order to obfuscate other parts (not related to function calls) of your code. Like this, for example:


Despite the simplicity, even such trivial macros can convert readable code into something like this (main starts at loc_4013FB):



Thanks for reading! Hope this post was helpful. 
See you at the next!

Thursday, December 22, 2011

Simple Virtual Machine

Sample code for this article may be found here.


In computing, Virtual Machine (VM) is a software implementation of either existing or a fictional hardware platform.  VM's are generally divided into two classes - system VM (VM which is capable of running an operating system) and process VM (the one that only can run one executable, roughly saying). Anyway, if you are just interested in the definition of the term, you better go here

There are tones of articles dedicated to this matter on the Internet, hundreds of tutorials and explanations. I see no reason to just add another "trivial" article or tutorial to the row. Instead, I think it may be more interesting to see it in action, to have an example of real application. One may say that we are surrounded by those examples - Java, .NET, etc. It is correct, however, I would like to touch a slightly different application of this technology - protect your software/data from being hacked.

Data Protection
Millions of dollars are being spent by software (or content) vendors in an attempt to protect their products from being stolen or used in any other illegal way. There are numerous protection tools and utilities, starting with simple packers/scramblers and ending with complex packages that implement multilevel encryption and virtual machines as well. However, you may disagree, but you wont convince me, an out-of-the-box solution is good until it gains popularity. There is enough evidence for this statement. In my opinion, no one can protect your software better than you. It only depends on how much protected you want it to be.

Although, there are numerous protection methods and techniques, we are going to concentrate on a virtual machine for data coding/decoding. Nothing special, just a trivial XOR method, but, in my opinion, enough to demonstrate the fundamentals.

Design Your VM
While in real life, hardware design precedes its software counterpart, we may let ourselves to do it in reverse order (it is our own VM, after all). Therefore, we will begin with the pseudo executable file format which will be supported by our VM. 


Pseudo Executable File Format
Well, it is a good idea to put a header in the beginning of the file. In order to do so, we have to think what our file is going to contain. The file may be a raw code (remember DOS com files?), but this would not be interesting enough. So, let our file be divided into three sections:
  • code section - this section would contain code written in our pseudo assembly language (we'll cover it a bit later);
  • data section - this section would contain all the data needed by our pseudo executable (PE :-) );
  • export section - this section would contain references to all the elements that we want to make visible to the core program.
Let us define the header as a C structure:

typedef struct _VM_HEADER
{
   unsigned int  version; /* Version of our VM. Will be 0x101 for now */
   unsigned int  codeOffset; /* File offset of the code section */
   unsigned int  codeSize; /* Size of the code section in bytes */
   unsigned int  dataOffset; /* File offset of the data section */
   unsigned int  dataSize; /* Size of the data section in bytes */
   unsigned int  exportOffset; /* File offset of the export section */
   unsigned int  exportSize; /* Size of the export section in bytes */
   unsigned int  requestedStack; /* Required size of stack in 4 bytes blocks */
   unsigned int  fileSize; /* Size of the whole file in bytes */
}VM_HEADER;

Well, one more thing. Actually the most important one. We need a compiler for our pseudo assembly that would be able to output files of this format. Fortunately, we do not have to write one (although, this may be an interesting task). Tomasz Grysztar has done a wonderful work with his Flat Assembler. Despite the fact, that this compiler is intended to compile Intel assembly code, thanks to the wonderful macro instruction support, we can adopt it to our needs. The skeleton source for our file would look like this:

include 'defs.asm' ;Definitions of our pseudo assembly instructions

org 0
; Header =======================
   h_version   dd 0x101
   h_code      dd _code
   h_code_size dd _code_size
   h_data      dd _data
   h_data_size dd _data_size
   h_exp       dd _export
   h_exp_size  dd _export_size
   h_stack     dd 0x40
   h_size      dd size

; Code =========================
_code:
   _function:
      ;some pseudo code here
_code_size = $ - _code

; Data =========================
_data:

   ;some data here

_data_size = $ - _data

; Export =======================
_export:
   
   ;export table structures here

_export_size = $ - _export
size = $ - h_version

as simple as that.

Export section deserves special attention. I tried to make it as easy to use as possible. It is divided into two parts:

  1. Array of file offsets of export entries terminated by 0;
  2. Export entries:
    1. File offset of the exported function/variable (4 bytes);
    2. Public name of the exported object (NULL terminated ASCII string);
In the above example, the export section would look like this:

; Array of file offsets
      dd  _f1  ; Offset of '_f1' export entry
      dd  0    ; Terminating 0
; List of export entries
  _f1 dd  _function                  ; File offset
      db  'exported_function_name',0 ; Public name

Save the file as 'something.asm' or whatever name you prefer. Compile it with Fasm.

Pseudo Assembly Language
Now, when we are done with the file format, we have to define our pseudo assembly language. This includes both definition of commands and instruction encoding. As this VM is designed to only code/decode short text message, there is no need to develop full scale set of commands. All we need is MOV, XOR, ADD, LOOP and RET

Before you start writing macros that would represent these commands, we have to think about instruction encoding. This is not going to be difficult - we are not Intel. For simplicity, all our instructions will be two bytes long followed by one or more immediate arguments if there are any. This allows us to encode all the needed information, such as opcode, type of arguments, size of arguments and operation direction:
typedef struct _INSTRUCTION
{
   unsigned short opCode:5;    /* Opcode value */
   unsigned short opType1:2;   /* Type of the first operand if present */
   unsigned short opType2:2;   /* Type of the second operand if present */
   unsigned short opSize:2;    /* Size of the operand(s) */
   unsigned short reg1:2;      /* Index of the register used as first operand */
   unsigned short reg2:2;      /* Index of the register used as second operand */
   unsigned short direction:1; /* Direction of the operation */

}INSTRUCTION;


Define the following constants:

/* Operand types */

#define OP_REG 0 /* Register operand */
#define OP_IMM 1 /* Immediate operand */
#define OP_MEM 2 /* Memory reference */
#define OP_NONE 3 /* No operand (optional) */

/* Operand sizes */
#define _BYTE 0
#define _WORD 1
#define _DWORD 2

/* Operation direction */
#define DIR_LEFT  0
#define DIR_RIGHT 1

/* Instructions (OpCodes) */
#define MOV     1
#define MOVI 7
#define ADD     2
#define SUB 3
#define XOR     4
#define LOOP 5
#define RET 6


It seems to me that there is no reason to put all the macros defining our pseudo assembly opcodes here, as it would be a waste of space. I will just put one here as an example. This will be the definition of MOV instruction:

Constants to be used with our pseudo assembly language



























Macro defining the MOV instruction































As you can see in the code above, I've been lazy again and decided, that it would be easier to implicitly specify the size of the arguments, rather then writing some extra code to identify their size automatically. In addition, the name of the instruction tells what that specific instruction is intended to do. For example, mov_rm - moves value from memory to register and letters 'r' and 'm' tell what types of arguments are in use (register, memory). In this case, moving a WORD from memory to a register would look like this:

   mov_rm REG_A, address, _WORD

and the whole code section (currently contains only one function)  is represented by the image below:

loads address of the message as immediate value into B register; loads length of the message from address described by message_len into C register; iterates message_len times and applies XOR to every byte of the message. "mov_rmi" performs the same operation as "mov_rm" but the address is in the register specified as second parameter.







This is what the output looks like in IDA Pro:

Header













Code


















Data and Export sections































Virtual Machine
Alright, now, when we have some sort of a "compiler", we may start working on the VM itself. First of all, let us define a structure, that would represent our virtual CPU:

typedef struct _VCPU
{
   unsigned int  registers[4]; /* Four registers */
   unsigned int  *stackBase;   /* Pointer to the allocated stack */
   unsigned int  *stackPtr;    /* Pointer to the current position in stack */
   unsigned int  ip;           /* Instruction pointer */
   unsigned char *base;        /* Pointer to the buffer where our pseudo 
                                  executable is loaded to */
}VCPU

registers - general purpose registers. There is no need for any additional register in this VM's CPU;
stackBase - pointer to the beginning of the allocated region which we use as stack for our VM;
stackPtr - this is our stack pointer;
ip - instruction pointer. Points to the next instruction to be executed. It cannot point outside the buffer containing our pseudo executable;
base - pointer to the buffer which contains our executable. You may say that this is the memory of our VM.

In addition, you should implement at least some functions for the following:
  1. allocate/free virtual CPU
  2. load pseudo executable into VM's memory and setup stack
  3. a function to retrieve either a file offset or normal pointer to an object exported by the pseudo executable
  4. a function to set instruction pointer (although, this may be done by directly accessing the ip field of the virtual CPU
  5. a function that would run our pseudo code.
In my case, the final source looks like this:




































I decided not to cite the VM's code here as you should be able to write it yourself if the subject is interesting enough for you. Although, the code in this article does not contain any checks for correct return values, you should take care of them.

Summary
Although, this article describes a trivial virtual machine which is only able to encode/decode a fixed length buffer, the concept itself may serve you well in software/data protection as hacking into VM is several times harder then cracking native code.

One more thing to add. Our design allows us to call procedures provided by the pseudo executable, but there are several ways to allow the pseudo executable to "talk to us". The simplest (as it seems to me) is to implement interrupts.

I hope, I've covered it. Would appreciate comments and/or suggestions.

P.S. The encoded result would be "V{rrq2>Iqlrz?".

See you at the next post!