- Open – choose an executable you want to protect, or a project file (*.vmp). You can also select a file to open from the list of previously protected applications shown in the File menu. You can also bring up the open dialog with the corresponding button on the toolbar
. Finally, you can drag-n-drop the file you need to the VMProtect window;
- Save Project – save application protection settings to a “*.vmp” file. Project settings file is saved to the same folder where the executable of the protected application is located. Saving is also available with the toolbar button
;
- Save Project As… – save the project file to a file with a new name;
- Close – finish working with the current project;
- Exit – close VMProtect.
File menu
GUI version
SDK functions
SDK functions can be integrated to the source code of the protected application to set boundaries of the protected areas, to detect debuggers or virtualization tools.
Code markers
- VMProtectBegin
- VMProtectBeginVirtualization
- VMProtectBeginMutation
- VMProtectBeginUltra
- VMProtectBeginVirtualizationLockByKey
- VMProtectBeginUltraLockByKey
- VMProtectEnd
Service functions
- VMProtectIsProtected
- VMProtectIsDebuggerPresent
- VMProtectIsVirtualMachinePresent
- VMProtectIsValidImageCRC
- VMProtectDecryptStringA
- VMProtectDecryptStringW
- VMProtectFreeString
Licensing functions
- VMProtectSetSerialNumber
- VMProtectGetSerialNumberState
- VMProtectGetSerialNumberData
- VMProtectGetCurrentHWID
Activation functions
- VMProtectActivateLicense
- VMProtectDeactivateLicense
- VMProtectGetOfflineActivationString
- VMProtectGetOfflineDeactivationString
!Important
After protection, the application won't longer require SDK library.
VMProtectBegin
void VMProtectBegin(const char *MarkerName);
The marker identifying the beginning of the protected area of the code. A call to VMProtectBegin must be placed before the first command (or procedure or function call) of the protected code block. MarkerName defines the name of the marker that looks like “VMProtectMarker”+MarkerNamе in VMProtect. For example, a marker VMProtectBegin(‘CheckRegistration’) will look as VMProtectMarker “CheckRegistration”. If the name of the marker is not set, it is given a unique name in the form of “VMProtectMarker”+marker_serial_number. You can set the compilation type of the given protected block in VMProtect.
VMProtectBeginVirtualization
void VMProtectBeginVirtualization(const char *MarkerName);
The marker identifying the beginning of the protected area of the code with the predefined “virtualization” compilation type. MarkerName defines the name of the marker. The compilation type of this marker cannot be changed during further work with VMProtect.
VMProtectBeginMutation
void VMProtectBeginMutation(const char *MarkerName);
The marker identifying the beginning of the protected area of the code with the predefined “mutation” compilation type. MarkerName defines the name of the marker. The compilation type of this marker cannot be changed during further work with VMProtect.
VMProtectBeginUltra
void VMProtectBeginUltra(const char *MarkerName);
The marker identifying the beginning of the protected area of the code with the predefined “ultra (virtualization+mutation)” compilation type. MarkerName defines the name of the marker. The compilation type of this marker cannot be changed during further work with VMProtect.
VMProtectBeginVirtualizationByKey
void VMProtectBeginVirtualizationLockByKey(const char *MarkerName);
The marker identifying the beginning of the protected area of the code with the predefined “virtualization” compilation type and the enabled “Lock to Serial Number” option. MarkerName defines the name of the marker. The compilation type of this marker cannot be changed during further work with VMProtect.
VMProtectBeginUltraLockByKey
void VMProtectBeginUltraLockByKey(const char *MarkerName);
The marker identifying the beginning of the protected area of the code with the predefined “ultra (virtualization+mutation)” compilation type and the enabled “Lock to Serial Number” option. MarkerName defines the name of the marker. The compilation type of this marker cannot be changed during further work with VMProtect.
VMProtectEnd
void VMProtectEnd(void);
The marker identifying the end of the protected area of the code. The call to VMProtectEnd must be placed after the last command (procedure or function call) of the protected code block.
VMProtectIsProtected
bool VMProtectIsProtected(void);
The VMProtectIsProtected function returns True if the file is processed by VMProtect.
VMProtectIsDebuggerPresent
bool VMProtectIsDebuggerPresent(bool CheckKernelMode);
The VMProtectIsDebuggerPresent function allows to detect the launch of the application under a debugger. The result (True/False) can be processed with in-app protection mechanisms. If CheckKernelMode=False the function checks for User-mode debuggers (OllyDBG, WinDBG etc.). If CheckKernelMode=True, both User-mode and Kernel-mode debuggers (SoftICE, Syser etc.). When protecting drivers, the value of CheckKernelMode does not make sense, because drivers always work in the kernel mode, so presence of kernel-mode debugger is always checked.
VMProtectIsVirtualMachinePresent
bool VMProtectIsVirtualMachinePresent(void);
The VMProtectIsVirtualMachinePresent function allows to detect the launch of the application under a virtual machine tool: VMware, Virtual PC, VirtualBox, Sandboxie. The result (True/False) can be processed with in-app protection mechanisms.
VMProtectIsValidImageCRC
bool VMProtectIsValidImageCRC(void);
The VMProtectIsValidImageCRC function detects the fact that the executable module has been changed in the memory of the process (only unchangeable segments of code and data are checked). The result (True/False) can be processed with in-app protection mechanisms.
VMProtectDecryptStringA
const char * VMProtectDecryptStringA(const char *Value);
The VMProtectDecryptStringA function decrypts the ANSI string constant – Value. To decrypt the constant, you must include it to the list of protected objects.
VMProtectDecryptStringW
const wchar_t * VMProtectDecryptStringW(const wchar_t *Value);
The VMProtectDecryptStringW function decrypts the Unicode string constant – Value. To decrypt the constant, you must include it to the list of protected objects.
VMProtectFreeString
bool VMProtectFreeString(const void *Value);
The VMProtectFreeString function frees dynamic memory allocated for the decrypted string. It is not necessary to free up memory, but if you do this – you must use this function. If VMProtectDecryptStringA / VMProtectDecryptStringW are used with the same parameters for the second time without destroying previously decrypted string, additional memory is not allocated.
Using ObfuscationAttribute for .NET
VMProtect supports the following values for ObfuscationAttribute.Feature:
- renaming - allows to exclude class/method from renaming. Possible values of ObfuscationAttribute.Exclude=true/false
- virtualization, virtualizationlockbykey, ultra, ultralockbykey, mutation - allows to specify the compilation type for the certain method/methods of class. Possible values of ObfuscationAttribute.ApplyToMembers=true/false.
- strings - allows to obfuscate strings for the certain method/methods of class. Possible values of ObfuscationAttribute.ApplyToMembers=true/false.
For each class/method/property it's possible to specify several attributes at once:
[Obfuscation(Feature = "strings", Exclude = false)]
class Class
{
[Obfuscation(Feature = "renaming", Exclude = true)]
[Obfuscation(Feature = "virtualization", Exclude = false)]
public Foo()
{
...
Examples
The following example adds Class.Foo into the "Functions For Protection" section with the compilation type "Virtualization":
class Class
{
[Obfuscation(Feature = "virtualization")]
public Foo()
{
...
The following example excludes Class.Foo from renaming:
class Class
{
[Obfuscation(Feature = "renaming", Exclude = true)]
public Foo()
{
...
The following example excludes all methods of Class from renaming:
[Obfuscation(Feature = "renaming", Exclude = true, ApplyToMembers = true)]
class Class
{
public Foo()
{
...
Using markers
To protect individual fragments of the code and to protect string constants, you can insert special markers to the source code of your application. Markers are calls to functions imported from SDK library:
For .NET
- VMProtect.SDK.dll
For Windows
- 32-bit user-mode applications - VMProtectSDK32.dll
- 32-bit kernel drivers - VMProtectDDK32.sys
- 64-bit user-mode applications - VMProtectSDK64.dll
- 64-bit kernel drivers - VMProtectDDK64.sys
For Linux
- 32-bit applications - libVMProtectSDK32.so
- 64-bit applications - libVMProtectSDK64.so
For macOS
- libVMProtectSDK.dylib
Procedures and functions in SDK do not perform any actions and are merely labels VMProtect uses to determine boundaries of the protected code. The beginning and the end of the protected block are marked as follows:
C/C++
#include "VMProtectSDK.h"
VMProtectBegin(MARKER_TITLE);
...
VMProtectEnd();
C#
using System.Reflection;
class Foo
{
[Obfuscation(Feature = "virtualization", Exclude = false)]
// Feature = "virtualization", "ultra", "mutation", "virtualizationlockbykey", "ultralockbykey"
public Foo()
{
...
Pascal
uses VMProtectSDK;
VMProtectBegin(MARKER_TITLE);
...
VMProtectEnd;
MASM
include VMProtectSDK.inc
invoke VMProtectBegin,SADD(MARKER_TITLE)
...
invoke VMProtectEnd
Visual Basic
VMProtectBegin (StrPtr(MARKER_TITLE))
...
VMProtectEnd
Also, instead of VMProtectBegin you can use markers with the predefined compilation types:
- VMProtectBeginVirtualization – the marker uses the “Virtualization” compilation type.
- VMProtectBeginMutation – the marker uses the “Mutation” compilation type.
- VMProtectBeginUltra – the marker uses the “Ultra” compilation type.
Markers are processed as follows: when VMProtect analyzes the code of the protected application, it locates all calls to VMProtectSDK procedures and functions. Boundaries of blocks to protect are defined by marker pairs VMProtectBegin / VMProtectBeginVirtualization / VMProtectBeginMutation / VMProtectBeginUltra and VMProtectEnd. Then, when VMProtect processes the code of the protected application, it removes both the markers and any mentions of the VMProtectSDK, so there is no need to include these libraries to your setup package. Markers are removed regardless of whether they are included to compilation or not. When named markers are used, their names are also removed.
If a title for the marker is specified, it is assigned with the name like “VMProtectMarker MARKER_TITLE”. If a title of the marker is not specified, it is assigned with a unique name: “VMProtectMarker”+marker serial number. However, using non-named markers has a significant disadvantage: if a new marker will be inserted to the code of the program, numeration of all non-named markers will change. So we recommend to always use named markers.
A particularly important thing to consider when working with markers is that you shouldn’t allow jumps from non-protected areas inside the marker. This can happen, for example, if you enclose a part of a cycle in markers. If the application that uses markers becomes non-functional after protection, you can detect jumps from non-protected areas and addresses by enabling the “Debug mode” option. In this mode, when the protected application works under the debugger, the latter will interrupt execution of the program if a jump from a non-protected area into the protected debugger is detected. When all such jumps are found, you should either change placement of the markers, or if this is impossible, mark those addresses as external using the GUI version of VMProtect.
Using a PDB/MAP file
To create a MAP-file you should enable the corresponding option in the compiler settings.
Visual Studio
If you develop your application in Visual Studio, this can be done as follows: in the main menu of the IDE, open the project properties (Project – Properties), then on the “Linker – Debugging” tab set the “Generate MAP File” option to “Yes (/MAP)”:
Borland Delphi
If you develop the application using Borland Delphi, the same can be done as follows: in the main menu of the Delphi IDE open the project options(Project – Options) and on the “Linker” tab set the “MAP file” section option to “Detailed”:
After you enabled MAP-file generation, the project must be rebuilt.
Upon loading of a MAP-file VMProtect compares modification date and time of the MAP-file and those of the protected file. If they are different, the MAP-file is not loaded.
Preparing a project
Let us take a look to a very simple application consisting of only one form (Form1), a text element (Edit1) and a button (Button1). The application works as follows: when the Button1 is clicked, the app checks if the password entered is correct and displays a corresponding message.
A password is checked using a very simple algorithm: on the first step we transform it to a numeric form, then we calculate the remainder on dividing it by 17. The password is correct if the remainder on dividing the numeric representation of the entered password by 17 is equal to 13. The password check procedure implementation on Delphi looks as follows:
function TForm1.CheckPassword: Boolean;
begin
Result:=(StrToIntDef(Edit1.Text, 0) mod 17=13);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if CheckPassword then
MessageDlg('Correct password', mtInformation, [mbOK], 0)
else
begin
MessageDlg('Incorrect password', mtError, [mbOK], 0);
Edit1.SetFocus;
end;
end;
Selection of procedures and functions to protect can be done in three ways:
- Using a PDB/MAP file created by the compiler along with the executable of the program. The MAP-file contains all necessary information about names and addresses of all procedures and functions of the app. If the MAP-file is used, you can select procedures and functions to protect by their names. With the MAP-file, every time the project is recompiled, VMProtect automatically determines new addresses of procedures and functions.
- Using markers inserted to the source code of the application. Markers are special marks VMProtect uses to determine the boundaries of the protected fragment. Also, VMProtect supports markers with a predefined compilation type. Using markers makes sense when you only want to protect a part of a function or procedure. Using markers allows you to specify parts of the code where string constants to protect will be further placed.
- By address of protected procedures in the executable file. In comparison with the above two ways, this one is less convenient for use. Every time the application is modified and recompiled, you have to specify all addresses again. This type of protection is recommended for applications without the source code available.
Using a MAP-file to define boundaries of the protected code has one more significant advantage. It is worth reviewing it a bit more. Almost any procedure or function that has local variables or that uses the stack to save registers and/or intermediate calculation results has the so called prologue and epilogue located correspondingly in the beginning and in the end of the compiled procedure or function:
push ebp \
mov ebp, esp \ prologue
push 00 /
push ebx /
...
pop ebx \
pop ecx \ epilogue
pop ebp /
ret /
Due to the way modern compilers work, code markers never incorporate the prologue and the epilogue of a function. Even if the entire code of the CheckPassword function between begin and end is enclosed to markers. It would be enough for a hacker to modify the prologue of the function so that the virtualized code was never executed. For the CheckPassword function this can be done as follows:
mov eax, 1
ret
!Important
If a PDB/MAP file is used to choose code fragments for virtualization, the prologue and the epilogue are also virtualized significantly boosting hack-proof capability of the protected program. Moreover, is one virtualized function is called from another virtualized function, control transfers between them without actually jumping to the address of the called function (a call in this case is a simple jump to another address within the bytecode of the virtual machine interpreter). This also strengthens protection of the program as all modification of the entry point a hacker makes are rendered useless. When working with virtualized functions, transfer of control to the entry point of a virtualized function only happens when the protected function is called from an unprotected or mutated fragment of code.