Introduction

There is no perfect way to protect software against unauthorized use and distribution. No available system can either provide abolsute protection or prevent a potential cracker from disabling it. However, high-quality and effective protection can make the process of cracking a software product as difficult as possible. Moreover, it can make this process inexpedient from the viewpoint of time and effort spent on it. The protection of a software product may achieve all kinds of aims and solve various problems, but the basis of any protection scheme comes down to protection against analysis because it is strength against reverse engineering that determines the effectiveness of protection.

  • Glossary
  • Analyzing, cracking and protecting software
  • What is VMProtect?
  • Protection recommendations

© 2006-2012 Copyright VMProtect Software


Glossary

It is impossible to effectively operate any tool without knowledge of the specific terms used. This glossary defines the terms relating to the use of VMProtect. The glossary is not intended to be universal and define everything completely; the definitions given may differ from the classic ones.

Bytecode: code received after translating native machine instructions into virtual machine instructions.

Virtualization:the process of translating part of the application executable code into instructions of a virtual machine with an instruction set, architecture and logic that is unknown to a potential cracker. Virtualized parts of the code are executed by the interpreter (virtual machine) without being precompiled into native machine code. Generally, the reengineering of virtualized fragments derives from the study of virtual machine architecture. This results in the creation of a disassembler that corresponds to the CPU architecture, which is emulated by the virtual machine and analyzing the disassembled code.

Virtual machine: the software code that directly executes bytecode in the protected program.

Watermarks: a byte array unique to every user allowing the unambiguous identification of the legal owner of the cracked software copy.

Mutation: replacing the source instruction with its analog (or a certain instruction sequence), which gives the same result as the source instruction.

Obfuscation: a set of methods and techniques aimed at making it harder to analyze the software code. Depending on the programming language of the protected program, different types of obfuscators are used. In case applications written in interpreted languages (Perl, PHP, etc) are obfuscated, changes are made in their source code: comments are deleted, variables are given meaningless names, string constants are encrypted and so on. If software written in Java / .NET is obfuscated, the bytecode executed by the virtual machine is transformed. If compiled software is obfuscated, the machine code is modified: "garbage" instructions, "dead code", random jumps are inserted, some operations are moved to the stack, and various structural (more rarely mathematical) transformations are made. During the reengineering of obfuscated parts of code, attempts are made to convert them back to the original, which becomes a very time-consuming task if a comprehensive approach to obfuscation is adopted.

Protector: a program used to protect other software products against being cracked. Most modern protectors do not modify the source code of the application, but pack/encrypt it with close attention to protecting the program (procedure) that unpacks/decrypts the protected software.

EntryPoint: the initial address where the file execution starts.

Packing: an application code protection method when the executable file of the program and/or its inventory is compressed with the help of unusual algorithms. When using packing, the packer compresses the protected software code and when the end user runs the application, it is completely or partially unpacked.

Encryption protecting part of the application with the help of strong encryption algorithms. In cases of software protected with the help of encryption, the end user has to enter the activation code that disables the limitations of the unregistered version imposed by the developer.


Analyzing, cracking and protecting software

It is possible to analyze a software product statically and/or dynamically. In case of static analysis, the development of the protection crack algorithm is based on analyzing the results of dissassembling or decompiling the program being cracked. Dynamic analysis is most often used to crack software products whose executable code is encrypted or dynamically changed because the static analysis of such programs presents some difficulties.

In order to perform dynamic analysis, the program being cracked is run in the debugger environment, which makes it possible to control all changes taking place while the application is running. During the dynamic analysis, the debugging mode is used to get through the "protection" algorithms of the program step-by-step, including algorithms of checking and generating the correct registration code. Activity monitors are another tool used during dynamic analysis. They monitor how the program accesses files, system services, ports and external devices.

Protectors are the main tool used to protect applications against being cracked. Protection offered by most protectors comes down to packing and/or encrypting the source executable file, paying special attention to protecting the procedure of unpacking/decrypting the file.

This kind of algorithm very often turns out to be the reason why the protection provided by most of them is not enough. In case an application is protected by packing, the cracker may get the source file as a result of dumping a certain memory area after the unpacker finishes its work. Moreover, crackers have developed a lot of software tools allowing protection to be broken automatically. A similar approach is used to break encryption: after getting a license key, which may have been legally purchased, the cracker can decrypt protected code parts.

Some protectors use various anti-debugging techniques. However, the use of anti-debugging considerably slows down the performance of the application. You should also bear in mind that anti-debugging techniques are effective only against dynamic analysis and are completely useless against static analysis. Moreover, all anti-debugging techniques used in modern protectors were studied long ago and crackers have written a lot of tools that completely neutralize them. Anti-debugging techniques built into an application do not affect the effectiveness of activity monitors.

Obfuscation and virtualization that make it harder to analyze the code of the protected application are more effective methods for application protection. Generally, the effectiveness of these methods is achieved due to peculiarities of human factors: the more complicated the source code is and the more resources an application uses, the more difficult it is for the person analyzing it to understand the logic of its work and, therefore, break the protection methods used in it.

In case of obfuscation, the application code is obfuscated due to introducing additional instructions. In case of virtualization, the source code is compiled into bytecode executed on a special interpreter emulating a virtual machine with a special instruction set. Therefore, the use of virtualization results in high obfuscation that cannot be reduced in the output code. And if you use a certain approach to the implementation of this method, the protected code will not contain methods for restoring the source code as it was. Thus, the main advantage of virtualization is the fact that at the moment of executing a virtualized part of code it is not translated back into machine code, which excludes the possibility of getting the source code of the application by the cracker.

The reengineering of virtualized fragments comes down to studying the virtual machine architecture, creating the disassembler corresponding to the architecture of the CPU emulated by the virtual machine and analyzing the disassembled code. With a certain approach to the implementation of the virtual machine, it becomes rather time-consuming to create a disassembler for the virtualized code. The only disadvantage of virtualization is a considerably low performance rate so this method should only be used to protect parts of code that do not require a high performance rate.

Obfuscation and virtualization play secondary roles in the overwhelming majority of modern protectors and the level of their implementation is not good enough, which allows crackers to break this kind of protection automatically or manually. Another weak point in most modern protectors is the use of undocumented Windows features, which imposes limitations on using applications in new versions of the operating system or with the DEP mode enabled.


What is VMProtect?

VMProtect is a new generation tool for software protection. VMProtect supports the Delphi, Borland C Builder, Visual C/C++, Visual Basic (native), and Virtual Pascal compilers. At the same time VMProtect contains a built-in disassembler making it possible to work with files based on the EXE, DLL, BPL, OCX, and SYS formats. It is possible to connect a MAP file created by the compiler for quickly selecting parts of code that should be protected. In order to automate application protection operations, VMProtect contains a built-in scripting language. VMProtect completely supports all 32/64-bit Windows operating systems: Windows 95/98/ME, Windows NT, Windows 2000, Windows XP, Windows 2003, Windows Vista, Windows 7 and Windows 8.

The main principle of VMProtect is to provide effective protection for the application code against analysis. The most effective method to prevent program crack attempts lies in the maximum complexity of the logic of the internal application protection mechanisms. The main software code protection methods used in VMProtect are virtualization, mutation and a combination of the two when the application code is mutated first and then virtualized.

An advantage of how the VMProtect virtualization method is implemented is that the virtual machine (which is used to execute the virtualized parts of the code) is built into the output code of the protected application. Therefore, there is no need to use any additional libraries or modules to run an application that is protected with VMProtect. VMProtect allows the use of several different virtual machines to protect different parts of the code in one application, which makes it even harder to break protection because the cracker will have to analyze the architecture of several virtual machines.

The mutation method implemented in VMProtect is based on obfuscation when "garbage" instructions, "dead" code, and random conditional jumps are added to the program. In addition, source instructions are mutated and the execution of certain instructions is moved to the stack.

The key feature of VMProtect that makes it stand out from other protectors is protection of different parts of code with different methods: virtualizing part of the code, obfuscating another part, and using the combined protection method for the most critical parts.

Another unique feature of VMProtect is the inclusion of watermarks in the application code. This allows the unambiguous identification of the legal owner of the cracked software copy and the potential to undertake corresponding measures.

VMProtect comes in 4 versions:

  • Demo (a demo version with limited features);
  • Lite (lite version);
  • Professional (basic version);
  • SenseLock Edition (it is possible to lock software to hardware SenseLock keys).

The functional differences of different VMProtect versions can be seen in the table:

Features | VMProtect version
 | Demo | Lite | Professional | SenseLock Edition | Ultimate
Protection methods
Mutation | +* | + | + | + | +
Virtualization | +* | + | + | + | +
Ultra (mutation + virtualization) | +* | + | + | + | +
Interface types
"Wizard" mode | + | + | + | + | +
"Expert" mode | + | - | + | + | +
Command-line version | + | - | + | + | +
Protection options
Memory protection | + | + | + | + | +
Import protection | - | + | + | + | +
Resource protection | + | - | + | - | +
Packing | - | + | + | + | +
Debugger detection | +** | + | + | + | +
Virtualization tool detection | +** | + | + | + | +
Service features
Watermarks | +*** | - | + | + | +
Scripting language | + | - | + | + | +
Licensing system | + | - | - | + | +
SenseLock key management system | - | - | - | + | -
License manager | + | - | - | - | +
DLL Box | + | - | - | - | +

* - It is possible to protect only one procedure in the Demo version.
** - The features are limited in the Demo version.
*** - Only one watermark is available in the Demo version.

SenseLock Edition

VMProtect SenseLock Edition (VMProtect SE) is a joint product of VMProtect Software and Seculab companies. This edition implements all the modern achievements in software piracy protection area:

  • code virtualization
  • packing and encryption of the protected file
  • execution of code inside the SenseLock dongle
  • support of the demo-licenses, limiting number of runs and working time
  • support of the separate licensing of various application features
  • encrypted protocol of the data exchange with the dongle (RSA-1024) leaves no chances to emulate it

VMProtect SE allows you to prevent software piracy without any special knowledge and in no time. We guarantee the highest possible protection level.

As opposed to most of the other protectors that simply encrypt and pack the code using the dongle, VMProtect SE virtualizes the code and lock the virtual machine that runs it to the dongle. That means that the dongle is used all the time the software runs and there is no way to dump the code after decryption, as there is no decryption at all. A special encrypted software-dongle protocol prevents the emulation because of its floating nature.

You may also use a licensing system to limit the number of software runs or its running time.

Using VMProtect and SenseLock dongles together allows you to get the maximum level of protection, that is order of magnitude better than other dongle-based protectors and envelopes. Add the other SenseLock features like driver-less mode and high stability.

Important:
You need a master-dongle to work with VMProtect SE. To obtain a master dongle, please e-mail at info@vmpsoft.com.


Protection recommendations

VMProtect is a reliable tool for protecting software code against analysis and cracking. However, it is only possible to effectively use it if the protection mechanisms built into the application are designed according to certain rules and have no typical mistakes. Let us consider some aspects relating to the creation of protection mechanisms in an application.

Registration procedure

Software developers often make a common mistake when developing their own registration scheme for an application copy. This mistake involves following a procedure of checking the validity of the entered registration key in a separate function that returns whether the entered key is valid or not:

function CheckRegistration(const RegNumber: String): Boolean;
begin
  if RegNumber='123' then
   Result:=True
  else
   Result:=False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ...
  if not CheckRegistration(RegNumber) then
   exit;
  Application.CreateForm(TForm2, Form2);
  Form2.ShowModal;
  ...
end;

If this approach is used to check the registration code, the cracker does not have to go into the key check algorithm. This is because it will be enough just to change the code at the beginning of the procedure so that it always returns the value corresponding to the valid registration key:

function CheckRegistration(const RegNumber: String): Boolean;
begin
  Result:=True;
  exit;
  ...
end;

A much more effective way is to build the registration code validity check into the program logic. This will mean that it is impossible to separate the registration code check from the algorithm of the procedure calling. It is also recommended to "add" some logic of the program to the registration code check procedure. This will ensure that "bypassing" of the registration procedure will cause errors in the application. The following method can be used in the above example:

function CheckRegistration(const RegNumber: String): Boolean;
begin
  if RegNumber='123' then
   begin
    Application.CreateForm(TForm2, Form2);
    Result:=True
   end
  else
    Result:=False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ...
  Form2:=nil;
  if not CheckRegistration(RegNumber) then
   exit;
  Form2.ShowModal;
  ...
end;

If the CheckRegistration function is implemented this way, the cracker will have to thoroughly analyze how the whole key check function works in order to "bypass" checking of the registration key. If you use VMProtect to protect this application, it is recommended to virtualize not only the CheckRegistration function, but also the TForm1.Button1Click procedure. To make it even more difficult to crack the application, it is possible to use the "Ultra" protection mode combining the mutation of the application code with its virtualization.

Registration key check

Very often programmers make real blunders while implementing the registration key validity check when they compare the entered key to its correct value. In this case the cracker can easily find out the correct key value during single-stepping by viewing the arguments used to call the string comparison function:

var ValidRegNumber: String;
...
function CheckRegistration(const RegNumber: String): Boolean;
begin
  if RegNumber=ValidRegNumber then
   Result:=True
  else
   Result:=False;
end;

To avoid this kind of situation during the comparison of the entered key value to its correct value, it is recommended to use their hashes instead of the real values. As a matter of fact, a hash function is a one-way function, which means it is impossible to determine the value of the valid key by checking hashes. Cracking the application will take much more time spent on analyzing the program because it will be necessary to examine far more parts of the code instead of only the procedure of checking whether the entered key is valid:

var
  HashOfValidRegNumber: Longint;
...
// An example of using Peter Weinberger's (PJW) generic hashing algorithm
function HashPJW(const Value: String): Longint;
var I:Integer;
    G:Longint;
begin
  Result:=0;
  for I:=1 to Length(Value) do
   begin
    Result:=(Result shl 4)+Ord(Value[I]);
    G:=Result and $F0000000;
    if G<&gt0 then
     Result:=(Result xor (G shr 24)) xor G;
   end;
end;

function CheckRegistration(const RegNumber: String): Boolean;
begin
  if HashPJW(RegNumber)=HashOfValidRegNumber then
   Result:=True
  else
   Result:=False;
end;
...
initialization
  HashOfValidRegNumber:=HashPJW(ValidRegNumber);

end.

If you use VMProtect to protect the program, it makes sense to protect the HashPJW and CheckRegistration functions to make the cracker's work harder.

Registration check result storage

As a rule, programmers who spend a lot of time implementing the registration procedure do not pay enough attention to protecting the results of the program registration. In the example below, the state of the global variable that stores the result of the registration check is verified before calling the registration number check function. It is easy for the cracker to find the global variable - it is enough just to check the data segments BEFORE and AFTER the registration. Incidentally, the well-known program ArtMoney is based on a similar principle.

var IsRegistered: Boolean;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  ...
  if not IsRegistered then
   IsRegistered:=CheckRegistration(RegNumber);
  if  not IsRegistered then
   exit;
  ...
end;

To avoid this kind of situation, it is recommended to store all checks related to the program registration in dynamic memory. This is because scanning data segments for changes in memory cells BEFORE and AFTER the registration will be useless in this case. See below for the simplest example showing how to store the result in dynamically allocated memory.

type PBoolean = ^Boolean;

var IsRegistered: PBoolean;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  ...
  if not IsRegistered^ then
   IsRegistered^:=CheckRegistration(RegNumber);
  if  not IsRegistered^ then
   exit;
  ...
end;
...
initialization
  New(IsRegistered);

It is possible to see the simplest examples of protection mechanisms built into the application above. It is only up to the developer to decide how to actually implement the registration procedure, the registration key check function or how to store the registration key check result. It is important to know about mistakes possible during the implementation of application protection mechanisms in order to avoid them.


Version history

Version 2.13

[!] PE: Delayed Import support added
[!] PE: VS2012 compatibility improved
[!] PE: Embarcadero RAD Studio XE3 compatibility improved
[!] PE: MinGW compatibility improved
[!] Ultimate: DLL Box compatibility improved with Windows 8
[+] WebLM: Added support for mbstring.func_overload = 2
[+] WebLM: Activation codes now have "expiration date" field, so VMProtectActivateLicense may return ACTIVATION_EXPIRED, if the current date is past the expiration one
[+] WebLM: Expiration date and maximal build date for modes can be relative to the current date

Version 2.12

[!] PE: fixed some errors in x64 mode
[!] PE: fixed error in applications with IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag
[+] PE: Windows 8 support added
[+] WebLM: Russian and Chinese localizations added

Version 2.11

[!] x64 binaries packing error fixed
[!] PE: import protection error fixed
[!] PE: TLS procession error fixed (for TLS sections with relocs)
[+] Ultimate: activation module added
[+] Mach-O: Packing added
[*] PE: DriverVerifier support added
[*] PE: Better SEH support for MSVC compiler
[*] Mach-O: Better support for IndirectSymbols

Version 2.1

[!] Resource protection error fixed
[!] Import protection error fixed
[!] Fixed protection of 64-bit drivers without relocs
[+] Mac OS X files (Mach-O) support added
[+] Added support of HLT command
[+] New API in the SDK: VMProtectFreeString
[*] Markers and encrypted strings detection enhanced
[*] Relative paths are supported in the source file name and licensing parameters
[*] SEH support enhanced for Delphi
[*] SenseLock Edition: resource protection feature added
[*] SenseLock Edition: GUI version got a license manager
[*] SenseLock Edition: GUI version got a dongle flashing feature
[*] License manager allows copying and pasting licenses from the clipboard

Version 2.09

[!] Debugger detection fixed for Windows 2000
[!] Fixed the disassembling of PMULHUW and LDMXCSR commands
[!] Memory protection issue fixed (related to checking the file on disk)
[!] Ultimate: fixed problem with running of protected files with licensing on Windows 2000
[*] Enhanced Visual C exception handlers virtualization
[*] Resource protection is available in Professional edition
[*] Resource protection enhanced with support of EnumResourceTypes, EnumResourceNames and EnumResourceLanguages functions (for the main executable only)
[+] Added support for Borland C Builder exceptions
[+] Ultimate: Improved speed of VMProtectGetSerialNumberState and VMProtectGetSerialNumberData functions
[+] Scripting class TLicenseManager enhanced with the Save method
[-] The "Move resources to the end of file" options is removed - the resources are always moved to the end of file now.

Version 2.08

[!] Fixed issue with the assembling of TEST [XXXX], REG command
[!] Fixed issue with the disassembling of x64-commands with 0x67 prefix
[*] Enhanced support of files with small SectionAlignment value
[*] Enhanced markers and strings detection for Intel Compiler 12 and Lazarus
[*] Enhanced import protection
[*] Ultimate: Support of "licenses-only" projects
[+] Ultimate: Added resource protection
[+] Ultimate: Classes TLicense and TLicenseManager are added to scripting

Version 2.07

[!] Fixed error in license manager, if it runs on non-English version of Windows
[+] Error messages are fully customizable now
[+] Added "Messages" property to TVMProtector in scripts - allows to customize error messages
[*] Enhanced compatibility the memory protection with some antivirus products

Version 2.06

[!] Fixed marker detection in MinGW files
[!] Fixed import protection bug in 64-bit files
[!] Fixed virtualization of FILD WORD PTR [XXXX] instruction
[!] Fixed the incorrect disassembling of PSRLW instruction
[!] Fixed DLL Box errors with some antivirus applications
[*] VMP files are now in XML format
[*] Scripts are stored in VMP files as well, VMS are not used anymore
[*] Ultimate: License Manager is moved to VMProtect interface
[*] Ultimate: The speed of code locked to serial number is significantly improved
[+] Added support of MinGW map files
[+] Added support of JCLDEBUG library
[+] Scripts: new method Clear in TPEDirectory
[+] Scripts: new properties InputSize and OutputSize in TVMProcedure
[+] Ultimate: Two new API functions in SDK: VMProtectBeginVirtualizationLockByKey and VMProtectBeginUltraLockByKey

Version 2.05

[!] Fixed string detection errors for MinGW applications
[!] Fixed "Out of system resources" error, caused by adding a great number of procedures to a project
[!] Fixed string output error in the console version
[*] Timestamps of MAP files are now taken from the MAP files' content
[+] Ultimate: Added licensing system examples (MSVC and Delphi)
[+] Ultimate: Added source code of MSVC serial number generator
[+] Ultimate: Added DLL Boxing (EXE files only)
[+] Ultimate: New script class added: TInternalDLLs
[+] Ultimate: Script class TVMProtector got a new property InternalDlls
[+] Script class TMapRecord got a new property SegmentName
[+] Script class TIntelRecord got a new property Operands
[+] Czech language added

Version 2.04

[!] Fixed error related to the obfuscation of FDIVR [ESP+XXXX]
[!] Fixed error related to the obfuscation of SWITCH in x64 applications
[!] Small glitches fixed
[*] Memory protection now checks the file on disk as well
[*] Ultimate: Better integration of licensing module and protected application
[+] Files with overlays are now supported
[+] Ultimate: Added serial number generator example on C#
[+] Ultimate: License manager now exports product data for C# serial number generator
[+] Ultimate: Console version has a new parameter "-lf" that defines file with licensing parameters
[+] French help file was added

Version 2.03

[!] Fixed import protection bug for SYS-files under Windows 7 (base address of hal.dll was not detected properly)
[!] Fixed incorrect obfuscation of commands that use CRx reigsters in SYS-files
[!] Small bugs fixed
[*] Licensing system improved in Ultimate version
[+] French language added

Version 2.02

[!] SenseLock Edition's bug fixed, that may appear while using several licenses simultaneously if they are stored in the same dongle (it is neccessary to update license in the master key)
[!] Entry Point disassembly error fixed (for Delphi apps only)
[!] Small bugs were fixed
[*] Licensing system improved in the Ultimage edition
[*] SenseLock Edition uses VMProtect Ultimate licensing (it is neccessary to update license in the master key)
[*] Improved detection of strings, marked with VMProtectDecryptStringA/VMProtectDecryptStringW
[+] Unicode support added
[+] Added Chinese Traditional translation

Version 2.01

[!] Small bugs fixed in installer, License Manager and SDK
[*] VMProtect now uses its own licensing system
[+] Added Chinese Simplified language

Version 2.0

[!] Bugs have been fixed solving the errors during disassembly and virtualization of the CALL FWORD PTR [XXXX] and JMP FWORD PTR [XXXX] instructions
[!] Bug fixed solving the error in the SenseLock Edition during the key serial number request after the standard user PIN is changed
[!] Bug fixed solving the error during the virtualization of the SWITCH in the x64 applications
[!] Bug fixed solving the error during the virtualization of the ROL, ROR, RCL, RCR, SHL, SHR, SAL and SAR instructions, which have REF preffix in the x64 applications
[!] Minor bugs have been fixed
[*] The Russian help file has been updated
[*] The English help file has been updated
[+] The licensing system has been added (available only in the Ultimate version)
[+] A new API is now available in the SDK - VMProtectDecryptStringA and VMProtectDecryptStringW
[+] The CompilationOptions property for the TVMProcedure class has been added to the script
[+] The SELicense and SEVersion properties for the TVMProcedure class has been added to the script (available only in SenseLock Edition)
[+] The VMSectionName and VMExecutorCount properties for the TVMProtector class has been added to the script
[+] The SEMasterPassword, SEUserPIN, SEKeyID, SELicense and SEVersion properties for the TVMProtector class has been added to the script (available only in SenseLock Edition)
[-] The "Create online commands dynamically" option has been removed

Version 1.81

[!] Bug fixed solving the error during the execution of mutated code on Pentium 4 CPUs
[!] Bug fixed solving the error during the execution of virtualized code on some Athlon CPUs
[!] Bugs have been fixed solving the errors during disassembly and virtualization of structures describing exception handlers with a filter for the exception class (for programs in Delphi and Borland C++ Builder)
[!] Bug fixed solving the error in case several VMs in 64-bit files are used
[!] Minor bugs have been fixed
[*] Marker names are now better determined (for programs in Visual C)
[*] RAM usage during the compilation process has been considerably reduced
[*] The "HTML" resource is no longer packed
[*] The Russian help file has been updated
[+] The list of procedures by mask has been added in the "Wizard" mode
[+] A new packing mode with a higher compression rate is now available
[+] Files with TLS callbacks are now supported
[+] A new API is now available in the SDK - VMProtectIsValidImageCRC

Version 1.8

[!] Bugs have been fixed solving the errors during the import protection in SYS files
[!] Bugs have been fixed solving the errors during the memory protection in DLL/SYS files
[!] Bugs have been fixed solving the errors during the use of several VMs in DLL/SYS files
[!] Bug fixed solving the error during the mutation of the code with an exception handler
[!] Bug fixed solving the error in cases of memory protection and watermarks used simultaneously
[!] Minor bugs have been fixed
[*] References to strings marked with markers are now better determined (no need to mark all references to the protected string any more)
[*] Import protection has been improved
[*] Debugger detection has been improved
[*] The Russian help file has been updated
[+] Now it is possible to save project files with different names in the GUI version
[+] Now it is possible to open project files
[+] Sandboxie detection has been added
[+] Files compiled with the /SAFESEH key are now supported
[+] CodeGear 2009 is now supported
[+] Now it is possible to name markers (when using VMProtectBegin/VMProtectBeginMutation/VMProtectBeginVirtualization/VMProtectBeginUltra from VMProtectSDKxx.dll/VMProtectDDKxx.sys)
[+] New APIs (VMProtectIsDebuggerPresent and VMProtectIsVirtualMachinePresent) have been added to the SDK
[+] The "Remote update and licensing system" tool has been added for the SenseLock Edition version
[+] Association with the key identifier has been added for the SenseLock Edition version

Version 1.7

[!] Sections with SHAREABLE+WRITABLE flags are no longer packed
[!] Minor bugs have been fixed
[*] The import protection for SYS files has been improved
[*] SYS files are now packed better (the WRITABLE flag is no longer set for packed sections)
[*] Debugger detection has been improved
[+] The "Memory protection" option has been added (this option protects memory against changes while the application is running)
[+] The "Virtualization tool detection (VMware/Virtual PC)" option has been added
[+] The EntryPoint property for the TPEFile class has been added to the script

Version 1.65

[!] Minor bugs have been fixed
[*] Import protection has been improved
[*] Debugger detection has been improved
[+] It is now possible to detect unpaired markers (unpaired markers are available in the list of all objects)
[+] Markers with predefined compilations types are now available: VMProtectBeginVirtualization, VMProtectBeginMutation and VMProtectBeginUltra
[+] Traditional Chinese has been added

Version 1.64

[!] Bug fixed solving the error when a packed file with the "TYPELIB", "REGISTRY" and "MUI" resources is running
[!] Bug fixed solving the error R6002 when a packed file is running (for programs in Visual C)
[!] Bug fixed solving the error when the "Import protection" option is used in 64-bit SYS files
[!] Minor bugs have been fixed
[*] The settings file is now saved to the %APPDATA%/PolyTech/VMProtect folder
[*] Import protection has been improved
[+] The "Virtual Machines" option has been added (different virtualized sections of code will be run on different virtual machines with different architectures)
[+] The "Debugger detection" option has been added (in case a debugger is detected, the protected application will show the corresponding message and be closed)
[+] The IncludedInPacking property for the TPESection class has been added to the script
[+] IRETD is now supported
[+] An example with markers used in Lazarus has been added

Version 1.63

[!] Bug fixed solving the casing errors when the "Import protection" option is used
[!] Minor bugs have been fixed
[+] The OnBeforePackFile event has been added to the script
[+] The Handle property for the TPEFile class has been added to the script
[+] An example with markers used in Free Pascal has been added
[*] The control over the integrity of VM objects has been improved

Version 1.62

[!] Bug fixed solving the casing errors when the "Import protection" option is used
[!] Bug fixed solving the error when an exception in the TRY/EXCEPT/END block is processed in the "Ultra" mode (for programs in Delphi and Borland C++ Builder)
[*] The import protection for SYS files has been improved
[+] Each object in a project can now have its compilation options for more precise performance configuration (individual options are configured in the project tree)
[+] The "Move resources to end of file" option has been added
[+] An example of using extended markers has been added (the Examples\Scripts\UseExtMarkers folder)

Version 1.61

[!] Bug fixed solving the error during the launch of packed files under Windows 95/98
[!] Minor bugs have been fixed
[+] Now it is possible to pass the file name in the command line in the Lite version
[+] History has been added in the "Wizard" mode
[+] Language selection has been added in the "Wizard" mode
[+] The "Import protection" option has been added (it is recommended to use it together with packing the output file)
[+] The "-we" parameter has been added in the command-line version (all warnings will be displayed as errors if this parameter is used)
[+] The TPEResourceRecords class has been added to the script
[+] The ResourceRecords property for the TPEFile class has been added to the script

Version 1.6

[!] Minor bugs have been fixed
[*] Working with the same strings has been improved (now the same strings are displayed as one object containing several records)
[+] It is now possible to pack the output file
[+] The TPEExportRecords class has been added to the script
[+] The ExportRecords property for the TPEFile class has been added to the script
[+] The OnBeforeSaveFile event has been added to the script
[+] The help file is now available in Chinese (translated by Wang Xiaopeng)
[+] When a MAP file is loaded, its modification date and time are checked (if the modification date and time of the MAP file differ from the modification date and time of the protected file, the MAP file will not be loaded)

Version 1.56

[!] Bug fixed solving the error during the virtualization of the BT, BTC, BTR and BTS instructions
[!] Minor bugs have been fixed
[*] The list of protection options is sorted by their priority
[*] The SEH support for VC8 has been improved
[*] The dynamic creation of online commands has been improved
[*] The control over the integrity of VM objects has been improved
[+] The PREFETCH and PREFETCHW instructions are now supported
[+] The CompareText and AnsiCompareText functions have been added to the script
[+] The size property for the TPEFile class has been added to the script
[+] An example with markers used in MinGW has been added
[+] The Chinese language has been added (translated by Wang Xiaopeng)
[+] Now it is possible to search with the "*" and "?" wildcards in the Add Procedure dialog box
[+] Information about the size of the output file is now displayed in compiler messages

Version 1.55

[!] Bug fixed solving the error while working with a file having the unaligned ImageSize value
[!] Bug fixed solving the error while determining the beginning of the TRY block for programs written in Delphi
[!] Bug fixed solving the error while processing mixed code from Discardable and NonDiscardable sections in SYS files
[!] Bug fixed solving the error during the virtualization of 64-bit DLL/SYS files
[*] The "Encrypt registers at VM output" option has been restored
[+] The TIniFile class has been added to the script
[+] Additional properties and methods have been added to the script for the TPEFile class
[+] The calculator for estimating address offsets in a file has been added in the GUI version
[+] The Italian language has been added (translated by Gabriele Lanza)

Version 1.54

[!] Bug fixed solving the error during the mutation of the PUSH ESP instruction
[!] Bug fixed solving the error during the virtualization of the FLDCW [ESP+xxxx] instruction
[!] Bug fixed solving the error during the obfuscation of bytecode
[*] Improvements in the GUI
[+] The German and Spanish languages have been added
[+] Scripts are now supported
[+] The parameter for specifying the name of the script file has been added in the command-line version

Version 1.53

[!] Bugs have been fixed solving the errors during the virtualization of code in DLL/SYS files
[*] Improvements in code virtualization
[*] Running percentage numbers have been removed in the command-line version when the text output is redirected to a file
[+] It is now possible to insert markers for x86-64
[+] It is now possible to create folders in the project
[+] The parameter for specifying the name of the project file has been added in the command-line version

Version 1.52

[!] Bug fixed solving the error during the mutation of the CMOVxx, CMPXCHG and CMPXCHG8B instruction
[!] Bug fixed solving the error during the mutation of the LEA ESP,[ESP+xxxx] instruction
[*] The RCL and RCR instructions have been added to the VM basic logic
[+] x86-64 is now supported
[+] Now it is possible to specify the name of the output file on the "Options" tab in the GUI version
[+] Help in the Russian and English languages has been added

Version 1.51

[!] Bug fixed solving the error while creating a zero-length "Relocation Table"
[!] Bug fixed solving the error occurring when the CALL [xxxx] instruction is mutating in the "Mutation" mode in DLL/SYS files
[!] In the Delphi example, the instruction preventing VMProtectBegin from optimization has been added to the VMProtectBegin statement (the optimization of VMProtectBegin could prevent the protected file from running correctly)
[+] The "Autosave project after compilation" option has been added

Version 1.5

[!] Bug fixed solving the error when files were dragged and dropped onto the main window
[!] Bug fixed solving the error while working with files having the "Read only" attribute
[!] Minor bugs during compilation in the "Ultra" mode have been fixed
[*] Now the protected file has exactly the same attributes as the original file
[+] Strings can now be added to the list of protected procedures (strings must be marked with markers)
[+] Now the distribution package has examples with markers in Delphi, MASM, Visual Basic and Visual C++
[+] The "Intregrate into shell" option has been added

Version 1.4

[!] Bug fixed solving the error during the mutation of the IMUL, MUL, IDIV, DIV instructions
[!] Bug fixed solving the error during the virtualization of several consecutive CASE tables
[!] Bug fixed solving the error during the virtualization of the POP word/dword ptr [esp+xxxx] and IMUL reg8 instructions
[!] Bug fixed solving the error while disassembling the MOVNTPS and MOVNTPD instructions
[+] A new compilation type has been added: "Ultra (mutation + virtualization"
[+] The compilation type option has been added to the dialog box for adding/editing a procedure
[+] Searching processes for watermarks has been added (it is used to search packed files for watermarks)
[*] Compatibility with ASM compilers has been improved (TASM, MASM, etc)

Version 1.3

[!] Bug fixed solving the error during the emulation of the FILD word ptr [xxxx] instruction
[+] It is possible to specify the compilation type for every procedure ("Mutation" or "Virtualization")

Version 1.25

[+] SEH for VC7-8 is now supported
[+] Markers are now supported in Visual Basic (the start and end of the section are marked with insertions in Basic – Call VarPtr("VMProtect begin") and Call VarPtr("VMProtect end"))
[*] Now the VM interpreter is completely transformed in the registered version of the program (it prevents signature scanners like PEiD, RDG Packer Detector, etc from automatic protection detection)

Version 1.24

[+] It is now possible to insert watermarks in the protected file
[*] The VM interpreter now completely supports multithreaded applications (now there is no need to create a procedure allocating memory for interpreter needs)
[-] The option for selecting the memory allocation function has been removed

Version 1.23

[!] Minor drawbacks have been fixed
[+] The "Wizard" mode has been added
[+] References are now shown in the procedure tree
[*] The "Hide constants", "Create online commands dynamically", "Encrypt registers at jumps", "Control the integrity of VM objects" options and others are now available only for registered users

Version 1.22.2

[!] Bug fixed solving the error during the compilation of projects with the "Interpreter memory allocation" option enabled

Version 1.22

[!] Minor bugs have been fixed
[+] It is now possible to control the integrity of bytecode (when the "Control the integrity of VM objects" option is enabled)

Version 1.21

[!] Bug fixed solving the error in the CMPXCHG8b emulation if the EAX and EDX registers are included in operands
[+] Only those instructions involved in the bytecode execution are present in the intepreter
[-] Some instructions removed from the VM logic (making it more difficult to analyze and decompile the bytecode)

Version 1.2

[!] Bug fixed solving the error while moving the Bound Import directory
[+] It is now possible to include sections of code marked with markers in the project (the start and end of such a section are marked with insertions in assembler – "db $EB,$10,'VMProtect begin',0" and "db $EB,$0E,'VMProtect end',0")
[+] It is now possible to include an exported function in the project
[-] The "Encrypt bytecode" option has been removed (bytecode is now always encrypted)

Version 1.1

[!] Bugs have been fixed solving errors during the emulation of SBB (the incorrect state of some flags), CMOVxx, CMPXCHG and CMPXCHG8b (when addresses with the ESP register or a DWORD value were used as the second argument)
[+] SEH is now supported in VB6 (native)
[+] MAP files generated by IDA are now supported
[+] It is now possible to add several procedures at once
[+] Now references to instructions are shown right in the procedure listing
[+] The "Encrypt registers at jumps" option has been added

Version 1.09

[*] Completion of the process of determining the end of a procedure during the disassembling of loops
[+] It is now possible to clear the Sharable checkbox for Writable section for the compatibility with UPX (when only the reloc table is used for VM needs)

Version 1.08

[*] Large sections of code can be disassembled faster
[*] The bytecode interpreter metamorphism mechanism has been completed

Version 1.07

[!] Bugs fixed solving the errors in processing PE files (when the virtual size of code sections qeuals zero), obfuscating FPU instructions (errors led to incorrect results), disassembling (the CWD instruction was detected instead of CWDE), and emulating the ROL and ROR instructions (if the size of an argument was 1 byte)
[+] The group of SSE2 instructions is now supported

Version 1.06

[!] Bugs fixed solving the errors that led to the incorrect state of the A flag after the emulation of the SUB, CMP, DEC, NEG, CMPXCHG, SCAS and CMPS instructions
[!] Bug fixed solving the error while working with an "unaligned" file
[+] The BSF, BSR and CMPXCHG8b instructions are now supported
[+] It is now possible to specify the procedure of memory allocation for the VM interpreter in case of multithreaded applications (the memory allocation procedure must be defined as fastcall)

Version 1.05

[+] The FUCOMPP and FCMOVxx instructions are now supported
[+] While viewing sections of code, it is now possible to see string types (ANSI/Unicode/Pascal) and comments for the fields of the structures created by compilers
[*] The bytecode interpreter metamorphism procedure has been added
[+] New compilation options are added: "Encrypt bytecode" and "Remove fixup elements (only for EXE files)"
[-] The "Obfuscate bytecode" option has been removed (bytecode is now always obfuscated)

Version 1.04

[!] Bug fixed solving the error in detecting SEH debuggers for C++
[*] The bytecode obfuscator has been completely revised
[+] Fixup elements are now highlighted when viewing code sections

Version 1.03

[+] Using "unoccupied space" freed from the original sections of the code for storing the bytecode
[+] Now "short" strings in the pascal format are detected (the first byte is the length of the string)

Version 1.02

[!] Bugs fixed solving the errors in the execution of online commands in DLL/SYS
[*] The algorithm of bytecode checksum calculation has been modified
[+] The "Control the integrity of VM interpreter" option is now available (it also functions as an anti-debugging tool in case of debugging the interpreter if the debugger uses INT 03 as breakpoints)
[+] Using the "old" table of fixup elements for storing the bytecode

Version 1.01

[!] Bug fixed solving the error while creating files with fixup elements (the error occurred while working with DLL/SYS files in the Windows 95/98/Me operating systems)
[!] Minor bugs in the disassembler have been fixed
[+] The F2XM1, FABS, FCLEX, FCOS, FDECSTP, FINCSTP, FINIT, FLDCW, FLDLN2, FLDLG2, FPREM, FREM1, FPTAN, FRNDINT, FSIN, FSTCW, FTST, FYL2X, FPATAN, FLDZ, FISTP instructions are now supported
[+] MAP files created by Borland C Builder are now supported
[+] Now it is possible to select the language of the interface and messages in the program settings (Russian and English languages are available)

Version 1.0

[!] Minor bugs related to the interface have been fixed
[+] The CMOV and CMPXCHG instructions are now supported

Version 0.97

[!] Minor bugs related to the interface have been fixed
[+] Virtual Pascal is now supported

Version 0.96

[!] Bugs in the compiler have been fixed (incorrect work with the C flag during the compilation of the NEG and SBB instructions; processing POPF more correctly; incorrect bytecode CRC check while processing external conditional jumps)
[!] Bugs in the VM interpreter relating to bytecode obfuscation have been fixed
[+] MAP files generated by C/C++ compilers are now better processed (the names of procedures contain information about arguments)

Version 0.94

[!] Several bugs have been fixed


Working with VMProtect

It is possible to work with VMProtect in the following three modes:

  • Preparing a project
  • GUI mode
    • Main menu
    • Main window
  • Command-line mode


Preparing a project

Let us take the simplest application as an example. It consists of one form (Form1), a text input field (Edit1) and a button (Button1). The application does the following: once Button1 is clicked, the application checks if the entered password is correct and shows the corresponding message.

The following simple algorithm is used to check the password: it is converted into a number first, then this number is divided by 17 for the remainder to be found. The password is considered correct if the remainder is 13, after dividing the numerical representation of the password by 17. The password check procedure is implemented in the Delphi programming language in the following way:

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;

There are three ways to select procedures and functions for protection:

  • Using the MAP file generated by the compiler when the executable module of the program is created. This MAP file contains all necessary information about names and addresses of all procedures and functions in the application. In cases where the MAP file is used in work with VMProtect, it is possible to select procedures and functions for protection by their names. Using the MAP file, VMProtect will automatically determine the new addresses of procedures and functions after recompilation.
  • It is possible to use markers inserted into the source code of the application. Markers are special marks that VMProtect uses to determine the borders of the protected block. In addition, VMProtect supports markers with the predefined type of compilation. It makes sense to use markers if only part of a procedure or function needs to be protected. If markers are used, parts of code can be specified where string constants will later be protected.
  • The addresses of procedures to be protected can be used in the executable file. When compared to others, this method is less convenient because all addresses have to be determined again after any program changes are made and recompilation has occurred. This method is recommended only for protecting software products that do not possess any source code.

Using the MAP file to determine the borders of protected code has a further advantage that should be highlighted. The prologue and epilogue are located at the beginning and end of the compiled procedure or function for almost any procedure or function with local variables or that employ the stack to store registers and/or intermediate calculation results:

push ebp      \
mov ebp, esp   \ prologue
push 00        /
push ebx      /

...

pop ebx       \
pop ecx        \ epilogue
pop ebp        /
ret           /

Due to the peculiarities of implementation in modern compilers, code markers never cover either prologue or epilogue (even if the markers cover the entire text of the CheckPassword function located in-between the beginning and end). To crack the program, it will be enough to edit the function prologue so that the virtualized code is not executed. It can be done for the CheckPassword function in the following way:

mov eax, 1
ret

Important:
If you use the MAP file to select the virtualized sections of the code, the prologue and epilogue will also be virtualized, which will enhance the protection of the program. Moreover, if one virtualized function is called from another virtualized function, control will be handed to the called function without going to its address (the call will be a simple jump to another address in the bytecode inside the virtual machine interpreter). This will also increase the level of program protection since corrections the cracker will make in the entry point will be useless. In case of virtualized functions, control will be handed over to the entry point of the virtualized function only when the protected function is called from an unprotected or mutated section of the code.


Using a MAP file

To create a MAP file, you should enable the corresponding option in the compiler settings.

Borland Delphi

If you use Borland Delphi, it is possible by doing the following: use the main menu of the Delphi IDE to open the project options (Project - Options) and select the "Detailed" option in the "MAP file" group on the "Linker" tab:

Visual Studio

If you use Visual Studio, it is possible by doing the following: use the main menu of the Visual Studio IDE to open the project properties (Project - Properties) and set the "Generate MAP File" option to "Yes (/MAP)" on the "Linker - Debugging" tab:

After the MAP file is enabled, you should completely recompile the project.

While loading the MAP file, VMProtect compares the modification date and time of the protected file and the MAP file. If they do not coincide the MAP file is not loaded.


Using markers

Markers are inserted in the code to protect separate sections of the code and also protect string constants. Markers are calls of imported procedures stored in an external DLL (VMProtectSDK32.dll is used for 32-bit applications and VMProtectSDK64.dll is used for 64-bit applications; VMProtectDDK32.sys and VMProtectDDK64.sys respectively are used to protect drivers), VMProtectSDK are used hereinafter. Procedures and functions located in VMProtectSDK do not do anything and serve only as markers by which VMProtect determines the borders of the protected code. Correspondingly, the beginning and end of a protected block are marked in the following way:

  • Delphi
    • uses VMProtectSDK;

VMProtectBegin(MARKER_NAME);
...
VMProtectEnd;
  • C/C++
    • #include "VMProtectSDK.h"

VMProtectBegin(MARKER_NAME);
...
VMProtectEnd();
  • MASM
    • include VMProtectSDK.inc

invoke VMProtectBegin,SADD(MARKER_NAME)
...
invoke VMProtectEnd
  • Visual Basic
    • Call VarPtr("VMProtect begin")
...
Call VarPtr("VMProtect end")

It is also possible to use markers with predefined compilation types instead of VMProtectBegin:

  • VMProtectBeginVirtualization - the marker will use the "Virtualization" type of compilation.
  • VMProtectBeginMutation - the marker will use the "Mutation" type of compilation.
  • VMProtectBeginUltra - the marker will use the "Ultra" type of compilation.

Important:
To protect markers, they should be added to the project in the GUI mode. It is also possible to automatically add all markers with the help of scripts.

Markers are processed in the following way: while analyzing the code of the protected application, VMProtect finds all calls of the VMProtectSDK procedures and functions. The borders of protected blocks are determined by the pairs of the following markers: VMProtectBegin/VMProtectBeginVirtualization/VMProtectBeginMutation/VMProtectBeginUltra and VMProtectEnd. When VMProtect processes the project, it removes markers and any mention of the VMProtectSDK libraries so there is no need to include these libraries in the software distribution package. Markers are removed no matter whether they are included in the compilation or not. If you use named markers, their names are also removed.

If you specify the name of a marker, it will be named "VMProtectMarker MARKER_NAME". If no name is specified for a marker, it will be given a unique name of the following type: "VMProtectMarker"+marker ordinal number. However, unnamed markers have one serious drawback: if you insert a new marker in the source code of the program, the numbers of unnamed markers will change so it is always makes sense to use named markers for software protection.

One important thing you should take into account while working with markers is that you must not allow jumps from unprotected sections inside a marker, which may occur, for example, when you mark only part of a loop with a marker. In case your application does not work after you protect it using markers, you can find out the addresses of jumps from unprotected sections by enabling the "Debug mode" option. It is possible to run the protected application in the debugger in this mode; the debugger will stop the execution of the program when it detects jumps from an unprotected section of the code. After you detect all jumps of this kind, you should either change the position of markers in the code of the program or, if possible, mark these addresses as external using the GUI version of VMProtect.

Important:
When VMProtect is used to protect applications, markers cannot be embedded and only inside markers will be considered the borders of the block. Only VMProtectBegin2 and VMProtectEnd1 will be considered the borders of the block in the example below. The rest of the markers will be considered unpaired, ie VMProtectBegin1 will be considered as an unpaired marker named UnpairedVMProtectBegin1, and VMProtectEnd2 will be considered as an unpaired marker named UnpairedVMProtectEnd1:

VMProtectBegin // 1
...
VMProtectBegin // 2
...
VMProtectEnd // 1
...
VMProtectEnd // 2

You can see unpaired markers on the "Dump" tab in the "Expert" mode:

Also, markers can be used to mark blocks of the code containing string constants that should be protected:

procedure TForm1.Button1Click(Sender: TObject);
begin
 VMProtectBegin(nil);
 if StrToIntDef(Edit1.Text, 0) mod 17=13 then
  MessageDlg('Correct password', mtInformation, [mbOK], 0)
 else
  begin
   MessageDlg('Incorrect password', mtError, [mbOK], 0);
   Edit1.SetFocus;
  end;
 VMProtectEnd;
end;

In this case, you do not have to include the marker in the list of protected objects:


SDK functions

The SDK functions are used for integration into the source code of the protected application and serve to define the borders of protected sections and detect the use of debugging or virtualization tools.

Code markers

  • VMProtectBegin
  • VMProtectBeginVirtualization
  • VMProtectBeginMutation
  • VMProtectBeginUltra
  • VMProtectBeginVirtualizationLockByKey
  • VMProtectBeginUltraLockByKey
  • VMProtectEnd

Utils

  • VMProtectIsDebuggerPresent
  • VMProtectIsVirtualMachinePresent
  • VMProtectIsValidImageCRC
  • VMProtectDecryptStringA
  • VMProtectDecryptStringW
  • VMProtectFreeString

Licensing functions

  • VMProtectSetSerialNumber
  • VMProtectGetSerialNumberState
  • VMProtectGetSerialNumberData
  • VMProtectGetCurrentHWID

VMProtectBegin

procedure VMProtectBegin(MarkerName: PAnsiChar);

VMProtectBegin, the marker that starts the protected section of the code, must be called before the first instruction (calling a procedure or function) in the protected block of the code. MarkerName defines the marker name that will look like "VMProtectMarker"+MarkerName in VMProtect. For example, the VMProtectBegin('CheckRegistration') marker will look like VMProtectMarker "CheckRegistration". If no name is specified for a marker, it will be given a unique name of the following type: "VMProtectMarker"+marker ordinal number. The compilation type of the protected section is specified when the project is processed in VMProtect.

VMProtectBeginVirtualization

procedure VMProtectBeginVirtualization(MarkerName: PAnsiChar);

The marker that starts the protected section of the code with the predefined "virtualization" compilation type. MarkerName defines the marker name (see VMProtectBegin). It is impossible to change the compilation type specified by the marker later in VMPotect.

VMProtectBeginMutation

procedure VMProtectBeginMutation(MarkerName: PAnsiChar);

The marker that starts the protected section of the code with the predefined "mutation" compilation type. MarkerName defines the marker name (see VMProtectBegin). It is impossible to change the compilation type specified by the marker later in VMPotect.

VMProtectBeginUltra

procedure VMProtectBeginUltra(MarkerName: PAnsiChar);

The marker that starts the protected section of the code with the predefined "ultra" (mutation + virtualization) compilation type. MarkerName defines the marker name (see VMProtectBegin). It is impossible to change the compilation type specified by the marker later in VMPotect.

VMProtectBeginVirtualizationLockByKey

procedure VMProtectBeginVirtualizationLockByKey(MarkerName: PAnsiChar);

The marker that starts the protected section of the code with the predefined "virtualization" compilation type and the option "Lock to key". MarkerName defines the marker name (see VMProtectBegin). It is impossible to change the compilation type specified by the marker later in VMPotect.

VMProtectBeginUltraLockByKey

procedure VMProtectBeginUltraLockByKey(MarkerName: PAnsiChar);

The marker that starts the protected section of the code with the predefined "ultra" (mutation + virtualization) compilation type and the option "Lock to key". MarkerName defines the marker name (see VMProtectBegin). It is impossible to change the compilation type specified by the marker later in VMPotect.

VMProtectEnd

procedure VMProtectEnd;

The marker that ends the protected section of the code. VMProtectEnd must be called after the last instruction (calling a procedure or function) in the protected block of the code.

VMProtectIsDebuggerPresent

function VMProtectIsDebuggerPresent(CheckKernelMode: Boolean): Boolean;

This VMProtect API function allows you to detect when the application is running in the debugger. The result of its work (True/False) can be processed by the protection mechanisms built into the application. If CheckKernelMode=False, the function checks for the user-mode debugger (OllyDBG, WinDBG, etc.). If CheckKernelMode=True, both user-mode and kernel-mode debuggers will be detected (SoftICE, Syser, etc.). In case of driver protection, CheckKernelMode does not matter because drivers run in the kernel mode so it is always checked if kernel-mode debuggers are running for them.

VMProtectIsVirtualMachinePresent

function VMProtectIsVirtualMachinePresent: Boolean;

This VMProtect API function allows you to detect when the application is running in a virtual environment: VMware, Virtual PC, VirtualBox, Sandboxie. The result of its work (True/False) can be processed by the protection mechanisms built into the application.

VMProtectIsValidImageCRC

function VMProtectIsValidImageCRC: Boolean;

This VMProtect API function allows you to detect changes made in the application memory. The result of its work (True/False) can be processed by the protection mechanisms built into the application.

VMProtectDecryptStringA

function VMProtectDecryptStringA(Value: PAnsiChar): PAnsiChar;

VMProtectDecryptStringA function allows to decrypt ANSI string constant. You have to add this constant to a list of protected objects.

VMProtectDecryptStringW

function VMProtectDecryptStringW(Value: PWideChar): PWideChar;

VMProtectDecryptStringA function allows to decrypt Unicode string constant. You have to add this constant to a list of protected objects.

VMProtectFreeString

function VMProtectFreeString(Value: Pointer): Boolean;

VMProtectFreeString function allows to free the decrypted string.


Main menu

The main menu consists of the following items:

  • File
  • Text
  • Project
  • Watermarks
  • Settings
  • Help

"File" menu

  • Open - select the executable file of the application to protect or a project file (*.vmp). This menu item also contains the history of protected applications and the "Clear history" item. You can also open files using the [image] toolbar button. You can also drag and drop the necessary executable file onto the VMProtect window;
  • Save project - save the application protection settings to a file of the "*.vmp" format. The project settings file will be saved to the same folder where the executable module of the protected application is stored. You can also save the project using the [image] toolbar button;
  • Save project as... - save the project to a file with a new name;
  • Close - close the current project. You can also click the [image] toolbar button to close the current project;
  • File info... - view the properties of the protected application. You can also access this function by clicking the [image] toolbar button;

  • Calculator... - calculate the absolute and relative addresses, offset and detect the section. The source data for calculation is the value of one of parameters.

  • Exit - exit the program.

"Text" menu

  • Undo - undo the previous changes;
  • Redo - redo the previous changes;
  • Cut - cut the selected data to the clipboard;
  • Copy - copy the selected data to the clipboard;
  • Paste - paste from the clipboard;
  • Select all - select all available items. For example, this can be used to select the entire disassembled code of an application;
  • Find... - find a text fragment.

  • Find next - find the next text fragment;
  • Go to address... - go to the specified address in the protected application. It is possible to go to the entered address or to the address of the object selected in the "All procedures" list. It is possible to search for an object by its name on the "Search" tab

"Project" menu

  • New procedure... - add a new object to the project;

  • New folder... - add a folder to the project:

    Folders do not affect the protection of objects located within them, but serve to systematize protected objects displayed in the project tree. Just drag-and-drop objects to move them to a folder.

  • Edit... - edit the parameters of the protected object;
  • Delete - remove the object from the list of protected objects;
  • Compile - compile the protected application with the current protection options. You can also click the [image] toolbar button to compile the project;
  • Execute - run the protected application. You can also run the source/protected executable file of the application with a click on the [image] toolbar button.

"Watermarks" menu

The "Watermarks" menu is used to open the window in order to manage watermarks:

See how to work with this window in the "Watermarks" section.

"Settings" menu

  • Language - select the language of the user interface;
  • View - select the VMProtect mode (Simple/Expert);
  • Autosave project after compilation - enable/disable saving the project automatically after its compilation;
  • Integrate into shell - integrate VMProtect into Windows Explorer.

"Help" menu

  • Contents - open VMProtect help. You can also click the [image] toolbar button to open help;
  • Legend... - see the conventional signs;
  • Home page - go to the VMProtect home page. You can also click the [image] toolbar button to go to the home page;
  • About... - view basic information about VMProtect.


Main window

The main window consists of the following tabs in the default view mode:

  • Procedures for protection
  • Licenses
  • Options

The main window consists of the following tabs in the "Expert" mode:

  • Procedures for protection
  • Licenses
  • DLL Box
  • Script
  • Options
  • Dump

Also, messages generated by the script and compiler are shown in the lower part of the window.


"Procedures for protection" tab

The "Procedures for protection" tab is used to select the objects to protect. The left part of the window shows the project tree:

  • New procedure... - add a new object to the project;
  • New folder... - add a folder to the project:

    Folders are used to group protected objects in the project tree and also to change protection options (inclusion in compilation, compilation type) for all objects in one folder at once.

  • Edit... - edit the parameters of the protected object;
  • Delete - remove the object from the list of protected objects;
  • Include in compilation - include/exclude the selected object from the list of protected objects. If the selected object is a folder, this option will be applied to all objects in this folder;
  • Compilation type - select the compilation type for the selected object (mutation, virtualization, ultra). If the selected object is a folder, this option will be applied to all objects in this folder;
  • Move up - move the selected object up in the project tree;
  • Move down - move the selected object down in the project tree;
  • Expand all folders - expandn all folders in the project tree;
  • Collapse all folders - collapse all folders in the project tree.

The right part of the window shows the content of the selected object. If a protected object is selected in the tree, the right part of the window shows its disassembled representation:

  • External address - include the selected address to the list of external addresses;
  • End of procedure - limit the list of instructions that will be processed during compilation;
  • Go to address... - go to the specified address in the protected application;
  • Copy - copy the selected data to the clipboard;
  • Select all - select all content;
  • Find... - find a text fragment in the selected object;
  • Find next - find the next text fragment in the selected object.

If a folder is selected in the project tree, the right part of the window shows the list of objects in this folder:

  • Open - go to the selected object in the project tree;
  • Include in compilation - include/exclude the selected object from the list of protected objects;
  • Compilation type - select the compilation type for the selected object;
  • Copy - copy the selected data to the clipboard;
  • Select all - select all content;
  • Find... - find a text fragment in names of objects in this folder;
  • Find next - find the next text fragment in names of objects in this folder.

You can drag and drop objects to move them from one folder to another.

Adding/editing an object for protection

Options

  • Compilation type - select the compilation type for the object. The correct choice of the protection method against analyzing and cracking each protected object (procedure, function, code section) will ensure the optimal proportion between the protection level and the execution performance. VMProtect allows you to select the following compilation types for each protected object:
    • Mutation. The executable code is modified on the level of CPU instructions: existing instructions are modified, all kinds of garbage commands are added, etc. This compilation type provides relatively weak protection for the processed code against being cracked and analyzed and mainly prevents functions from being determined by signature analyzers (PEiD+KANAL, IDA+FLIRT, etc). It makes sense to use this compilation type while protecting library functions because they do not need protection against being cracked or analyzed, as a rule, and it will be enough just to change their signatures so the cracker is unable to automatically determine exactly what libraries the application uses. If mutation is used, the level of protection against being cracked and analyzed is low, but the code execution performance is high.
    • Virtualization. The executable code is compiled into the bytecode executed on the virtual machine. This compilation type should be used for all critical sections of the code where the execution performance is important together with protection against analysis. If virtualization is used, the level of protection against being cracked and analyzed is medium and the code execution performance is medium.
    • Ultra (mutation + virtualization). The executable code is modified on the level of CPU instructions and then compiled into the bytecode executed on the virtual machine. This compilation type should be used for all code sections where the execution performance is not important. The protection level is high, but the execution performance is low.
  • Use project options - if this option is enabled, the current object will use the common protection options. If this option is disabled, all individual protection options can be selected for the current object.

Additional options are available for SenseLock Edition

Lock to key

  • Do not use license - if this option is used, during the execution of the code of the current object the system will be checked for a connected SenseLock key without a license system;
  • Use license - if this option is used, during execution of the code of the current object the SenseLock key will be checked for a license with the specified number and version.

All procedures

You can see the list of objects that can be included in the project on the "All procedures" tab:

While adding a new procedure, it is possible to simultaneously select several objects in the list of all objects. The selected objects will be added to the project with predefined protection options.

Search

You can use the "Search" tab to find all necessary objects by their names:

You can use the standard wildcard characters in the search string:

  • * - any number of character;
  • ? - a single character.

While adding a new procedure, it is possible to simultaneously select several objects in the list of found objects. The selected objects will be added to the project with predefined protection options.


License Management

Initialization

The licensing feature is turned off by default. To enable it you need to open the "License" tab and create a pair of keys. If the project is linked to the License Manager database (old versions of VMProtect used a separate application for license management), you will be able to import licenses from that database. After initialization you will be able to generate and manage serial numbers and lock code to them.

User Interface

The "Licenses" tab shows the full list of licenses and the properties of the selected one. Above the list of licenses there are buttons that allows to created, edit, delete licenses and do the other things:

The right panel shows the details of the selected license. It also allows to block the serial number, copy it to the clipboard, show the details of hardware identifier (by clicking it).

License Creation

To create a new license, you need to click the appropriate button above the licenses list or press the Ctrl+N key combination. The "New License" window appears:

The top part of the window contains the main parameters of the new license. The bottom part allows to choose what parameters will be put to the serial number and add some other parameters as well. After filling the form you need to press the "OK" button to generate a license.

License deletion and blacklisting

There is an important difference between the deletion of serial number and blocking it. Deleted serial number is still accepted by the protected application. It just disappears from the database. If you created a license by mistake and never send it to anyone - you may delete it. If the serial number was stolen or published, you can't simply delete it. You have to block it and keep in the database as blocked. In that case licensing module that is embedded in the application will know about that serial number and will not accept it anymore.

Serial nubmers import

Serial numbers can be created by standalone generators. They are usually web-based and called by payment processing services. After receiving such generated serial number by e-mail you may want to add it to the database. That's what the "Import Serial Number" feature does. You may start import by pressing Ctrl+I key combination or by clicking the appropriate button above the licenses list.

The import window appears:

If the license is already exist in the database, you will see the "Edit license" window. Otherwise the "New license" window appears. You will be able to change some parameters and then finish the import by pressing the "OK" button.

Licensing parameters export

Standalone serial number generators require some information about the product, like private key, selected algorithm etc. To export such information, click the "Export" button above the list of licenses. You will see the export window:

The "Export format" field allows to choose between supported formats. The results of export are shown in the field below. The "Copy" button allows to copy results to the clipboard.


"Flashing dongles" tab

This page is used user flashing dongles is available in Senselock Edition only.


DLL Box tab

"DLL Box" tab allows to bundle DLL files into the protected EXE file. At runtime those DLLs can be loaded directly from the memory without writing to a disk:

  • Add DLL - add a new DLL. You may add a new DLL using the [image] button in toolbar;
  • Change DLL - change the parameters of the selected DLL. You may do the same using the [image] button in toolbar;
  • Remove DLL - remove the selected DLL. You may remove DLL using the [image] button in toolbar;

While adding or changin the parameters of DLL, you will see the following window:

  • Name - the name of DLL;
  • File name - the name of file of the bundled DLL;
  • Load at startup - defines whether DLL should be automatically loaded at the application start.


"Script" tab

The "Script" tab is used to create programs using the built-in scripting language:

The left part of the window shows the script contents and is used to quickly navigate through the script code (a double click on a script object). The right part of the window is used to edit the script:

  • Cut - cut the selected text to the clipboard. This can also be done with a click on the [image] toolbar button;
  • Copy - copy the selected text to the clipboard. This can also be done with a click on the [image] toolbar button;
  • Paste - paste text from the clipboard. This can also be done with a click on the [image] toolbar button;
  • Select all - select the entire text;
  • Undo - undo the previous changes. This can also be done with a click on the [image] toolbar button;
  • Redo - redo the previous changes. This can also be done with a click on the [image] toolbar button;
  • Find... - find a text fragment. This can also be done with a click on the [image] toolbar button;
  • Find next - find the next text fragment.


"Options" tab

The "Options" tab is used to specify protection options:

File

  • Memory protection - this option allows protection of the file image in memory from any changes (the data integrity is checked in all sections that have no WRITABLE attribute). The image integrity is checked before control is handed over to the original entry point of the program. If the integrity is broken, the corresponding message will be displayed and the further execution of the program will be completely stopped.
  • Import protection - this option allows you to hide the list of APIs used by the protected program from the cracker (it is recommended to use it together with packing the output file).
  • Resource protection - option allows to encrypt resources, except icons, manifest and some other system types.
  • Pack the output file - this option enables packing of the protected file, which makes it smaller. The protected file is unpacked automatically once it is run. The file is unpacked completely in RAM without writing any data to the disk. There are two compression modes:
    • Fast - the mode offers the minimum compression level and maximum unpacking rate
    • Best - the mode offers the maximum compression level and minimum unpacking rate

    If this option is used, it is recommended to include EntryPoint in the list of protected objects. The EntryPoint address is displayed on the status bar of the main window:

    Important:
    Once the program is run, control will be handed over to EntryPoint after the program is unpacked. If the code of EntryPoint is virtualized, this code will be executed on the same VM interpreter as the code of the unpacker. Virtualizing EntryPoint together with packing the protected file prevents the "manual" unpacking of the protected file because the cracker will have to restore the code of EntryPoint in order to get a working file image.

Compilation

  • Virtual Machines - Each VM will have its own unique set of properties. Different VMs from those specified will be used to virtualize different sections of the code. This option makes it harder to analyze and decompile bytecode since the cracker will have to analyze all VM interpreters used in the protected file.
  • Debug mode (search of external addresses) - during protection VMProtect uses the space freed during the compilation process for its needs, including storing various data (virtualized, mutated code, VM interpreters, etc). This is why there may be situations when jumps from other parts of the program "inside" the protected code take place (addresses that jumps may be directed to are called external). Similar situations may occur if code markers are used when a jump takes place from an unprotected section of the code inside the marker:
    VMProtectBegin(nil);
for  I:=0 to 10 do
begin
  Inc(J);
  VMProtectEnd;
end;

    This loop in the form of assembler instructions looks like this:

    ------------------ marker start -----------------
0044D12C 	mov eax, 0000000B
0044D131 	inc ebx
------------  ----- marker end-------------------
0044D132 	dec eax
0044D133 	jnz 0044D131

    It is obvious that after the virtualization of the instructions with addresses 0044D12C and 0044D131, an error will occur during the execution of the non-virtualized conditional jump instruction with address 0044D133 (since there is various data or just garbage at 0044D131 instead of the original code). Address 0044D131 will be external in this example.

    When the "Debug mode" option is enabled, the INT 03 instruction will take the place of the original code. It is a regular breakpoint and a service instruction when the program is running in the debugger. In case control is handed over at 0044D131 while the protected program is running in the debugger, the breakpoint will be called and the address this jump took place at can be viewed after the debugger window is opened.

    In case such jumps are detected, it is recommended to check whether code markers are inserted correctly. In some cases the protected code is not marked with markers or it will be impossible to change the logic of the program so that there are no jumps from other sections of the program inside the protected code. In these circumstances, use the GUI version of the application to mark these addresses as external.

  • Check the integrity of VM objects - this option allows you to silently check the integrity of all virtualized sections of the code and VM interpreters. A silent check means that the protected program will not show any messages in case the integrity of VM objects is broken, but it will cause the virtualized code to function incorrectly, which will lead to critical errors and, therefore, to the complete crash of the protected program. Also, this option allows you to monitor changes in the integrity of VM interpreters that is broken when software breakpoints are used during the debugging of the protected program in a debugger.
  • Encrypt registers at VM output - if this option is enabled, the registers of the processor will contain encrypted values at VM output and these values will be decrypted in different places for each protected section of the code. This option prevents the analysis of register states at VM output.
  • Hide constants - if this option is enabled, it will be impossible to find the addresses of variables or the addresses of called functions in the bytecode in the open form. This option does not allow the protection of string constants.

Detection

  • Debugger - this option prevents the debugging of the protected file. There are 2 types of debuggers: user-mode (OllyDBG, WinDBG, etc) and kernel-mode (SoftICE, Syser, etc). Debuggers are detected before control is handed over to the original entry point of the program. If a debugger is detected, the corresponding message will be displayed and the further execution of the program will be stopped.
  • Virtualization tools - this option prevents the protected file from being run in all kinds of virtual environments: VMware, Virtual PC, VirtualBox, Sandboxie. Virtual environments are detected before control is handed over to the original entry point of the program. If a virtual environment is detected, the corresponding message will be displayed and the further execution of the program will be stopped.

Sections

  • VM sections - additional sections will be added to the file during compilation. Data will be written to these sections (virtualized code, mutated code, VM interpreters, watermarks, etc). This option allows the specification of names for new sections. It is recommended to change the standard ".vmp" section names (for example, to ".UPX").
  • Remove fixup elements - some compilers (Delphi in particular) create a list of fixup elements for EXE files, but the operating system does not use these elements while loading EXE files. If the option is enabled, the section occupied by the list of fixup elements will be used for VM needs.

Watermarks

Use the drop-down list to select the necessary watermark in order to include it in the protected file. The drop-down list contains only those watermarks that are enabled for use.

Licensing parameters

Use the file with parameters created in License Manager here. By default current project file is being used.

Additional options for the SenseLock Edition

Use these licensing options for all procedures

If this option is used, these licensing options will be applied to all objects included in the project with the "Use key" option enabled:

  • Do not use license - if this option is used, during code execution of the current object the system will be checked for a connected SenseLock key without a licensing system;
  • Use license - if this option is used, during code execution of the current object the SenseLock key will be checked for a license with the specified number and version.

SenseLock

  • Master key password - use this field to specify which password VMProtect uses to access the master key containing the software protection module. The master key comes with the VMProtect SenseLock Edition and is used only by the software product developer;
  • User PIN - use this field to specify which password the protected application uses to access the user key that is delivered to the end user together with the protected application. User keys are not delivered together with the VMProtect SenseLock Edition;
  • Key identifier - this parameter may contain the key identifier that the particular copy of the protected application is bound to. To automatically enter the identifier, connect the corresponding user key to the computer and click the [image] button.


"Dump" tab

The "Dump" tab is used to view the memory dump of the protected application in the form of machine code and assembler instructions:

  • Go to address... - go to the specified address in the protected application;
  • Copy - copy the selected data to the clipboard;
  • Select all - select all content.


Watermarks

VMProtect offers a unique opportunity to add hidden information about the file owner to the protected file. A watermark is a byte array that must be unique for each of your users. If you add watermarks to the protected file, later (ie if someone distributes the cracked program) you will always be able to determine its owner and take the corresponding measures.

The "Watermarks" dialog box has two tabs:

  • Setup
  • Search


"Setup" tab

The "Setup" tab is used to manage watermarks:

  • New watermark... - add a new watermark. You can also add a new watermark using the [image] toolbar button;
  • Edit watermark... - edit the properties of the selected watermark. You can also edit the selected watermark using the [image] toolbar button;
  • Delete watermark - delete the selected watermark. You can also delete the selected watermark using the [image] toolbar button;
  • Enabled for use - this option defines if it is possible to use this watermark in protected files.

The following dialog box is used to add/edit a watermark:

  • Name - watermark name;
  • Value - watermark value; the "Generate" button can also be used to specify a random value for the watermark. The "Value" field consists of two columns: you can type the value in the Hex format in the left column or as text in the right column. When the watermark is added to the protected file, the "?" character will be replaced with random values.


"Search" tab

The "Search" tab is used to search the executable file or a certain process in the protected application for watermarks:

  • Search in file - search the executable file of the application. Use the "File name" field to select the file to search for watermarks;
  • Search in process - search a module of the selected process. After you select the necessary process in the "Process" drop-down list, use the "Module" drop-down list to select the module of this process to search for watermarks.

The watermark search results are shown in the list in the lower part of the dialog box:

  • Name - the name of the located watermark;
  • Count - the number of copies of the located watermark.

Important:
If an unpacked executable file is searched for watermarks, either of these modes can be used. However, if the executable file is packed, you should search only a running application for watermarks (the "Search in process" mode) since a search in an executable file will bring no results because watermarks, as well as the rest of code and data, are packed and they are unpacked only after the application has been run.


Command-line version

After creating a project in the GUI mode, it is possible to later use the command-line version (VMProtect_Con.exe). This is called in the following way:

VMProtect_Con File [Output file] [-pf Project file] [-sf Script file] [-wm Watermark name] [-we]

  • File - the name of the executable file that should be protected (*.exe, *.dll, etc.) or the project file name (*.vmp). If a project file is specified as the source file, the name of the executable file will be taken from the project file.
  • Output file - the name and path to the protected file that should be created after the source file is processed. If the parameter is not specified, its value is taken from the project file.
  • Project file - the name and path to the project file created in the GUI mode. If the parameter is not specified, the folder with the executable file will be searched for in the project file (*.vmp).
  • Script file - the name of the file containing the script that will be used to process the protected file. If the parameter is not specified, the folder with the executable file will be searched for the script file (*.vms).
  • Watermark name - the name of the watermark to be added to the protected file. If the name of the watermark is not specified, the watermark specified during the creation of the project will be added to the protected file.
  • we - if you use this parameter, all warnings will be displayed as errors.


Frequently Asked Questions

Ordering VMProtect

Q: Can I use VMProtect on multiple computers?
A: Yes, you can use VMProtect on as many computers as you need. Please note, that the Personal edition can be used by one person only - the customer. The Company edition can be used by any employee of the company. In case of the violation of these terms we may block your license.

Q: What is the difference between Personal and Company editions?
A: The Personal edition of VMProtect is for the individual developers and can be used by the customer only. The Company edition can be used by any employee of the Company. Basically, if you are independent developer, you need a Personal edition, otherwise - a Company one.

How VMProtect delivers after ordering?
Full version of VMProtect and all updates will be delivered by e-mail. It is recommended to disable SPAM-filter and all limitations of message size (or increase it to 15 Mb).

Will I receive VMProtect updates for free after ordering?
You will receive free updates during a year after purchasing a license. You may then renew subscription or use the current version of VMProtect without time limitations.

Is it possible to order VMProtect using credit card or PayPal?
Yes. To do that, please select payment method "Plimus" here and after pressing "Send request" button you will be redirected to Plimus payment processing service that accepts credit cards and PayPal.

Common Questions

Is it possible to encrypt strings and arrays automatically?
VMProtect allows to hide ANSI and Unicode constants. All the rest data will be left as is. It is recommended to encrypt all important data and decrypt it immediately before use. You may virtualize decryption code.

Is VMProtect thread safe?
Yes, VMProtect is completely thread-safe.

Is it possible to use VMProtect with another protector or packer?
It is not recommended to use other protectors or packers with VMProtect. This may lead to unpredictable behavior.

Is it necessary to include VMProtectSDK32.dll/VMProtectSDK64.dll to installer?
No. These libraries are necessary until you protect the application. After protection VMProtect replaces every call to the libraries with the calls of embedded module, so you don't need those files anymore.

Compiler messages

What does the following mean: VMProtectMarker "ToolButton1Click.1".0044327D: Address is being used by "TForm1.ToolButton1Click" procedure?
It means that the command at address 0044327D is shared between two different procedures and both of them are protected. You have to remove one of them from the list of protected objects.

What does the following mean: [Error] TForm1.Test.004433F4: Minimal size of procedure for compilation is 5 bytes?
It means that TForm1.Test procedure is too short and cannot be protected. You have to remove it from the list of protected object.

What does the following mean: TForm1.ToolButton1Click.00443368: Jump to the internal address: 00443337?
It means that there is a jump from unprotected code to the protected one. This may lead to unpredictable behavior and you have to mark this address as external.

What does the following mean: Section ".text" is in the field of creation of heading of new section?
This usually happens when you protect drivers. It means that there is no enough free space for the new section between the first section of file and the system area of file header. You need to increase section alignment value in driver sources and rebuild the driver. For example if you build driver using WDK/DDK, you may put the line DRIVER_ALIGNMENT=0x200 to the SOURCES file.

What does "Master key not found" mean? I am using VMProtect SenseLock Edition.
VMProtect SenseLock Edition requires master key to run. Master Key is a hardware USB key - SenseLock EL with custom firmware. All questions about ordering and deliver of SenseLock keys should be sent to senselock@vmpsoft.com.


The software licensing system

The software licensing system used to generation and validation of serial numbers. Various limitations are supported: date of expiration, running time limit, hardware lock, code encryption, limitation of free updates and much more. Licensing system built on asymmetric encryption schemes, so it is almost impossible to create a serial number generator. Licensing system itself is protected with VMProtect virtualization technology, so it is a really hard task to crack and patch it.

  • The main features of the licensing system
  • How does the licensing system work
  • Licenses: creation, edit, blocking etc
  • Example of usage
  • Automatic generation of serial numbers


Licensing system features

Strong serial numbers

The licensing system uses asymmetric algorithms for serial number encryption. Serial number is crypted with private key, holded by software vendor. Protected application decrypts serial number using the public key and checks it. Because of length of used keys (more than 1024 bits for RSA) it is almost impossible to compute the private key and create a key generator for protected application.

Locking code to a serial number

VMProtect allows to run pieces of code on the virtual machine. Architecture of virtual machine is different for every protected application and every protection. Licensing system allows to encrypt some virtual machine code using the key that is stored in serial number. So, it will be impossible to run that code without valid serial number. Code will be decrypted on the virtual machine, so even if hacker will have valid serial number, it will be a hard task to find out how and when the decryption will be done.

Limitation of free updates

The licensing system allows to put a date to the serial number, so the applications, build after those date will not accept this serial number. This allows to limit a period of free updates. For example if you put "today + 1 year" date to the serial number before sending it to the customer, he will be able to use new versions of your application for 1 year. After that he should either stay with the latest working version or purchase a new serial number.

Serial number expiration

The licensing system allows to store an expiration date in the serial number. Serial number will stop work after that date. This is useful for subscription-based products. As in the example given above, user will be able to use your product for 1 year and after that he should either purchase a new serial number or stop using product at all.

Running time limitation

The licensing system allows to limit running time of the application. This is useful for testing purposes. For example, customers wants a full version to test all the functionality. You may put a 10 minutes limit to the serial number and the application will close after ten minutes, but all features will be enabled within those 10 minutes. This works well for server applications - it is hard to restart them every 10 minutes.

Hardware lock

The licesning system allows to get hardware identification number based on CPU, Ethernet cards and OS information. Serial number may be locked to that hardware ID and will not work on hardware with another ID. This allows to block usage of single serial number on several computers.

Black list of serial numbers

Stolen serial number can be blocked by the licensing system. Blocked serial numbers will not work in the newer versions of protected application.

Serial number content

The licensing system allows to keep the following data in serial numbers: user name, e-mail and up to 255 bytes of custom data. You may show user name and e-mail in the "About" window, use custom data to keep constants available in registered version only etc.

Time trials

The new activation feature allows vendors to issue the time-limit serial numbers that can be used both for limited evaluation purposes and for the time-limited licenses, as well. Each activation code may be associated with the set of features and limitations, so the user gets a limited serial number instead of the full one. This requires Internet access, but may work offline using the special API.

The advantage of this approach is that nothing is actually stored in the registry or hidden files, so the trial period cannot be restarted using registry cleanup or the similar methods.

What doesn't licensing system allow and why

100%-guaranted hardware lock

Despite that the licensing system allows to lock serial number to hardware, you should understand that most of the information about hardware is taken from the operation system. Experienced hacker can catch and replace some of the information. The licensing system uses tricks that make the replacement harder, but it is still possible. So, if you need 100%-strong hardware lock, it is better to use USB keys that give such guarantee. VMProtect supports USB keys, so you don't need another solution for that.


How does the licensing system work?

Protection of application

While protecting application, VMProtect puts licensing module to it. VMProtect uses the information, provided on the "Licenses" tab, like the pair of keys for the product, black list and other important information.

Generation of serial numbers

Serial numbers can be created on the "Licenses" page or using the standalone applications - serial number generators. Serial number is a set of data about the customer, encrypted using the asymmetric algorithm. Serial number is sent to the customer and he enters it to the application.

Serial number validation

The licensing module, embedded to protected application, have some functions to deal with serial numbers. Protected appliation put serial number to the licensing module, it checks the serial number and returns its state. Application analyzes state and decides what to do: to run in full or demo mode, to stop run at all etc. Application may ask the licensing module for details about serial number: user name, e-mail, date of expiration, custom data etc.


Example of usage

In a few steps described below, we'll create a test application that will operate with licensing module: put serial numbers and get states and data. At the first stage we'll use licensing module in the "test" mode and at the second state, we'll use it in the "real" mode.

Supported modes of licensing module

There are two stages at software protection implementation: development and usage. At first you create application, add protection modules and code, then test them all and only after that you give the application to customers and the second phase begins. Testing of protected application is a complex and complicated process: you have to check every limitation, every branch of code that will work or not depending on serial number. It is not a good idea to test protection code with real serial numbers - it is difficult. That's why the licensing system has "test" mode. In test mode application is not being protected and work with emulator of licensing module that works exactly as the real module, but doesn't analyze serial numbers. Every aspect of its work is defined in INI-file: accepted and blacklisted serial numbers, date of expiration, user name etc - everything is read from INI-file. You may use various INI-files or create them on-the-fly and test your appliation and protection code. When application is tested and works fine with emulation of licensing module, you will protect the application using VMProtect and it will replace emulator with the real licensing module that will not read INI-file and that will check contents of serial numbers.

Stage #1: Test mode

In test mode everything is defined by INI-file. Licensing module emulator reads VMProtectLicense.ini from the working directory of application. In the next 10 steps we'll go from creation of simple application to hardware locking and limitation of period of free updates.

  • Step #1: Create test application
  • Step #2: Add license checking code
  • Step #3: Get state of serial number
  • Step #4: Get user name and e-mail from serial number
  • Step #5: Check serial number expiration date
  • Step #6: Limit application running time
  • Step #7: Limit period of free updates
  • Step #8: Put serial number to "black" list
  • Step #9: Lock serial number to hardware
  • Step #10: Read user data from serial number
Stage #2: Real mode

When VMProtect protects the application, it replaces emulator of licensing module with the real module. It works exactly as emulator, but doesn't read INI-files. It reads serial number contents instead. In the next 5 steps we'll build another simple application and protect if with "real" protection using VMProtect.

  • Step #1: Create new test application
  • Step #2: Create VMProtect project
  • Step #3: The first run of protected application
  • Step #4: Test results
  • Step #5: Lock code to serial number
Notes

Steps above is a good tutorial with examples of real-world tasks, but if you need to know values of flags, format of structures or functions arguments - everything is listed in Licensing Module API section.


Step #1: Create test application

At the first step we'll create a test application. It will be a simple application, withour user interface and functionality it will just call licensing module and get result.

#include <windows.h>
#include <stdio.h>

bool is_registered(const char *serial)
{
    return serial && serial[0] == 'X';
}

int main(int argc, char **argv)
{
    char *serial = "Xserialnumber"; // define serial number in the code
    if (!is_registered(serial))
    {
        printf("please register!\n");
        return 0;
    }
    printf("We are registered.\n");
    return 0;
}

Our application checks serial number itself using is_registered() function. This function says that number is correct if the first letter or serial number is 'X'. If number is correct, our application prints "We are registered", otherwise it asks for registration.

At the second step we'll ask the licensing module to check our serial number.


Step #2: Add license checking code

Use VMProtect SDK

If you didn't already do that - it is time to link VMProtect SDK to our application. SDK consits of three files: VMProtectSDK.h - header file with functions, structures and constants. VMProtectSDK32.lib - library file. VMProtectSDK32.dll - dll-file with licensing module emulator. It is neccesary to link SDK statically otherwise VMProtect will not be able to replace it with real licensing module. Header file contains pragma-definition that will link 32- or 64-bits libraries automatically.

Do not forget to put DLL-file to the working directory of the application. After that include header file to our source:

#include <windows.h>
#include <stdio.h>
#include "VMProtectSDK.h"

Try to compile and run application. It should work exactly as without licesing module, because it is not yet active.

Pass serial number to licensing module

Let's add a few lines that will pass our serial number to licensing module. Put the following lines immediately after serial number definition:

char *serial = "Xserialnumber";
int res = VMProtectSetSerialNumber(serial);
printf("res = 0x%08X\n", res);

Try to compile and run the application. If it will fail with error, related to dll-file - make sure that you put correct VMProtect SDK dll to the application working directory. If all will be done correct you should see the following output:

res = 0x00000002

Number 2 is a SERIAL_STATE_FLAG_INVALID flag as described here. The licensing module decides that our serial number is invalid. It is right. We didn't yet tell the licensing module what keys are correct and what are not.

Define "correct" serial number

As we are in the test mode, we should put correct serial number to VMProtectLicense.ini file. Format of this file will be described later, now just create it and put the following lines:

[TestLicense]
AcceptedSerialNumber=Xserialnumber

Run the appliation again. If you get "2" one more time - make sure that you created INI-file in the working directory, that it has correct names of section and parameter. You should finally get the "0" code, that means that licensing module accepts our serial number. Now you may remove is_registered() function. Serial numbers will be checked by licensing module:

#include <windows.h>
#include <stdio.h>
#include "VMProtectSDK.h"

int main(int argc, char **argv)
{
    char *serial = "Xserialnumber";

    int res = VMProtectSetSerialNumber(serial);
    printf("res = 0x%08X\n", res);

    if (res)
    {
        printf("please register!\n");
        return 0;
    }
    printf("We are registered.\n");
    return 0;
}


Step #3: Get state of serial number

A function to print flags

First of all, let's write a simple function that will print flags in human-readable format. Code of the function is below:

#define PRINT_HELPER(state, flag) if (state & flag) printf("%s ", #flag)
void print_state(INT state)
{
    if (state == 0)
    {
        printf("state = 0\n");
        return;
    }

    printf("state = ");
    PRINT_HELPER(state, SERIAL_STATE_FLAG_CORRUPTED);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_INVALID);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_BLACKLISTED);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_DATE_EXPIRED);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_RUNNING_TIME_OVER);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_BAD_HWID);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED);
    printf("\n");
}

It is big, but simple. It checks flags one by one and prints those flags that are set. Now replace printf with print_state and pass a wrong serial number to the licensing module:

char *serial = "Xserialnumber1";
int res = VMProtectSetSerialNumber(serial);
print_state(res);

After running the application, you will see the following:

state = SERIAL_STATE_FLAG_INVALID
please register!

If we'll take our "correct" serial number back, and run the application again, we should see this:

state = 0
We are registered.

Now we can see all the flags that licensing module sets, we may continue.

Getting state of serial number

There are three methods to get state of serial number: by calling VMProtectSetSerialNumber() function, VMProtectGetSerialNumberState() function or VMProtectGetSerialNumberData() function (flags will be placed to the one of the fields of structure). The first check of serial number state should be made after setting it. You should analyze returned flags and maybe terminate application immediately. This will block invalid and expired serial numbers, blocked numbers etc. If the serial number may contain running time limitations, you have to check its state periodically using the second function. The third function allows to get all information about serial number that may help to describe some of flags in state.


Step #4: Get user name and e-mail

Let's start from the simplest things: let's get user name and e-mail from the serial number. We may show them in "About" window in the real application, but we'll just print them. As we are in test mode, the licensing module will read data from INI-file instead of serial number, so we need to put name and e-mail to INI-file. Let's add two lines to our INI-file:

[TestLicense]
AcceptedSerialNumber=Xserialnumber
UserName=John Doe
EMail=john@doe.com

Now we need to modify our application. In case of successful registration, let's get more data about the serial number:

VMProtectSerialNumberData sd = {0};
VMProtectGetSerialNumberData(&sd, sizeof(sd));
printf("name = %ls,\ne-mail = %ls\n", sd.wUserName, sd.wEMail);

VMProtectSerialNumberData structure stores strings in UNICODE, so we have to use "%ls" markers for printf() instead of "%s". If you run the application, you should see the following:

state = 0
We are registered.
name = John Doe,
e-mail = john@doe.com


Step #5: Check serial number expiration date

Put the following line to INI-file:

ExpDate=20000101

Format of the date is YYYYMMDD, it is important that you put date from the past. Run the application and you will see:

state = SERIAL_STATE_FLAG_DATE_EXPIRED
please register!

Let's query some information before printing "pelase register":

if (res)
{
    VMProtectSerialNumberData sd = {0};
    VMProtectGetSerialNumberData(&sd, sizeof(sd));
    printf("exp. date: y = %d, m = %d, d = %d\n", sd.dtExpire.wYear, sd.dtExpire.bMonth, sd.dtExpire.bDay);
    printf("please register!\n");
    return 0;
}

Run the application again and you will see why serial number is invalid:

state = SERIAL_STATE_FLAG_DATE_EXPIRED
exp. date: y = 2000, m = 1, d = 1
please register!

Remove the ExpDate=.... line from INI-file so we can check other limitations.


Step #6: Limit application running time

You may limit time that application will work after starting. Licensing module will not terminate the application when time will run out, but it will set a corresponding flag in the serial number state. Let's limit running time of our application by 1 minute. Add the following line to INI-file:

TimeLimit=1

We need to change our application a bit:

int main(int argc, char **argv)
{
    char *serial = "Xserialnumber";

    int res = VMProtectSetSerialNumber(serial);
    print_state(res);
    if (res) return 0;

    VMProtectSerialNumberData sd = {0};
    VMProtectGetSerialNumberData(&sd, sizeof(sd));
    printf("I will run for %d minute(s)\n", sd.bRunningTime);
    print_state(VMProtectGetSerialNumberState());
    Sleep(60 * 1000 * sd.bRunningTime);
    printf("After %d minute(s):\n", sd.bRunningTime);
    print_state(VMProtectGetSerialNumberState());

    return 0;
}

After start our application prints serial number state, then reads number of minutes it is allowed to run, then sleeps this time and query state of serial number again. If we run the application, we'll see the following:

state = 0
I will run for 1 minute(s)
state = 0
After 1 minute(s):
state = SERIAL_STATE_FLAG_RUNNING_TIME_OVER

Real protected application should check state of serial number periodically and terminate itself if the flag will appear. Licensing module doesn't do that automatically because you may want not to terminate application but to block some features, or show messages, or allow user to save data etc. It is up to developer what to do when time is over. Licensing module will just inform about it.


Step #7: Limit period of free updates

How does it work?

While protecting application VMProtect stores current date - a date of build. You may put a maximal build date to the serial number. The licensing module will compate both dates and will not accept key with maximal build date that is less than date of application build. This allows to limit period of free updates. Customer should use older version or purchase a new serial number.

Let's try it

Put the following line to INI-file:

MaxBuildDate=20000101

Date format is YYYYMMDD. In test mode date of build is always current date, so you have to use some date from the past for MaxBuildDate parameter. Now change main() function, so it will look like below:

int main(int argc, char **argv)
{
    char *serial = "Xserialnumber";

    int res = VMProtectSetSerialNumber(serial);
    print_state(res);

    if (res)
    {
        VMProtectSerialNumberData sd = {0};
        VMProtectGetSerialNumberData(&sd, sizeof(sd));
        printf("max. build date: y = %d, m = %d, d = %d\n", sd.dtMaxBuild.wYear, sd.dtMaxBuild.bMonth, sd.dtMaxBuild.bDay);
        printf("please register!\n");
        return 0;
    }

    printf("I'm registered\n");

    return 0;
}

After running application you will see the following:

state = SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED
max. build date: y = 2000, m = 1, d = 1
please register!

If you will change date in INI-file to some date from the future, you should see the following after running application:

state = 0
I'm registered

Do not forget ro remove MaxBuildDate=... line from INI-file before we'll continue.


Step #8: Put serial number to "black" list

Blocked serial numbers should not be accepted by the licensing module. While the next protection VMProtect will store hashes for blocked numbers to the application and licensing module will not accept such numbers anymore.

Let's create a very short main() function:

int main(int argc, char **argv)
{
    char *serial = "Xserialnumber";

    int res = VMProtectSetSerialNumber(serial);
    print_state(res);

    return 0;
}

Run the application and make sure that it accepts the serial number:

state = 0

Now let's block our serial number. To do that add the following line to INI-file:

BlackListedSerialNumber=Xserialnumber

And run the application again:

state = SERIAL_STATE_FLAG_BLACKLISTED

It is up to you what to do when serial number is blacklisted. The licensing module just informs you about it and treats the serial number as "invalid".


Step #9: Lock serial number to hardware

Retrieving hardware identifier

To lock serial number to hardware, you need to get a hardware identifier first. This identifier will be stored in serial number and licensing module will check it while validating serial number. So, first of all, let's get our hardware identifier. Change the main() function, so it will look like:

int main(int argc, char **argv)
{
    int nSize = VMProtectGetCurrentHWID(NULL, 0);
    char *buf = new char[nSize];
    VMProtectGetCurrentHWID(buf, nSize);
    printf("HWID: %s\n", buf);
    delete [] buf;
    return 0;
}

Run the application and you will see a default hardware identifier for the test mode:

HWID: myhwid

To change default identifier - add the following line to INI-file:

MyHWID=test

And run the application again. Now our hardware identifier is "test":

HWID: test

Lock serial number to hardware identifier

Let's tell our test licensing module that serial number is locked to hardware identifier. Add the following line to INI-file:

KeyHWID=test

And we'll need to change main() function a bit:

int main(int argc, char **argv)
{
    int nSize = VMProtectGetCurrentHWID(NULL, 0);
    char *buf = new char[nSize];
    VMProtectGetCurrentHWID(buf, nSize);
    printf("HWID: %s\n", buf);
    delete [] buf;

    char *serial = "Xserialnumber";
    int res = VMProtectSetSerialNumber(serial);
    print_state(res);
    return 0;
}

Run the application and you will see the following:

HWID: test
state = 0

As we may see, the licensing module compared two identifiers: hardware and the one that we put to the serial number. As they are the same, VMProtectSetSerialNumber() returned 0 - serial number was accepted.

Now let's try to run the application on another hardware. Let's replace "test" value for MyHWID parameter in INI-file to something different, for example "new test". And run the application again:

HWID: new test
state = SERIAL_STATE_FLAG_BAD_HWID

As we may see, licensing module returned SERIAL_STATE_FLAG_BAD_HWID flag, that means that serial number is not accepted because of invalid hardware identifier. If we'll replace value of KeyHWID parameter with "new test" and run the application one more time - we'll see that licensing module will accept the new serial number.


Step #10: Read user data from serial number

A serial number may contain up to 255 bytes of "user" data, that can be read by protected application. You may store anything you need there. Additional information about license or order, some data that will be used by registered version etc. We'll now change main() function, so it will read user data and print them:

int main(int argc, char **argv)
{
    char *serial = "Xserialnumber";
    int res = VMProtectSetSerialNumber(serial);
    print_state(res);
    if (res) return 0;

    VMProtectSerialNumberData sd = {0};
    VMProtectGetSerialNumberData(&sd, sizeof(sd));
    printf("Serial number has %d byte(s) of data\n", sd.nUserDataLength);
    for (int i = 0; i < sd.nUserDataLength; i++)
        printf("%02X ", sd.bUserData[i]);
    printf("\n");
    return 0;
}

Cut the INI-file to the following lines:

[TestLicense]
AcceptedSerialNumber=Xserialnumber

Run the application to see that our serial number doesn't have any user data:

state = 0
Serial number has 0 byte(s) of data

To add user data in the test mode, you need to add UserData parameter to INI-file. Value of the parameter is a HEX-string each to characters make a byte, so number of characters in the line should be even. For example, add the following line:

UserData=010203A0B0C0D0E0

Now run the application and see the bytes you put to INI-file:

state = 0
Serial number has 8 byte(s) of data
01 02 03 A0 B0 C0 D0 E0


Step #1: Create new test application

At the first stage we created several test applications while learning the licensing system API. At this stage we'll use a single test application. As the first one it will be console-based. We'll add a foo() function that will work in the registered version only. The code is below:

#include <windows.h>
#include <stdio.h>
#include "VMProtectSDK.h"

#define PRINT_HELPER(state, flag) if (state & flag) printf("%s ", #flag)
void print_state(INT state)
{
    if (state == 0)
    {
        printf("state = 0\n");
        return;
    }

    printf("state = ");
    PRINT_HELPER(state, SERIAL_STATE_FLAG_CORRUPTED);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_INVALID);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_BLACKLISTED);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_DATE_EXPIRED);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_RUNNING_TIME_OVER);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_BAD_HWID);
    PRINT_HELPER(state, SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED);
    printf("\n");
}

char *read_serial(const char *fname)
{
    FILE *f;
    if (0 != fopen_s(&f, fname, "rb")) return NULL;
    fseek(f, 0, SEEK_END);
    int s = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buf = new char[s + 1];
    fread(buf, s, 1, f);
    buf[s] = 0;
    fclose(f);
    return buf;
}

// foo() is a very short function, we need to prevent it from inlining
__declspec(noinline) void foo()
{
    printf("I'm foo!\n");
}

int main(int argc, char **argv)
{
    char *serial = read_serial("serial.txt");
    int res = VMProtectSetSerialNumber(serial);
    delete [] serial;
    if (res)
    {
        printf("serial number is bad\n");
        print_state(res);
        return 0;
    }
    printf("serial number is correct, calling foo()\n");
    foo();
    printf("done\n");
    return 0;
}

Configure MSVC, so it will create MAP-file while compiling the application. It will be used by VMProtect. Run the application:

serial number is bad
state = SERIAL_STATE_FLAG_INVALID

At this moment the licensing module still works in the test mode, because our application was not yet protected with VMProtect that will replace test module with the real one. At the next step we'll create VMProtect project and try to protect our application.


Step #2: Create VMProtect project

Now you should have compiled version of the test application and its MAP file in the same directory. It is time to run VMProtect Ultimate and open the executable file of the application. You need to put two functions to the project: _main() (MSVC renamed our main() function) and foo(). Both functions are shown by VMProtect, so you should see something like this after adding:

Now you need to initialize the licensing system. To do that, go to the "Licenses" tab and generate a pair of keys with length 2048 bits.


Step #3: The first run of protected application

The licensing system is initalized, so we can compile the VMProtect project and run the protected file. We should see the following after running:

C:\test>dummy_app.vmp.exe
serial number is bad
state = SERIAL_STATE_FLAG_INVALID

Run depends.exe application and see that our application doesn't use VMProtectSDK.dll. That means that VMProtect embedded licensing module to the application. You may see a list of used DLLs in VMProtect: open the protected file, and use "Properties" item from the "File" menu.

Our application reads serial number from the serial.txt file. As the file doesn't exist now, our application reads nothing and the licensing module receives empty string as a serial number. Empty string is not a correct serial number, so we need to generate one. Let's switch to the "Licenses" tab and create a serial number. Serial number generation is described here. We'll generate a simple serial number without limitations.

Upon generation, click the "Copy Serial Number" button, create the "serial.txt" file near the application and paste the serial number there. Then run the application again. You should see that the serial number is accepted:

C:\test>dummy_app.vmp.exe
serial number is correct, calling foo()
I'm foo
done

The licensing module accepted our serial number. In the next step we'll try to add some limitations and see what happens.


Step #4: Test results

Date of expiration

Let's create one more serial number and set it to expire in the 2005 year. This is a date from the past, so licensing module should not accept the serial number. Let's check this. Go to the "Licenses" tab, click the "Add License" button and check the "Expiration date" box, then enter "September 30, 2005" there. Then click the "OK" button and put the generated serial number to the "serial.txt" file. Finally, run the application:

C:\test>dummy_app.vmp.exe
serial number is bad
state = SERIAL_STATE_FLAG_DATE_EXPIRED

The licensing module returned "serial number is expired" code. Replace the number in the "serial.txt" file with the old one and check that it is accepted as before.

C:\test>dummy_app.vmp.exe
serial number is correct, calling foo()
I'm foo
done
Blocking a serial number

Imagine that our "good" serial number was stolen and available on the Internet. We have to block it, so it will not work in the next versions of the application. We should locate it in the list of serial numbers and click "Block Serial Number" button in the right pane. However, if we run our protected application now, it will accept the blocked serial number, because it doesn't know that it is blocked.

C:\test>dummy_app.vmp.exe
serial number is correct, calling foo()
I'm foo
done

Make a copy of our application and name it "dummy_app1.vmp.exe". Then switch to VMProtect and protect the application again. Run the new version:

C:\test>dummy_app.vmp.exe
serial number is bad
state = SERIAL_STATE_FLAG_BLACKLISTED

Run the old version of the application:

C:\test>dummy_app1.vmp.exe
serial number is correct, calling foo()
I'm foo
done

As said above, the old version doesn't know about blocked serial number and accepts it. The new version blocks such serial number. In the last step we'll try to lock code to serial number. Before doing that please generate a new serial number or unlock this one and protect the application again. It should accept serial number from the "serial.txt" file before we can continue.


Step #5: Lock code to serial number

One of the most popular methods of cracking is to find a conditional jump after checking serial number and replace it with unconditional one. Conditional jump jumps to "correct" or "invalid" branches of code depending on serial number correctness. Hacker replaces it with uncoditional jump to the "correct" branch, so the application will accept any serial number. Let's "crack" our test application like hacker may do - turn our IF-operator off:

char *serial = read_serial("serial.txt");
int res = VMProtectSetSerialNumber(serial);
delete [] serial;
if (false && res)
{

Now our application will accept any serial number. Of course, it took a few seconds to modify a source code. It will take several minutes to find and modify binary code after compilation. But it may take months to find and modify this code after VMProtect protection. If the application was protected correct, it checks serial number in many places and hacker will need a long time to find and fix all the jumps. But we'll add more protection.

Lock the code to serial number

Important! Demo-version of VMProtect limits number of protected functions. It allows to protect only one function. So if you try a demo-version, you should add only the foo() function to the project. Otherwise VMProtect may protect main() function instead of foo() and it's code will not be locked to the serial number.

VMProtect Licensing System allows to lock the code of one or several functions to serial number. They will not work without correct serial number. VMProtect will virtualize those functions and encrypt them using the key from serial number. So even if hacker will replace unconditional jump, it will not be able to decrypt such functions without correct serial number. Let's try this. Switch to VMProtect, select the foo() function in the project tree and open its properties.

Check the "Lock to serial number" box and protect the application again. Put the any text to serial.txt instead of correct serial number and run the application. You will see the following text in the console:

C:\test>dummy_app.vmp.exe
serial number is correct, calling foo()

As hacker "replaced" unconditional jump, the application will think that it is registered. But when it will try to run foo() function, it will fail with the following error:

As we locked foo() function to serial number and hacker doesn't have one, VMProtect will not be able to decrypt the function body. So it informs user that it is impossible to continue running the application and terminates it. When you press the "OK" button, you will not see "done" text in the console.

What to lock to serial number?

You should lock to serial number functions that will be called only in the registered version. As the code locking uses virtualization, those functions have to be virtualized (use Virtualization or Ultra modes in VMProtect). Do not forget that virtualisation decreases speed of code execution. For example if your text editor application doesn't allow to save documents in the demo version, you may lock saving function to serial number. If this function calls other functions while saving, you don't need to lock them too. Locking the "top" function is enough in most cases.

Please note, that attempt of execution of locking code without correct serial number will terminate the application. All changes will be lost. You need to test the application, so it will never run locked code in demo mode. For the text editor described above, make sure that you disable "Save" menu item, Ctrl+S hotkey and that you don't ask about saving while closing application. Otherwise customers may think that the application has bugs.

Invalid keys and code lock

When you call VMProtectSetSerialNumber() function, it analyzes the serial number and allows to decrypt locked code only if the serial number is correct. If it is blacklisted, expired, has invalid hardware id or anything else - the licensing module will not decrypt locked code and application will fail if it will try to execute the code.

Some limitations may be activated while running the application. For example running time limit may run out or serial number may expire by date. In that case licensing module will continue to decrypt code blocks until you set serial number again or terminate the application. It is hard for vendor to disable all "full-version" features of application immediately when limitations are activated, so it is safer to continue decrypting code blocks until the application will not find out the limitations and do something with them.


Licensing module API

Licensing module API is a part of VMPortect API and located in its SDK. API allows to set serial number, read its state and detailed information. API also allows to obtain hardware identifier of current computer to lock serial numbers to it.

VMProtectSetSerialNumber

Function passes serial number to the licensing module. Full specification follows:

INT __stdcall VMProtectSetSerialNumber(const char *SerialNumber);

Parameter SerialNumber should contain text string with serial number generated by VMProtect or by standalone generator. Serial number is base-64-encoded string, terminated by '\0'. Function returns set of bit flags, like the VMProtectGetSerialNumberState() does. Flags show if serial number is correct or invalid and describe why. If serial number is correct, function returns 0.

VMProtectGetSerialNumberState

Function returns state of serial number, set by VMProtectSetSerialNumber() call. Full specification is simple:

INT __stdcall VMProtectGetSerialNumberState();

If at least one bit of returned value is set - key is invalid and application should terminate or fall back to the demo mode. Below is a description of possible flags:

Flag | Value | Description
SERIAL_STATE_FLAG_CORRUPTED | 0x00000001 | Licensing module is corrupted. This may be caused by cracking attempts. Usually you will never get this flag.
SERIAL_STATE_FLAG_INVALID | 0x00000002 | Serial number is invalid. You will get this flag if licensing module will be unable to decrypt serial number of if the serial number is not yet set or it is empty.
SERIAL_STATE_FLAG_BLACKLISTED | 0x00000004 | Serial number is correct, but it was blacklisted in VMProtect.
SERIAL_STATE_FLAG_DATE_EXPIRED | 0x00000008 | Serial number is expired. You will get this flag if serial number has expiration date chunk and this date is in the past. You may read the expiration date by calling VMProtectGetSerialNumberData() function.
SERIAL_STATE_FLAG_RUNNING_TIME_OVER | 0x00000010 | Running time limit for this session is over. You may read limit value by calling VMProtectGetSerialNumberData() function.
SERIAL_STATE_FLAG_BAD_HWID | 0x00000020 | Serial number is locked to another hardware identifier.
SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED | 0x00000040 | Serial number will not work with this version of application, because it has "Max Build Date" block and the application was build after those date. You may read maximal build date by calling VMProtectGetSerialNumberData() function.

VMProtectGetSerialNumberData

Function reads content of serial number to the structure. Full specification follows:

BOOL __stdcall VMProtectGetSerialNumberData(VMProtectSerialNumberData *pData, UINT nSize);

The first parameter is a pointer to VMProtectSerialNumberData structure, that will receive all the information. The second parameter is a size of the structure. Function returns FALSE if licensing module is corrupted or if the structure or its size is invalid. Otherwise it returns TRUE and you may analyze its fields:

Field | Type | Description
nState | INT | Serial number bit mask. Exactly as returned by VMProtectGetSerialNumberState().
wUserName | wchar_t[256] | User name in UNICODE with terminating '\0'.
wEMail | wchar_t[256] | E-Mail in UNICODE with terminating '\0'.
dtExpire | VMProtectDate | Serial number expiration date. Format of VMProtectDate stucture is described below.
dtMaxBuild | VMProtectDate | Maximal date of product build that will accept the serial number. Format of VMProtectDate structure is described below.
bRunningTime | INT | Number of minutes that the application is allowed to work.
nUserDataLength | BYTE | Length of user data, stored in bUserData field.
bUserData | BYTE[255] | User data, stored in serial number. Length of actual data is stored in nUserDataLength field.

VMProtectDate structure has the following fields:

Field | Type | Description
wYear | WORD | Year.
bMonth | BYTE | Month, starting from 1.
bDay | BYTE | Day, starting from 1.

VMProtectGetCurrentHWID

Function allows to get hardware identifier of current computer. Full specification follows:

INT __stdcall VMProtectGetCurrentHWID(char * HWID, UINT nSize);

The first parameter is a pointer to a memory that will receive identifier. The second parameter is a size of that memory. Function returns number of bytes with terminating '\0'. If the first parameter is NULL, function will return number of required bytes. The most correct way to call the function is here:

INT nSize = VMProtectGetCurrentHWID(NULL, 0); // get number of required bytes
char *pBuf = new char[nSize]; // allocate buffer
VMProtectGetCurrentHWID(pBuf, nSize); // obtain hardeare identifier
// use it
delete [] pBuf; // release buffer


Serial number generators

Why do I need standalone generator?

Besides VMProtect, serial numbers using standalone generators. They are usually web-based and used to generate serial number by request from payment processing service. Customer pays money to service, service calls serial number generator, it generates serial number and sends it back to service and finally service sends it to the customer. Vendor receives notification and import serial number to the database.

How does it work

The licensing system based on asymmetric algorithms, so standalone serial number generator needs a products private key to generate licenses. You should export the key using "Export Key Pair" button from the product properties window.

Standalone generator usually called by HTTP-query. PHP-based generator can be called directly, DLL-based generator needs some environment to run. But both work the same way:

  • Get information about customer from payment processing service
  • Add some vendor and product-specific data
  • Pack data to a serial number
  • Encrypt serial number using the products algorithm
  • Send serial number to payment processing service
Do you provide working generators?

Yes, the licensing system is bundled with to serial number generators: DLL-based and .Net-based for Windows and PHP-based for UNIX.

Can I make my own generator?

Yes, you can. See the serial number format specifications here and encryption algorithm details here.

Is it safe?

Usually - yes. But here are some recommendations:

  • Use HTTPS protocol - if your payment processing service allow to query generator by HTTPS and your hoster allows to reply by HTTPS, it is better to use HTTPS. In that case data will be encrypted while exchange and nobody will be able to catch them.
  • Hide the generator - make sure that it is hard to find out URL for the generator. For example it is better to use www.site.com/abc123.php for generator instead of www.site.com/keygen.php. Make sure that there is no external links to the generator, that it doesn't listed in robots.txt and other system files. You may even put generator to another site that doesn't related to your product. The less is known about generator - the better.
  • Make sure that generator is called by payment processing service - before starting serial number generation it is recommended to check that serial number generator is called by payment processing service and not a hacker. Usually such services have list of IP addresses that they may use to call generators. Add checks that will compare caller's IP with the list, provided by service. If the caller IP is not listed - just return nothing. Or return error 404. Do not return errors like "Serial number cannot be generated" or "Bad IP address". This will give hacker too much information. And always send yourself e-mail with details of such attempts.
  • Check input parameters - payment processing services allows to define a number of parameters that will be passed to the generator. Do not use default names like "user" and "e-mail", use "sx43a" and "a094d" instead. Check parameters on the generators side. Do not reply to queries with incorrect parameters set. With missed parameters or with additional parameters. And do not forget to notify yourself by e-mail about such attempts. Attach caller IP, list of input parameters and other information.
  • Add a "password" - you may put some additional parameters to the query with random values. Check those parameters and values on the generators side and do not reply on query without such parameters.


DLL-Based Serial Number Generator

DLL-based serial number generator is: two DLL-files for x86 and x64 platforms, two LIB-files for both platforms and a single H-file for Microsoft Visual C compiler. You may link DLLs static or load them dynamic, H-file supports both ways.

All files can be found in Keygen\DLL folder. There is an example application that uses DLL-based serial number generator.

API of the generator

The serial number generator exports only two functions: the first one generates serial number, the second - releases memory, used by the first one. Let's start from the first function:

VMProtectErrors __stdcall VMProtectGenerateSerialNumber(
                VMProtectProductInfo * pProductInfo,
                VMProtectSerialNumberInfo * pSerialInfo,
                char ** pSerialNumber
            );

The first parameter is pointer to VMProtectProductInfo structure, that is filled by VMProtect during the Key Data Export. The structure holds private key of the product, parameters of used algorithm and a product identifier. Details of filling the structure are given below.

The second parameter is a pointer to VMProtectSerialNumberInfo structure that contains data for the new serial number. The structure has a bit mask field that defines fields that will be put to serial number.

struct VMProtectSerialNumberInfo
{
    INT		flags;
    wchar_t *	pUserName;
    wchar_t *	pEMail;
    DWORD		dwExpDate;
    DWORD		dwMaxBuildDate;
    BYTE		nRunningTimeLimit;
    char *		pHardwareID;
    size_t		nUserDataLength;
    BYTE *		pUserData;
};

The flags field contains any number of flags from VMProtectSerialNumberFlags enumeration:

  • HAS_USER_NAME - put user name, pointed by pUserName field to serial number.
  • HAS_EMAIL - put e-mail, pointed by pEMail field to serial number.
  • HAS_EXP_DATE - put expiration date from dwExpDate field to serial number.
  • HAS_MAX_BUILD_DATE - put maximal build date from dwMaxBuildDate field to serial number.
  • HAS_TIME_LIMIT - put running time limit from nRunningTimeLimit field to serial number (time measured in minutes and should not exceed 255).
  • HAS_HARDWARE_ID - put hardware identifier from pHardwareID to serial number.
  • HAS_USER_DATA - put user data of size defined by nUserDataLength field and pointed by pUserData field to serial number.

The last parameter is a pointer to pointer. It will receive a pointer to a new serial number. You have to release this pointer after use by passing it to the second API function:

void __stdcall VMProtectFreeSerialNumberMemory(char * pSerialNumber);

The VMProtectGenerateSerialNumber function returns result of VMProtectErrors type. If there is no error, it will return 0. Otherwise the following error codes are possible:

  • ALL_RIGHT - no errors, serial number was successfully created.
  • UNSUPPORTED_ALGORITHM - unsupported encryption algorithm passed in the first parameter.
  • UNSUPPORTED_NUMBER_OF_BITS - unsupported number of bits passed in the first parameter.
  • USER_NAME_IS_TOO_LONG - length of user name in UTF-8 encoding exceeds 255 bytes.
  • EMAIL_IS_TOO_LONG - length of e-mail in UTF-8 encoding exceeds 255 bytes.
  • USER_DATA_IS_TOO_LONG - length of user data exceeds 255 bytes.
  • HWID_HAS_BAD_SIZE - hardware identifier has invalid length.
  • PRODUCT_CODE_HAS_BAD_SIZE - product identifier passed in the first parameter has invalid length.
  • SERIAL_NUMBER_TOO_LONG - serial number is too long to be encrypted by selected algorithm.
  • BAD_PRODUCT_INFO - the first parameter is invalid or NULL.
  • BAD_SERIAL_NUMBER_INFO - the second parameter is invalid or NULL.
  • BAD_SERIAL_NUMBER_CONTAINER - the third parameter doesn't point to a pointer.
  • NOT_EMPTY_SERIAL_NUMBER_CONTAINER - the third parameter doesn't point to a NULL-pointer.
  • BAD_PRIVATE_EXPONENT - private exponent passed in the first parameter is invalid.
  • BAD_MODULUS - modulus passed in the first parameter is invalid.

There are two types of errors: errors caused by invalid environment (mostly the first parameter of function) and errors caused by invalid serial number contents. For the first type of errors you need to make sure that you exported products settings without errors, see the example of correct export and environment setup below.

Errors of the second type mean that serial number can't be generated because of data you want to put in it. User name may be too long, or hardware id can be invalid or something like this. In that case you have to send all the information to yourself by e-mail or using any other method and reply "Serial number will be sent to you in 24 hours" to the payment processing service. After receiving such notification from serial number generator you should analyze the problem and generate a serial number yourself in VMProtect.

A simple example

Below is an example that generates serial number using DLL-based generator. The block of code at the very beginning of example should be filled by data, exported from VMProtect. Otherwise the example will not work at all.

//////////////////////////////////////////////////////////////////////////
// !!! this block should be generated by VMProtect !!! ///
//////////////////////////////////////////////////////////////////////////

VMProtectAlgorithms g_Algorithm = ALGORITHM_RSA;
size_t g_nBits = 0;
byte g_vModulus[1];
byte g_vPrivate[1];
byte g_vProductCode[1];

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

int _tmain(int argc, _TCHAR* argv[])
{
    VMProtectProductInfo	pi;
    pi.algorithm = g_Algorithm;
    pi.nBits = g_nBits;
    pi.nModulusSize = sizeof(g_vModulus);
    pi.pModulus = g_vModulus;
    pi.nPrivateSize = sizeof(g_vPrivate);
    pi.pPrivate = g_vPrivate;
    pi.nProductCodeSize = sizeof(g_vProductCode);
    pi.pProductCode = g_vProductCode;

    VMProtectSerialNumberInfo si = {0};
    si.flags = HAS_USER_NAME | HAS_EMAIL;
    si.pUserName = L"John Doe";
    si.pEMail = L"john@doe.com";
    char * pBuf = NULL;
    VMProtectErrors res = VMProtectGenerateSerialNumber(&pi, &si, &pBuf);
    if (res == ALL_RIGHT)
    {
        printf("Serial number:\n%s\n", pBuf);
        VMProtectFreeSerialNumberMemory(pBuf);
    }
    else
    {
        printf("Error: %d\n", res);
    }

    return 0;
}

The full version of the above code can be found in Keygen\DLL\Example. The most interesting pieces of the code are described below.

The first lines of main() function fill the VMProtectProductInfo structure by data exported from VMProtect. This is a standard code and it is recommended to copy it as-is to your generator. After that we create VMProtectSerialNumberInfo structure and flag fields is filled with combination of user name and e-mail flags. Then we put unicode strings to corresponding fields of the structure. Serial number generator accepts UNICODE strings and converts them to UTF-8 later, while generating serial number.

After that we define pointer variable that will receive a serial number and then we call VMProtectGenerateSerialNumber function. If there were no errors while generation, we print serial number to console and release allocated memory.

Other fields of VMprotectSerialNumberInfo structure

Some fields require additional comments. For example dwExpDate and dwMaxBuildDate accepts specially formatted dates: 0xYYYYMMDD, year stored in higher word, month and day stored in higher and lower bytes of the lower word. Header file has macro MAKEDATE(y, m, d) that simplifies the proccess. It can be called like this: MAKEDATE(2010, 05, 12).

The pHardwareID field should point to an ASCII string, returned by VMProtectGetCurrentHWID() method from licensing module API.


Serial Number Generator For The .Net Platform

.Net-version of the serial number generator is an assembly that allows to generate serial numbers according to provided settings. Sources are located at Keygen\Net as a solution with two projects: KeyGen - the generator itself and Usage - example project that uses the generator.

Although, the generator is shipped as a source code, it is not recommended to make any changes in the generator's assembly. The main purpose of source code shipment is an ability to build the generator for various versions of .Net Framework. Any modifications may lead to errors and incompatibles that are hard to find, so if you feel that the generator should be changed, please contact us. If your reasons are serious, we will do this for you, so everybody win as a result.

Using generator

You should start using the code from the Usage project. Copy it to your project and add reference to VMProtect.KeyGen.dll assembly. After that you will be able to generate serial numbers. Before you start, you should init the generator with the information about the product you are going to build serial numbers for. You need to export this information from the "Licenses" page of VMProtect. Press the "Export" button above the licenses list and select "KeyGen.Net" format from the drop down list. Then copy the lines in the field below and paste them as a string for the data variable in the example below:

try
{
    string data = @""; // put the exported data here
    Generator g = new Generator(data);
    g.UserName = "John Doe";
    g.EMail = "john@doe.com";
    g.ExpirationDate = DateTime.Now.AddMonths(1);
    g.MaxBuildDate = DateTime.Now.AddYears(1);
    g.RunningTimeLimit = 15;
    g.HardwareID = "AQIDBAgHBgU=";
    g.UserData = new byte[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    string serial = g.Generate();
    Console.WriteLine("Serial number:\n{0}\n", serial);
}
catch (Exception ex)
{
    Console.WriteLine("Error: {0}", ex);
}

While being constructed, generator parses the product's data. If the constructor didn't throw exception, you are ready to generate serial numbers. Otherwise you will get exception with the description of the problem.

All the information serial number contains is defined using the generator's properties. Some of them have constraints though. For example User Name and E-Mail fields cannot accept strings exceeding 255 bytes in UTF-8 encoding. In that case setting the property may lead to throwing exception.

After adjusting the generator, you need to call the Generate() method, that builds a serial number. It packs the data and encrypts the result. If the length of data exceeds the maximal size of serial number, exception will be thrown.

You may generate as many serial numbers as you need using the same generator. Each call to Generate() makes a new serial number with the same data. Although you may change generator's properties if neccessary. To remove something from the serial number, just set the corresponding property to null.


PHP-Based Serial Number Generator

The full version of PHP-based generator can be found in Keygen\PHP folder, below you will find description of the most interesting details.

Setup of PHP-based generator

At the top of keygen.php file there are some lines that setup the generator:

//////////////////////////////////////////////////////////////////////////////////////////////
// The following lines should be generated by VMProtect
$exported_algorithm = "RSA";
$exported_bits = 2048;
$exported_private = "PJvj4kEpoQMIpYK+9wEt......xKeiSZgzdiln8Q==";
$exported_modulus = "rOlny/3QgZb/VmGr3CmY......I6ESAUmtQ+RBqQ==";
$exported_product_code = "oLQdGUn8kVk=";
//////////////////////////////////////////////////////////////////////////////////////////////

This code is generated by VMProtect (see Export Key Pair section for details). It is important to copy it without errors. Otherwise serial numbers will not be generated or will not work with protected application.

Serial number content

In the next lines of keygen.php we'll provide data that will be placed to the new serial number. Data stored in the keyed array, some of them may absent. All fields are the same as in VMProtect, when you create a new license.

$params = array(
    user_name => "John Doe", // UTF-8!
    email => "john@doe.com",
    hwid => "vHGMdMRvGCPjWcCQ", // Exactly as returned by VMProtectGetCurrentHWID
    expire_date => array(year => 2009, month => 10, day => 1),
    maxbuild_date => array(year => 2009, month => 10, day => 1),
    time_limit => 10,
    user_data => base64_decode("CGCvRvMWcPHGdMjQ"), // string of bytes
    );
Handler function for successfull generation

Next code block in keygen.php is a function that will be called if the serial number will be generated end encrypted without errors. It has single parameter - string with new serial number. The purpose of the function is to send serial number to the payment processing service. As the generator is called by HTTP-query and this is PHP - just print it using echo command. You may want to split it to lines of 75 characters length. You may also want to send yourself a copy of the serial number or put it to the database. This is up to you.

function OnSerialGenerated($serial)
{
    $serial = wordwrap($serial, 75, "\n", true);
    echo $serial;
}
Handler function for failed generation

The last piece of code in keygen.php is a function that will be called in case of any problems while generation or encryption. Function has single parameter - english text that describes what was wrong.

function OnSerialGenerationFailed($details)
{
    echo "You will receive serial number in the next 24 hours"; // message to the customer
//	mail("support@vendor.com", "Houston, we have a problem", $details); // message to vendor
}

This fucntion can be called if user name doesn't fit to the serial number or if serial number is too long, or if encryption failed because of bad parameters. There are many reasons why this function may be called. You should do two things here. First of all, you have to reply something to payment processing service that called you. It waits for serial number, but you don't have it, so just reply "The serial number will be sent to you in 24 hours".

Afther that collect all the information you can. Collect input parameters, $params block, description of the problem that you received as a parameter. Collect $_GET and $_POST arrays and send them all to yourself by e-mail. You have to generate serial number yourself, so you need as much information as possible. Do not call die() here. It will be called automatically a bit later.

Notes

The PHP-based serial number generator is very simple. It doesn't care about recomendations given here. It doesn't check caller's IP address and doesn't read input parameters. Do not forget to follow the recomendations while modifying the generator for your needs.

The serial number generator accepts user name and e-mail in UTF-8 encoding. Ask your payment processing service about encoding they use to send you those parameters. Maybe you will have to convert them before creating serial number. Incorrect encoding will not break serial number, but if you want to display user name or e-mail in "About" window - it is better to use UTF-8.

Asymmetric encryption is a time and CPU consuming process. Being implemented using plain PHP it may take up to 20 seconds to encrypt serial number with 2048-bits RSA key. The serial number generator tries to use accelerated functions if any of them exists. It tries gmp_powm, bi_powmod, bcpowmod and falls back to PHP if none of them presents. The first one works 20 times faster than the last one, so it is better to check what functions are supported by your host and maybe ask system administrator to enable faster functions to speedup number generation process.


Serial Number Format

Layout of serial number

Serial number is a set of chunks. Each chunk starts from identifier byte that defines contents of the chunk. The last chunk always has id=255 and holds checksum of all chunks listed before.

Chunks may have fixed or variable length, depending of its type. For chunks of variable length the byte after identifier usually contains length of chunk. Detailed description of every chunk is below:

Format of chunks
ID | Chunk | Length (bytes) | Description | Example
0x01 | Version | 1 | Chunk holds version of serial number. Version has to be 1. | 01 01
0x02 | User Name | 1 + N | Chunk holds user name in UTF-8 encoding. The first byte after ID byte contains length of user name. Terminating '\0' is not required. Maximal length of user name is 255 bytes. | 02 04 4A 5F 48 4E
0x03 | E-Mail | 1 + N | Chunk holds e-mail in UTF-8 encoding. The first byte after ID byte contains length of e-mail. Terminating '\0' is not required. Maximal length of e-mail is 255 bytes. | 03 07 61 40 62 2E 63 6F 6D
0x04 | Hardware Identifier | 1 + N | Chunk holds hardware identifier returned by VMProtectGetCurrentHWID(). The function returns base-64 encoded string, chunk holds decoded, binary representation of hardware identifier. Length of identifier should be a multiple of 4. Next byte after ID byte contains length of hardware identifier. Maximal length is 32 bytes. | 04 08 E1 E2 E3 E4 A1 A2 A3 A4
0x05 | Expiration Date | 4 | Chunk holds date of expiration for the serial number. Format of date is described below. | 05 01 0A 07 DA
0x06 | Running Time Limit | 1 | Chunk holds 1 byte with number of minutes that application is allowed to run. Maximal allowed time is 255 minutes. | 06 05
0x07 | Product Code | 8 | Chunk holds 8 bytes of product code that is generated by VMProtect. Serial number holds this data in binary format, so they have to be converted from base-64 encoding. Please make sure that length of binary data is exactly 8 bytes. This chunk is required. Apllication will not work without it! | 07 01 02 03 04 05 06 07 08
0x08 | User Data | 1 + N | Chunk holds up to 255 bytes of user data. The next byte after ID byte contains length of the following data. Application can read this data by calling VMProtectGetSerialNumberData() function. | 08 05 01 02 03 04 05
0x09 | Maximal Build Date | 4 | Chunks holds maximal date of application build that will support this serial number. Date format is described below. | 09 01 0A 07 DA
0xFF | Checksum | 4 | Chunk holds 4 bytes of checksum for all chunks listed before it. Details about calculating checksum are below. | FF 01 02 03 04

Date storage format

Serial number keeps date in 4 byte DWORD - 0xYYYYMMDD. Higher word contains year, lower word contains month and day. Bytes are ordered in little endian order - from lower to higher. If you have a pointer to the first byte of data, you may read and write it using the following code:

byte *pDate = 0xNNNNNN; // pointer to the stored date
*(DWORD *)pDate = (2010 << 16) | (10 << 8) | 1; // October 1, 2010
DWORD dwExp = *(DWORD *)pDate;
Checksum computation

Checksum of the serial number is computed using SHA-1 algorithm. SHA-1 returns 5 32-bit numbers, the first one is used as checksum. Important: 32-bit number is stored in little endian order, not the big endian usually used to print SHA-1 hashes. So if your SHA-1 function returns string, you may need to reverse order of bytes before storing it to serial number.

Notes

Chunks with other identifiers are ignored by licensing module. New chunks may appear in the future versions. It is not recommended to create chunks using "free" identifiers! This may break serial numbers in the future when licensing system will use those chunks. And anyway protected application will not be able to read those chunks. If you need to store some information in the serial number, use User Data field.

Serial numbers don't contain "SALT" - random data that make each number unique even for the same data. Salt will be added later while encryption of serial number. If you need to generate several different serial numbers for one customer you may append a number to the user name or use User Data field.


Serial number encryption algorithms

Reliability of serial numbers in licensing system is based on asymmetric encryption schemes. Current version of licensing system uses RSA with length of key from 1024 to 16384 bits. In the future versions we'll add ECC-based algorithms and combined algorithms that use asymmetric encryption with symmetric ciphers.

Each product has its own algorithm. Serial numbers, created using one algorithm will not work with another one. That's why you can't change algorithm and its parameters after creating a single license. Licensing module, embedded in application, knows about algorithm used by product and will not accept serial number created with another algorithm.

RSA algorithm

Licensing system encrypts serial number with RSA using the following scheme:

  • Adding random data to the beginning of serial number - this is based on RFC2313, but implementation is a bit different. The following bytes are added at the beginning of serial number: 00 02 NN...NN 00. Where NN are from 8 to 16 random non-zero bytes. Number of bytes is random, but it may be limited by a total length of serial number and number of bits in the RSA keys.

  • Adding random data to the end of serial number - total number of bytes in the serial number before encryption should be equal to number of bits in RSA key, divided by 8. That's why random bytes are added to the end of serial number until total length of serial number will not be maximal possible.
  • Encryption - the default scheme is used that is implemented in many "big number" libraries. You may see PHP-based generator for details of implementation.
  • Encoding - binary serial number after encryption is encoded to base-64 before sending to the customer.


Activation System

Activation

What is it?

Basically, activation is a two-step application registering process. At the first step (usually, after the order) a customer receives an activation code (it looks like XXXX-YYYY-ZZZZ). At the second step, the customer enters the code to the application, the application connects to the vedor's server, checks the code and get the real serial number back.

This allows to control the usage of serial number, prevent their leaking and some other things like demo licenses and time-limited trials.

Why do I need it?

Well, activation feature allows vendor to implement some neat features, for example:

  • Short serial numbers - don't bother your users with entering long serial numbers and making mistakes. Use short and readable activation codes and VMProtect will do the rest.
  • Control the number of installations - you see all the activations online in the real time. Track them, analyze them, block them, communicate your customers and so on.
  • Time trials - generate time-limited serial numbers using the special product "modes".
  • Annual licenses - this works pretty much the same way as time trials, but you may use the same feature to sell subscription-based software easily. Exactly like most antivirus vendors do, you will just sell the activation codes for the next year of service.

Please visit the Web License Manager page for more details on how the activation system works.

What do I need?

VMProtect Ultimate has some new API functions to implement both online and offline activation. You also need a Web License Manager on a server side to support the activation features. Finally, you need to setup the protection project in VMProtect itself.


Activation Support In VMProtect

Setup Activation in VMProtect

Activation API needs the address of the Web License Manager server to operate, so you need to put it to the VMProtect project. To do this run VMProtect and go to the options section:

Enter the address to the "Activation server" field. The address should be http://yourserver/weblm path/activate.php. If you later get errors during the online activation testing, this is the first place to check.


Activation Support In Web License Manager

Activation in Web License Manager

The Web License Manager is described here in more details, so please visit that page if you have questions. Below you will find a simple guide on how to create the activation code that can be used with the Activation API later.

First of all, login to the Web License Manager (you can use the demo version on our site) and create a product. Then export that product as VMProtect project to be able to use the licensing and activation modules. When done, go to the activation codes panel and click the "Add New" link at the top:

Select your product in the top field and fill all the other fields with the data you'd like to put to the serial number. Then click the "Save" button. You'll see the activation code that you may use for testing the API:

Web License Manager allows you to generate activation codes automatically, it can be integrated with e-commerce providers and much more. Visit the site link above for mode details.


Activation API

There are 4 API functions in the Activation API. Two for online mode and two for offline. Activation API is about dealing with the Web License Manager, you still need the Licensing API to set up the serial numbers.

VMProtectActivateLicense

Function passes the activation code to the server and returns the serial number for that exactly installation. Otherwise it returns the error code.

INT __stdcall VMProtectActivateLicense(const char *code, char *serial, int size);

Parameter code contains the activation code that is provided by the Web License Manager during the order process. The serial parameter points to the memory block of size size, enough to hold the serial number.

VMProtectDeactivateLicense

Function passes the serial number to the server for deactivation. See the possible return codes to check the result of deactivation.

INT __stdcall VMProtectDeactivateLicense(const char *serial);

Parameter serial contains the serial number (not the code) obtained from the server during the activation process.

VMProtectGetOfflineActivationString
VMProtectGetOfflineDeactivationString

These two functions work pretty much the same way as the online ones above, but they don't try to connect the server. Instead, they return a "magic" string that needs to be copy-pasted to the web browser, showing the Web License Manager offline activation page.

INT __stdcall VMProtectGetOfflineActivationString(const char *code, char *buf, int size);
INT __stdcall VMProtectGetOfflineDeactivationString(const char *serial, char *buf, int size);

The code and serial parameters are the same as in the functions above, the buf parameter should be about 1000 characters and receives a null-terminated text string that needs to be copied by the user. See the possible error codes here.

The possible return codes

Flag | Value | Description
ACTIVATION_OK | 0 | Activation successful, the serial field contains a serial number.
ACTIVATION_SMALL_BUFFER | 1 | The serial number buffer is too small. The formula for the buffer size is: bits / 8 * 3 / 2 + N, where bits is the length of your RSA key and N is a "safety" buffer that should be at least 10.
ACTIVATION_NO_CONNECTION | 2 | The activation module was unable to connect the Web License Manager.
ACTIVATION_BAD_REPLY | 3 | The server returned unexpected reply. This means configuration problems or probably a cracking attempt.
ACTIVATION_BANNED | 4 | The activation code is blocked on the server. This doesn't mean that the number of possible activations is exceeded, this means that this exactly code is banned by the vendor, so the user is trying to cheat you.
ACTIVATION_CORRUPTED | 5 | Something goes really wrong here. The error means that the internal activation stuff is not working, it is not safe to continue with the activation, registration and so on.
ACTIVATION_BAD_CODE | 6 | The code has been successfully passed to the server and it was unable to find it in the database. Probably the user made a mistake, so it is worth asking the user to check the entered code for mistakes.
ACTIVATION_ALREADY_USED | 7 | The code has been used too many times and cannot be used more. This doesn't mean the code is bad or banned, it is OK. The user may contact vendor to ask/purchase more activations, or the user may deactivate some installations to increase the number of activations available for that code.
ACTIVATION_SERIAL_UNKNOWN | 8 | This is the deactivation error code that means that the server can't find the serial number that the user tries to deactivate.

Tips and tricks

The Activation API is quite straightforward, so it shouldn't be difficult to use it. Do not forget to provide an offline mode for the users, so they can activate the software even without the Internet access. Please also note, that the Activation API neither stores the serial number nor passes it to the licensing module. This is up to the developer to do this. It is not necessary to call the Activation API every time the application starts, call it once and then use the serial number and the licensing API.


Using scripts

VMProtect has a powerful built-in scripting language that allows you to enhance the VMProtect features in every step of processing the protected file. The language built into VMProtect is an object-oriented language supporting:

  • Classes
  • Built-in functions
  • Calling functions from external DLLs
  • Events

The syntax of the scriping language is similar to Object Pascal in many ways. You can find sample scripts in the "VMProtect/Examples/Scripts" folder.


Classes

The scripting language built into VMProtect is an object-oriented language and is similar to Object Pascal in many ways. The scripting language includes both standard classes providing the basic functionality and specialized classes providing access to the application protection features. Below you can see the hierarchy of all available classes:

  • TObject
    • TPersistent
      • TStrings
        • TStringList
    • TStream
      • THandleStream
        • TFileStream
    • TIniFile
    • TVMObject
      • TPESection
      • TMapRecord
      • TPEDirectory
      • TPEDirectories
      • TPEExportRecord
      • TPEResourceRecord
        • TPEResourceRecords
      • TPEFile
      • TIntelRecord
      • TWatermark
      • TInternalDll
      • TVMObjectList
        • TPESections
        • TMapRecords
        • TPEExportRecords
        • TExtAddresses
        • TVMProcedure
        • TWatermarks
        • TInternalDlls
        • TVMProtector

TObject

This basic class is a parent for all classes in the language.

TObject = class
  constructor Create;
  procedure Free;
end;
constructor TObject.Create;

The Create constructor is used to create an instance of the TObject class or its children.

procedure TObject.Free;

The Free method is a safe method to free resources and destroy an object.

TPersistent

This class provides the method of copying the content of one object (source) into another (self) and the target object remains the same. This peculiarity does not allow the simple assignation of object variables, but requires a separate method.

TPersistent = class(TObject)
  procedure Assign(Source: TPersistent);
end;
procedure TPersistent.Assign(Source: TPersistent);

The Assign method allows the duplication of an object, which involves assigning values to an object that include all properties of another object.

TStrings

This class provides the set of methods and properties necessary for working with strings and string sets.

TStrings = class(TPersistent)
  function Add(const S: String): Integer;
  procedure Append(const S: String);
  procedure AddStrings(Strings: TStrings);
  procedure Clear;
  procedure Delete(Index: Integer);
  function IndexOf(const S: String): Integer;
  procedure Insert(Index: Integer; const S: String);
  procedure LoadFromFile(const FileName: String);
  procedure SaveToFile(const FileName: String);
  property Count: Integer; read;
  property Text: String; read write;
  property CommaText: String; read write;
  property Strings[Index: Integer]: String; read write;
  property Objects[Index: Integer]: TObject; read write;
end;
function TStrings.Add(const S: String): Integer;

Adds a string to the set and returns its index.

procedure TStrings.Append(const S: String);

Adds a string to the end of the string set.

procedure TStrings.AddStrings(Strings: TStrings);

Adds a new set to the current string set.

procedure TStrings.Clear;

Clears the data set and frees the memory allocated to it.

procedure TStrings.Delete(Index: Integer);

Destroys the set item with the index and frees the memory allocated to it.

function TStrings.IndexOf(const S: String): Integer;

Retuns the index of the string S. If there is no string S in the set, it returns -1.

procedure TStrings.Insert(Index: Integer; const S: String);

Inserts the string S into the set and assigns the Index index to it.

procedure TStrings.LoadFromFile(const FileName: String);

Loads a set of strings from a file.

procedure TStrings.SaveToFile(const FileName: String);

Saves the string set to a file.

property TStrings.Count: Integer; read;

Returns the number of strings in the set.

property TStrings.Text: String; read write;

Returns the string set as one long string with end-of-line delimiters between separate strings in the set.

property TStrings.CommaText: String; read write;

Used to set or get the entire string set as one string with quotation marks and commas.

property TStrings.Strings[Index: Integer]: String; read write;

Allows access to the string with the Index index.

property TStrings.Objects[Index: Integer]: TObject; read write;

Allows access to the object connected with the string with the Index index.

TStringList

TDuplicates = (dupIgnore, dupAccept, dupError);
TNotifyEvent = procedure(Sender: TObject);

TStringList = class(TStrings)
  function Find(const S: String; var Index: Integer): Boolean;
  procedure Sort;
  property Duplicates: TDuplicates; read write;
  property Sorted: Boolean; read write;
  property OnChange: TNotifyEvent; read write;
  property OnChanging: TNotifyEvent; read write;
end;
function TStringList.Find(const S: String; var Index: Integer): Boolean;

Searches the set for the string S and, if found, returns its index in the index parameter. The output value of the function indicates whether the search is successful.

procedure TStringList.Sort;

Sorts strings in the set according to the specified rules.

property TStringList.Duplicates: TDuplicates; read write;

This property allows control (if possible) of the set, to achieve two or more identical strings.

property TStringList.Sorted: Boolean; read write;

Defines if it is necessary to sort strings alphabetically.

property TStringList.OnChange: TNotifyEvent; read write;

Defines how to react to modifications in the string set. The event occurs after the last modification.

property TStringList.OnChanging: TNotifyEvent; read write;

Defines how to react to modifications in the string set. The event occurs before the next modification.

TStream

This basic class provides the execution of the basic stream operations irrelative to the actual media.

TStream = class(TObject)
  function Read(Buffer: AnsiString; Count: Longint): Longint;
  function Write(Buffer: AnsiString; Count: Longint): Longint;
  function Seek(Offset: Longint; Origin: Word): Longint;
  procedure ReadBuffer(Buffer: AnsiString; Count: Longint);
  procedure WriteBuffer(Buffer: AnsiString; Count: Longint);
  function CopyFrom(Source: TStream; Count: Longint): Longint;
  property Position: Longint; read write;
  property Size: Longint; read write;
end;
function TStream.Read(Buffer: AnsiString; Count: Longint): Longint;

Reads bytes from the Count stream to the Buffer buffer. Returns the number of copied bytes.

function TStream.Write(Buffer: AnsiString; Count: Longint): Longint;

Writes bytes from the Buffer buffer to the Count stream. Returns the number of copied bytes.

function TStream.Seek(Offset: Longint; Origin: Word): Longint;

This abstract class is overridden when dealing with children. Offsets the current position in the actual media for Offset bytes depending on the Origin condition:

  • soFromBeginning - the offset must be positive and counted from the beginning of the stream
  • soFromCurrent - the offset relative to the current position in the stream
  • soFromEnd - the offset must be negative and counted from the end of the stream
procedure TStream.ReadBuffer(Buffer: AnsiString; Count: Longint);

Reads bytes from the Count stream to the Buffer buffer. Returns the number of copied bytes.

procedure TStream.WriteBuffer(Buffer: AnsiString; Count: Longint);

Writes bytes from the Buffer buffer to the Count stream. Returns the number of copied bytes.

function TStream.CopyFrom(Source: TStream; Count: Longint): Longint;

Copies count bytes from the Source stream starting from the current position. Returns the number of copied bytes.

property TStream.Position: Longint; read write;

Determines the current position in the stream.

property TStream.Size: Longint; read write;

Determines the size of the stream in bytes.

THandleStream

The THandleStream class encapsulates the stream connected to the physical media via a handle.

THandleStream = class(TStream)
  constructor Create(AHandle: Integer);
  property Handle: Integer; read;
end;
constructor THandleStream.Create(AHandle: Integer);

The create constructor is used to create an instance of the class. The value of the handle is sent in the constructor parameter.

property THandleStream.Handle: Integer; read;

Determines the value of the handle.

TFileStream

This class is used to work with a file via streams.

TFileStream = class(THandleStream)
  constructor Create(const FileName: String; Mode: Word);
end;
constructor TFileStream.Create(const FileName: String; Mode: Word);

The FileName parameter defines the file name. The mode parameter defines the type of operations and access rights applied to this object:

  • fmCreate - create a new file. If the file exists, it is only opened for reading;
  • fmOpenRead - open for reading;
  • fmOpenWrite - open for writing;
  • fmOpenReadWrite - open for reading/writing;
  • fmShareCompat - multi-user access mode;
  • fmShareExclusive - exclusive mode. No application can read or modify the open file;
  • fmShareDenyWrite - forbid writing for other users and applications;
  • fmShareDenyRead - forbid reading for other users and applications;
  • fmShareDenyNone - forbid sharing the resource.

TIniFile

This class is used to work with ini files.

TIniFile = class(TObject)
  constructor Create(const FileName: String);
  function SectionExists(const Section: String): Boolean;
  function ReadString(const Section, Ident, Default: String): String;
  procedure WriteString(const Section, Ident, Value: String);
  function ReadInteger(const Section, Ident: String; Default: Longint): Longint;
  procedure WriteInteger(const Section, Ident: String; Value: Longint);
  function ReadBool(const Section, Ident: String; Default: Boolean): Boolean;
  procedure WriteBool(const Section, Ident: String; Value: Boolean);
  function ReadDate(const Section, Name: String; Default: TDateTime): TDateTime;
  function ReadDateTime(const Section, Name: String; Default: TDateTime): TDateTime;
  function ReadFloat(const Section, Name: String; Default: Double): Double;
  function ReadTime(const Section, Name: String; Default: TDateTime): TDateTime;
  procedure WriteDate(const Section, Name: String; Value: TDateTime);
  procedure WriteDateTime(const Section, Name: String; Value: TDateTime);
  procedure WriteFloat(const Section, Name: String; Value: Double);
  procedure WriteTime(const Section, Name: String; Value: TDateTime);
  procedure ReadSection(const Section: String; Strings: TStrings);
  procedure ReadSections(Strings: TStrings);
  procedure ReadSectionValues(const Section: String; Strings: TStrings);
  procedure EraseSection(const Section: String);
  procedure DeleteKey(const Section, Ident: String);
  procedure UpdateFile;
  function ValueExists(const Section, Ident: String): Boolean;
  property FileName: String read;
end;
constructor TIniFile.Create(const FileName: String);

Creates an instance of the TIniFile class associated with the FileName file.

function TIniFile.SectionExists(const Section: String): Boolean;

Checks whether there is a section named Section in the ini file and returns True if the section exists.

function TIniFile.ReadString(const Section, Ident, Default: String): String;

Reads the Ident parameter of the Section section from the ini file. If there is no such parameter, it returns Default.

procedure TIniFile.WriteString(const Section, Ident, Value: String);

Assigns the Values value to the Ident string parameter of the Section section in the ini file.

function TIniFile.ReadInteger(const Section, Ident: String; Default: Longint): Longint;

Reads the Ident integer parameter of the Section section from the ini file. If there is no such parameter, it returns Default.

procedure TIniFile.WriteInteger(const Section, Ident: String; Value: Longint);

Assigns the Values value to the Ident integer parameter of the Section section in the ini file.

function TIniFile.ReadBool(const Section, Ident: String; Default: Boolean): Boolean;

Reads the Ident logical parameter of the Section section from the ini file. If there is no such parameter, it returns Default.

procedure TIniFile.WriteBool(const Section, Ident: String; Value: Boolean);

Assigns the Values value to the Ident logical parameter of the Section section in the ini file.

function TIniFile.ReadDate(const Section, Name: String; Default: TDateTime): TDateTime;

Reads the date from the Name parameter located in the Section section of the ini file. If there is no such parameter, it returns Default.

function TIniFile.ReadDateTime(const Section, Name: String; Default: TDateTime): TDateTime;

Reads the date and time from the Name parameter located in the Section section of the ini file. If there is no such parameter, it returns Default.

function TIniFile.ReadFloat(const Section, Name: String; Default: Double): Double;

Reads the Name real-valued parameter of the Section section from the ini file. If there is no such parameter, it returns Default.

function TIniFile.ReadTime(const Section, Name: String; Default: TDateTime): TDateTime;

Reads the time from the Name parameter located in the Section section of the ini file. If there is no such parameter, it returns Default.

procedure TIniFile.WriteDate(const Section, Name: String; Value: TDateTime);

Writes the date to the Name parameter located in the Section section of the ini file.

procedure TIniFile.WriteDateTime(const Section, Name: String; Value: TDateTime);

Writes the date and time to the Name parameter located in the Section section of the ini file.

procedure TIniFile.WriteFloat(const Section, Name: String; Value: Double);

Assigns the Values value to the Name real-valued parameter of the Section section in the ini file.

procedure TIniFile.WriteTime(const Section, Name: String; Value: TDateTime);

Writes the time to the Name parameter located in the Section section of the ini file. If there is no such parameter, it returns Default.

procedure TIniFile.ReadSection(const Section: String; Strings: TStrings);

Reads the Section section from the ini file to the Strings variable.

procedure TIniFile.ReadSections(Strings: TStrings);

Reads all sections from the ini file to the Strings variable.

procedure TIniFile.ReadSectionValues(const Section: String; Strings: TStrings);

Reads the values of parameters in the Section section from the ini file to the Strings variable.

procedure TIniFile.EraseSection(const Section: String);

Deletes the Section section from the ini file.

procedure TIniFile.DeleteKey(const Section, Ident: String);

Deletes the Ident parameter of the Section section in the ini file.

procedure TIniFile.UpdateFile;

Saves the values of parameters from the ini file located in the clipboard to the disk. The function makes sense only in Windows 97 because Windows NT systems do not use cache in their work with ini files.

function TIniFile.ValueExists(const Section, Ident: String): Boolean;

Checks whether the Ident parameter of the Section section exists in the ini file and returns True if the parameter exists.

property TIniFile.FileName: String read;

Returns the name of the file associated with the instance of the TIniFile class.

TVMObject

The basic class that is a parent for all specialized classes in VMProtect.

TVMObject = class(TObject)
  constructor Create(AOwner: TVMObject);
  procedure Assign(SourceObject: TVMObject);
  property Owner: TVMObject; read;
end;
constructor TVMObject.Create(AOwner: TVMObject);

Creates an instance of the TVMObject class specifying AOwner that will be the owner of the created object.

procedure TVMObject.Assign(SourceObject: TVMObject);

The Assign method allows the duplication of an object, which involves assigning values to an object the include all properties of another object.

property TVMObject.Owner: TVMObject; read;

Provides access to the object owner.

TVMObjectList

This abstract class is used to work with a list of objects.

TVMObjectList = class(TVMObject)
  procedure Clear;
  procedure Delete(Index: Integer);
  property Count: Longint; read;
end;
procedure TVMObjectList.Clear;

Clears the object list and frees the memory allocated to it.

procedure TVMObjectList.Delete(Index: Integer);

Destroys the list item with the Index index and frees the memory allocated to it.

property TVMObjectList.Count: Longint; read;

Returns the number of objects in the list.

TPESection

This class is used to work with the PE section of the file.

TPESection = class(TVMObject)
  property Name: String; read write;
  property PhysicalOffset: Longint; read;
  property PhysicalSize: Longint; read;
  property RVA: Longint; read;
  property VirtualSize: Longint; read;
  property Flags: Longint; read;
  property IncludedInPacking: Boolean; read write;
end;
property TPESection.Name: String; read write;

Determines the name of the section.

property TPESection.PhysicalOffset: Longint; read;

Determines the physical offset of the section in the file.

property TPESection.PhysicalSize: Longint; read;

Determines the physical size of the section in the file.

property TPESection.RVA: Longint; read;

Determines the relative address of the section.

property TPESection.VirtualSize: Longint; read;

Determines the virtual size of the section.

property TPESection.Flags: Longint; read;

Determines the properties of the section using a set of bits:

  • IMAGE_SCN_CNT_CODE - the section contains executable code;
  • IMAGE_SCN_CNT_INITIALIZED_DATA - the section contains initialized data;
  • IMAGE_SCN_CNT_UNINITIALIZED_DATA - the section contains uninitialized data;
  • IMAGE_SCN_MEM_DISCARDABLE - the section may be unloaded from memory after the application is started;
  • IMAGE_SCN_MEM_NOT_CACHED - the section is not cached;
  • IMAGE_SCN_MEM_NOT_PAGED - the sections is not paged;
  • IMAGE_SCN_MEM_SHARED - the section is shared for various processes;
  • IMAGE_SCN_MEM_EXECUTE - the code in the section can be executed;
  • IMAGE_SCN_MEM_READ - the data in the section can be read;
  • IMAGE_SCN_MEM_WRITE - the data in the section can be modified.
property TPESection.IncludedInPacking: Boolean; read write;

Allows the specification of whether this section will be packed when the entire file is packed. Its value is True by default.

TPESections

This class is used to work with a list of PE sections of the file.

TPESections = class(TVMObjectList)
  function IndexOfName(const Name: String): Integer;
  property Items[Index: Integer]: TPESection; read; default;
end;
function TPESections.IndexOfName(const Name: String): Integer;

Returns the index of the section by its name. If there is no section named Name, it returns -1.

property TPESections.Items[Index: Integer]: TPESection; read; default;

Allows access to the section with the Index index.

TMapRecord

This class is used to work with an object in the program.

TObjectType = (otCode, otData, otExport, otMarker, otVBMarker,
  otAPIMarker, otImport, otString, otUnknown);

TMapRecord = class(TVMObject)
  property Address: Int64; read;
  property Name: String; read;
  property CodeType: TObjectType; read;
  property SegmentName: String; read;
end;
property TMapRecord.Address: Int64; read;

Determines the object address.

property TMapRecord.Name: String; read;

Determines the object name.

property TMapRecord.CodeType: TObjectType; read;

Determines the object type:

  • otCode - the object is a function/procedure;
  • otData - the object is data;
  • otExport - the object is an exported function;
  • otMarker, otVBMarker, otAPIMarker - the object is a marker;
  • otImport - the object is an imported function;
  • otString - the object is a string constant.
property TMapRecord.SegmentName: String; read;

Gets the name of segment, containg the address of the object.

TMapRecords

This class is used to work with a list of objects in the program. It is a list of objects from the list of names available in the MAP file, the list of imported/exported functions, the list of found markers.

TMapRecords = class(TVMObjectList)
  function IndexOfAddress(Address: Int64): Integer;
  function IndexOfName(const Name: String): Integer;
  property Items[Index: Integer]: TMapRecord; read; default;
end;
function TMapRecords.IndexOfAddress(Address: Int64): Integer;

Returns the index of the object with the Address address. If the object with the Address address is not found, it returns -1.

function TMapRecords.IndexOfName(const Name: String): Integer;

Returns the index of the object with the Name name. If the object with the Name name is not found, it returns -1.

property TMapRecords.Items[Index: Integer]: TMapRecord; read; default;

Allows access to the program object with the Index index.

TPEDirectory

This class is used to work with the PE directory of the file.

TPEDirectory = class(TVMObject)
  function IsEmpty: Boolean;
  procedure Clear;
  property Name: String; read;
  property RVA: Longint; read;
  property Size: Longint; read;
end;
function TPEDirectory.IsEmpty: Boolean

If RVA or Size equal 0, it returns True. Otherwise it returns False.

procedure TPEDirectory.Clear;

Cleans RVA and Size.

property TPEDirectory.Name: String; read;

Determines the directory name.

property TPEDirectory.RVA: Longint; read;

Determines the relative address of the directory.

property TPEDirectory.Size: Longint; read;

Determines the directory size.

TPEDirectories

This class is used to work with PE directories of the file.

TPEDirectoryType = (dtExportTable, dtImportTable, dtResourceTable,
  dtExceptionTable, dtSecurityTable, dtFixupTable, dtDebugTable,
  dtImageDescription, dtMachineSpecific, dtThreadLocalStorage,
  dtLoadConfig, dtBoundImport, dtIAT, dtDelayImportDescription,
  dtCOMRuntimeHeader);

TPEDirectories = class(TVMObject)
  property Items[Index: TPEDirectoryType]: TPEDirectory; read; default;
end;
property Items[Index: TPEDirectoryType]: TPEDirectory; read; default;

Allows access to the directory with the Index index.

TPEExportRecord

The class is used to work with the description of an exported function.

TPEExportRecord = class(TVMObject)
  property Address: Int64; read;
  property Ordinal: Word; read;
  property Name: String; read;
  property ForwardName: String; read;
end;
property TPEExportRecord.Address: Int64; read;

Determines the address of the exported function.

property TPEExportRecord.Ordinal: Word; read;

Determines the ordinal of the exported function.

property TPEExportRecord.Name: String; read;

Determines the name of the exported function.

property TPEExportRecord.ForwardName: String; read;

Determines the name of the exported function located in another library.

TPEExportRecords

This class is used to work with a list of exported functions.

TPEExportRecords = class(TVMObjectList)
  procedure Add(Address: Int64; Ordinal: Word; const Name: String);
  function IndexOfName(const Name: String): Integer;
  function IndexOfOrdinal(const Ordinal: Word): Integer;
  property DLLName: String; read write;
  property Items[Index: Integer]: TPEExportRecord; read; default;
end;
procedure TPEExportRecords.Add(Address: Int64; Ordinal: Word; const Name: String);

Adds a new exported function with the Address address, the Ordinal ordinal and the Name name.

function TPEExportRecords.IndexOfName(const Name: String): Integer;

Returns the index of the exported function by its name. If there is no exported function named Name, it returns -1.

function TPEExportRecords.IndexOfOrdinal(const Ordinal: Word): Integer;

Returns the index of the exported function by its ordinal. If there is no exported function with the Ordinal ordinal, it returns -1.

property TPEExportRecords.DLLName: String; read write;

Allows access to the DLL name.

property TPEExportRecords.Items[Index: Integer]: TPEExportRecord; read; default;

Allows access to the exported function with the Index index.

TPEResourceRecord

TResourceType = (rtUnknown, rtCursor, rtBitmap, rtIcon, rtMenu,
rtDialog, rtStringTable, rtFontDir, rtFont, rtAccelerators,
  rtRCData, rtMessageTable, rtGroupCursor, rtGroupIcon, rtVersion,
  rtDlgInclude, rtPlugPlay, rtVXD, rtAniCursor, rtAniIcon,
  rtManifest);

TPEResourceRecord = class(TVMObjectList)
  function IndexOfName(const Name: String): Integer;
  property Name: String; read;
  property ResType: TResourceType; read;
  property Items[Index: Integer]: TPEResourceRecord; read; default;
end;

TPEResourceRecords

TPEResourceRecords = class(TPEResourceRecord)
  function IndexOfResType(ResType: TResourceType): Integer;
end;

TPEFile

This class is used to work with a PE file.

TPEFile = class(TVMObject)
  function Read(var Buffer: AnsiString; Count: Longint): Longint;
  function Write(const Buffer: AnsiString; Count: Longint): Longint;
  property ImageBase: Int64; read;
  property MapRecords: TMapRecords; read;
  property Directories: TPEDirectories; read;
  property Sections: TPESections; read;
  property ExportRecords: TPEExportRecords; read;
  property ResourceRecords: TPEResourceRecords; read;
  property Address: Int64; read write;
  property Position: Longint; read write;
  property Size: Longint; read;
  property Handle: Integer; read;
  property EntryPoint: Int64; read write;
end;
function TPEFile.Read(var Buffer: AnsiString; Count: Longint): Longint;

Reads bytes from the Count stream to the Buffer buffer. Returns the number of copied bytes.

function TPEFile.Write(const Buffer: AnsiString; Count: Longint): Longint;

Writes bytes from the Buffer buffer to the Count stream. Returns the number of copied bytes.

property TPEFile.ImageBase: Int64; read;

Determines the initial address for loading the file.

property TPEFile.MapRecords: TMapRecords; read;

Allows access to the basic address that will be used to load the file.

property TPEFile.Directories: TPEDirectories; read;

Allows access to the file directories.

property TPEFile.Sections: TPESections; read;

Allows access to the file sections.

property TPEFile.ExportRecords: TPEExportRecords; read;

Allows access to the list of exported functions.

property TPEFile.ResourceRecords: TPEResourceRecords; read;

Allows access to the file resources.

property TPEFile.Address: Int64; read write;

Determines the current address in the file.

property TPEFile.Position: Longint; read write;

Determines the current position in the file.

property TPEFile.Size: Longint; read;

Determines the physical size of the file.

property TPEFile.Handle: Integer; read;

Determines the physical size of the file.

property TPEFile.EntryPoint: Int64; read write;

Determines the address of the entry point in the file.

TExtAddresses

This class is used to work with a list of addresses.

TExtAddresses = class(TVMObjectList)
  procedure Add(Address: Int64);
  function IndexOf(Address: Int64): Integer;
  property Items[Index: Integer]: Int64; read; default;
end;
procedure TExtAddresses.Add(Address: Int64);

Adds a new address to the list.

function TExtAddresses.IndexOf(Address: Int64): Integer;

Returns the index of the address by its value. If the address with the Address value is not found, it returns -1.

property TExtAddresses.Items[Index: Integer]: Int64; read; default;

Allows access to the address with the Index index.

TIntelRecord

This class is used to work with a CPU instruction.

TCommandType = (cmUnknown, cmPush, cmPop, cmMov, cmAdd, cmXor,
  cmTest, cmLea, cmRetF, cmRet, cmSsh, cmCall, cmJmp, cmFstsw,
  cmFsqrt, cmFchs, cmFstcw, cmFldcw, cmFild, cmFist, cmFistp,
  cmFld, cmFstp, cmFst, cmFadd, cmFsub, cmFsubr, cmFisub,
  cmFisubr, cmFdiv, cmFcomp, cmFmul, cmRepe, cmRepne, cmRep, cmDB,
  cmDD, cmDQ, cmMovs, cmCmps, cmScas, cmMovzx, cmMovsx, cmInc,
  cmDec, cmLes, cmLds, cmLfs, cmLgs, cmLss, cmXadd, cmBswap,
  cmJmpWithFlag, cmAnd, cmSub, cmStos, cmLods, cmNop, cmXchg,
  cmPushf, cmPopf, cmSahf, cmLahf, cmShl, cmShr, cmSal, cmSar,
  cmRcl, cmRcr, cmRol, cmRor, cmShld, cmShrd, cmLoope, cmLoopne,
  cmLoop, cmJCXZ, cmIn, cmIns, cmOut, cmOuts, cmWait, cmCbw,
  cmCwde, cmCdqe, cmCwd, cmCdq, cmCqo, cmClc, cmStc, cmCli,
  cmSti, cmCld, cmStd, cmNot, cmNeg, cmDiv, cmImul, cmIdiv,
  cmMul, cmOr, cmAdc, cmCmp, cmSbb, cmPusha, cmPopa, cmClflush,
  cmPause, cmBound, cmArpl, cmDaa, cmDas, cmAaa, cmAam, cmAad,
  cmAas, cmEnter, cmLeave, cmInt, cmInto, cmIret, cmSetCC,
  cmCmov, cmAddpd, cmAddps, cmAddsd, cmAddss, cmAndpd, cmAndps,
  cmAndnpd, cmAndnps, cmCmppd, cmCmpps, cmCmpsd, cmCmpss,
  cmComisd, cmComiss, cmCvtdq2ps, cmCvtpd2dq, cmCvtdq2pd,
  cmCvtpd2pi, cmCvtps2pi, cmCvtpd2ps, cmCvtps2pd, cmCvtpi2pd,
  cmCvtpi2ps, cmCvtps2dq, cmCvtsd2si, cmCvtss2si, cmCvtsd2ss,
  cmCvtss2sd, cmCvttpd2pi, cmCvttps2pi, cmCvttpd2dq, cmCvttps2dq,
  cmCvttsd2si, cmCvttss2si, cmDivpd, cmDivps, cmDivsd, cmDivss,
  cmMaxpd, cmMaxps, cmMaxsd, cmMaxss, cmMinpd, cmMinps, cmMinsd,
  cmMinss, cmMulpd, cmMulps, cmMulsd, cmMulss, cmOrpd, cmOrps,
  cmMovd, cmMovq, cmMovntq, cmMovapd, cmMovaps, cmMovdqa,
  cmMovdqu, cmMovdq2q, cmMovq2dq, cmMovhlps, cmMovhpd, cmMovhps,
  cmMovlhps, cmMovlpd, cmMovlps, cmMovmskpd, cmMovmskps, cmMovnti,
  cmMovntpd, cmMovntps, cmMovsd, cmMovss, cmMovupd, cmMovups,
  cmPmovmskb, cmPsadbw, cmPshufw, cmPshufd, cmPshuflw, cmPshufhw,
  cmPsubb, cmPsubw, cmPsubd, cmPsubq, cmPsubsb, cmPsubsw,
  cmPsubusb, cmPsubusw, cmPaddb, cmPaddw, cmPaddd, cmPaddq,
  cmPaddsb, cmPaddsw, cmPaddusb, cmPaddusw, cmPavgb, cmPavgw,
  cmPinstrw, cmPextrw, cmPmaxsw, cmPmaxub, cmPminsw, cmPminub,
  cmPmulhuw, cmPmulhw, cmPmullw, cmPmuludq, cmPsllw, cmPslld,
  cmPsllq, cmPslldq, cmPsraw, cmPsrad, cmPsrlw, cmPsrld, cmPsrlq,
  cmPsrldq, cmPunpcklbw, cmPunpcklwd, cmPunpckldq, cmPunpcklqdq,
  cmPunpckhqdq, cmPackusdw, cmPcmpgtb, cmPcmpgtw, cmPcmpgtd,
  cmPcmpeqb, cmPcmpeqw, cmPcmpeqd, cmEmms, cmPacksswb, cmPackuswb,
  cmPunpckhbw, cmPunpckhwd, cmPunpckhdq, cmPackssdw, cmPand,
  cmPandn, cmPor, cmPxor, cmPmaddwd, cmRcpps, cmRcpss, cmRqrtps,
  cmRqrtss, cmShufps, cmShufpd, cmSqrtpd, cmSqrtps, cmSqrtsd,
  cmSqrtss, cmSubpd, cmSubps, cmSubsd, cmSubss, cmUcomisd,
  cmUcomiss, cmUnpckhpd, cmUnpckhps, cmUnpcklpd, cmUnpcklps,
  cmXorpd, cmXorps, cmBt, cmBts, cmBtr, cmBtc, cmXlat, cmCpuid,
  cmRsm, cmBsf, cmBsr, cmCmpxchg, cmCmpxchg8b,
  cmHlt, cmCmc, cmLgdt, cmSgdt, cmLidt, cmSidt, cmSmsw, cmLmsw,
  cmInvlpg, cmLar, cmLsl, cmCtls, cmInvd, cmWbinvd, cmUd2,
  cmWrmsr, cmRdtsc, cmRdmsr, cmRdpmc, cmFcom, cmFdivr, cmFiadd,
  cmFimul, cmFicom, cmFicomp, cmFidiv, cmFidivr, cmFaddp, cmFmulp,
  cmFsubp, cmFsubrp, cmFdivp, cmFdivrp, cmFbld, cmFbstp, cmFfree,
  cmFrstor, cmFsave, cmFucom, cmFucomp, cmFldenv, cmFstenvm,
  cmFxch, cmFabs, cmFxam, cmFld1, cmFldl2t, cmFldl2e, cmFldpi,
  cmFldlg2, cmFldln2, cmFldz, cmFyl2x, cmFptan, cmFpatan,
  cmFxtract, cmFprem1, cmFdecstp, cmFincstp, cmFprem, cmFyl2xp1,
  cmFsincos, cmFrndint, cmFscale, cmFsin, cmFcos, cmFtst,
  cmFstenv, cmF2xm1, cmFnop, cmFinit, cmFclex, cmFcompp,
  cmSysenter, cmSysexit, cmSldt, cmStr, cmLldt, cmLtr, cmVerr,
  cmVerw, cmSfence, cmLfence, cmMfence, cmPrefetchnta, cmPrefetcht0,
  cmPrefetcht1, cmPrefetcht2, cmPrefetch, cmPrefetchw, cmFxrstor,
  cmFxsave, cmStmxcsr, cmFcmov, cmFucomi, cmFcomi, cmFucomip,
  cmFcomip, cmFucompp);

TOperandType = (otNone, otValue, otRegistr, otMemory, otSegmentRegistr,
  otControlRegistr, otDebugRegistr, otFloatValue, otHiPartRegistr, otBaseRegistr,
  otMMXRegistr, otXMMRegistr);

TOperandTypes = set of TOperandType;

TOperandSize = (osByte, osWord, osDWord, osQWord, osTByte, osOWord);

TOperand = record
  OperandSize: TOperandSize;
  OperandType: TOperandTypes;
  Registr: Byte;
  BaseRegistr: Byte;
  Scale: Byte;
  Value: Int64;
  ValueSize: TOperandSize;
  AddressSize: TOperandSize;
  FixupIndex: Integer;
end;

TIntelRecord = class(TVMObject)
  procedure ReadFromFile(File: TPEFile; var Address: Int64);
  property Address: Int64; read;
  property CommandText: String; read;
  property CommandType: TCommandType; read;
  property Dump[Index: Integer]: Byte; read;
  property DumpLength: Longint; read;
  property Operands[Index: Integer]: TOperand; read;
end;
procedure TIntelRecord.ReadFromFile(File: TPEFile; var Address: Int64);

Disassembles the instruction with the Address address.

property TIntelRecord.Address: Int64; read;

Determines the instruction address.

property TIntelRecord.CommandText: String; read;

Determines the instruction mnemonic.

property TIntelRecord.CommandType: TCommandType; read;

Determines the instruction type.

property TIntelRecord.Dump[Index: Integer]: Byte; read;

Allows access to the byte array determining the instruction representation.

property TIntelRecord.DumpLength: Longint; read;

Determines the size of the byte array.

property TIntelRecord.Operands[Index: Integer]: TOperand; read;

Gets an operand by the index.

TVMProcedure

This class is used to work with a code block (a list of CPU instructions).

TCompilationType = (ctVirtualization, ctMutation, ctUltra);

TCompilationOption = (coUseProjectOptions,coCheckCRC,coEncryptRegs,coEncryptValues,coUseKey);
TCompilationOptions = set of TCompilationOption;

TVMProcedure = class(TVMObjectList)
  function IndexOfAddress(Address: Int64): Integer;
  property Address: Int64; read;
  property BreakAddress: Int64; read write;
  property Name: String; read;
  property CodeType: TObjectType; read;
  property CompilationType: TCompilationType; read write;
  property CompilationOptions: TCompilationOptions; read write;
  property ExtAddresses: TExtAddresses; read;
  property IncludedInCompilation: Boolean; read write;
  property Records[Index: Integer]: TIntelRecord; read;
  property SELicense: Byte; read write;
  property SEVersion: Byte; read write;
  property InputSize: Longint; read;
  property OutputSize: Longint; read;
end;
function TVMProcedure.IndexOfAddress(Address: Int64): Integer;

Returns the index of the instruction with the Address address. If the instruction with the Address address is not found, it returns -1.

property TVMProcedure.Address: Int64; read;

Determines the initial address of the code block.

property TVMProcedure.BreakAddress: Int64; read write;

Determines the final address of the code block.

property TVMProcedure.Name: String; read;

Determines the name of the code block.

property TVMProcedure.CodeType: TObjectType; read;

Determines the type of the code block.

property TVMProcedure.CompilationType: TCompilationType; read write;

Determines the compilation type:

  • ctVirtualization - virtualization;
  • ctMutation - mutation;
  • ctUltra - ultra (mutation + virtualization).
property TVMProcedure.CompilationOptions: TCompilationOptions; read write;

Determines the compilation type:

  • coUseProjectOptions - use the project options;
  • coCheckCRC - control the integrity of VM objects;
  • coEncryptRegs - encrypt registers at VM output;
  • coEncryptValues - hide constants;
  • coUseKey - use the key (available only in SenseLock Edition).
property TVMProcedure.ExtAddresses: TExtAddresses; read;

Allows access to the list of external addresses.

property TVMProcedure.IncludedInCompilation: Boolean; read write;

Allows the specification whether the code block is included in the compilation.

property TVMProcedure.Records[Index: Integer]: TIntelRecord; read;

Allows access to the instruction with the Index index.

property TVMProcedure.SELicense: Byte; read write;

Allows determining the license number if the code is locked to the key (available only in SenseLock Edition).

property TVMProcedure.SEVersion: Byte; read write;

Allows determining the version number if the code is locked to the key (available only in SenseLock Edition).

property TVMProcedure.InputSize: Longint; read;

Returns the size of procedure before compilation (in bytes).

property TVMProcedure.OutputSize: Longint; read;

Returns the size of procedure after compilation (in bytes).

TWatermark

This class is used to work with a watermark.

TWatermark = class(TVMObject)
  property Enabled: Boolean; read write;
  property Name: String; read write;
  property Value: String; read write;
  property UseCount: Longint; read write;
end;
property TWatermark.Enabled: Boolean; read write;

Allows the specification of whether the watermark can be used in protected files. Its value is True by default.

property TWatermark.Name: String; read write;

Determines the watermark name.

property TWatermark.Value: String; read write;

Determines the watermark value.

property TWatermark.UseCount: Longint; read write;

Determines how many times the watermark has been used in protected files.

TWatermarks

This class is used to work with a list of watermarks.

TWatermarks = class(TVMObjectList)
  function Add(const Name, Value: String): TWatermark;
  function IndexOfName(const Name: string): Integer;
  function IndexOfValue(const Value: string): Integer;
  function CreateWatermarkValue: String;
  property Items[Index: Integer]: TWatermark; read; default;
end;
function TWatermarks.Add(const Name, Value: String): TWatermark;

Adds a new watermark named Name and with the Value value.

function TWatermarks.IndexOfName(const Name: string): Integer;

Returns the index of the watermark by its name. If there is no watermark named Name, it returns -1.

function TWatermarks.IndexOfValue(const Value: string): Integer;

Returns the index of the watermark by its value. If there is no watermark with the Value value, it returns -1.

function TWatermarks.CreateWatermarkValue: String;

Returns the unique value of the watermark.

property TWatermarks.Items[Index: Integer]: TWatermark; read; default;

Allows access to the watermark with the Index index.

TInternalDll

This class is used to work with bundled DLL file.

TInternalDll = class(TVMObject)
  property Name: String; read write;
  property FileName: String; read write;
  property LoadAtStart: Booleam; read write;
end;
property TInternalDll.Name: String; read write;

Defines a name of bundled DLL.

property TInternalDll.FileName: String; read write;

Defines a file name of bundled DLL.

property TInternalDll.LoadAtStart: Boolean; read write;

Defines whether the bundled DLL should be automatically loaded at application startup.

TInternalDlls

This class is used to work with the list of bundled DLLs.

TInternalDlls = class(TVMObjectList)
  function Add(const Name, FileName: String; LoadAtStart: Boolean): TInternalDll;
  property Items[Index: Integer]: TInternalDll; read; default;
end;
function TInternalDlls.Add(const Name, FileName: String; LoadAtStart: Boolean): TInternalDll;

Adds new bundled DLL with the name "Name".

property TInternalDlls.Items[Index: Integer]: TInternalDll; read; default;

Gets a bundled DLL by the index.

TLicense

This class is used to work with license.

TLicense = class(TVMObject)
  property Date: TDate; read write;
  property CustomerName: String; read write;
  property CustomerEmail: String; read write;
  property OrderRef: String; read write;
  property Comments: String; read write;
  property SerialNumber: String; read;
  property Blocked: Boolean; read write;
end;
property TLicense.Date: TDate; read write;

Defines a date of the given license.

property TLicense.CustomerName: String; read write;

Defines a license owner.

property TLicense.CustomerEmail: String; read write;

Defines an email of the license owner.

property TLicense.OrderRef: String; read write;

Finds the order for the given license.

property TLicense.Comments: String; read write;

Defines a comments for the given license.

property TLicense.SerialNumber: String; read;

Defines a serial number.

property TLicense.Blocked: Boolean; read write;

Checks whether the license is blocked.

TLicenseManager

This class is used to work with the list of licenses.

TLicenseManager = class(TVMObjectList)
  function ImportLicense(const SerialNumber: String): TLicense;
  procedure Save;
  property Items[Index: Integer]: TLicense; read; default;
end;
function TLicenseManager.ImportLicense(const SerialNumber: String): TLicense;

Imports license information from the given serial number. Returns nil if the serial number is invalid.

procedure TLicenseManager.Save;

Saves licensing parameters and licenses to a file.

property TLicenseManager.Items[Index: Integer]: TLicense; read; default;

Gets a license by Index.

TVMProtector

This is the main class of the VMProtect kernel.

TMessageType = (mtDebuggerFound, mtVMToolsFound, mtFileCorrupted, mtSerialNumberRequired);

TVMProtector = class(TVMObjectList)
  function AddByAddress(Address: Int64; CompilationType: TCompilationType;
    IncludedInCompilation: Boolean): TVMProcedure;
  function AddByName(const Name: String; CompilationType: TCompilationType;
    IncludedInCompilation: Boolean): TVMProcedure;
  function IndexOfAddress(Address: Int64): Integer;
  function IndexOfName(const Name: String): Integer;
  property InputFile: TPEFile; read;
  property InputFileName: String; read;
  property OutputFile: TPEFile; read;
  property OutputFileName: String; read write;
  property Procedures[Index: Integer]: TVMProcedure; read;
  property WatermarkValue: String; read write;
  property VMExecutorCount: Longint; read write;
  property VMSectionName: String; read write;
  property SEMasterPassword: String; read write;
  property SEUserPIN: String; read write;
  property SEKeyID: Int64; read write;
  property SELicense: Byte; read write;
  property SEVersion: Byte; read write;
  property InternalDlls: TInternalDlls; read;
end;
function TVMProtector.AddByAddress(Address: Int64; CompilationType: TCompilationType;
  IncludedInCompilation: Boolean): TVMProcedure;

Adds a new code block with the Address address, the CompilationType compilation type and the IncludedInCompilation property.

function TVMProtector.AddByName(const Name: String; CompilationType: TCompilationType;
  IncludedInCompilation: Boolean): TVMProcedure;

Adds a new code block with the Name name, the CompilationType compilation type and the IncludedInCompilation property.

function TVMProtector.IndexOfAddress(Address: Int64): Integer;

Returns the index of the code block by its address. If there is no code block with the Address address, it returns -1.

function TVMProtector.IndexOfName(const Name: String): Integer;

Returns the index of the code block by its name. If there is no code block named Name, it returns -1.

property TVMProtector.InputFile: TPEFile; read;

Allows access to the protected file.

property TVMProtector.InputFileName: String; read;

Allows access to the name of the protected file.

property TVMProtector.OutputFile: TPEFile; read;

Allows access to the output file.

property TVMProtector.OutputFileName: String; read write;

Allows access to the name of the output file.

property TVMProtector.Procedures[Index: Integer]: TVMProcedure; read;

Allows access to the code block with the Index index.

property TVMProtector.WatermarkValue: String; read write;

Allows the specification of the watermark value.

property TVMProtector.VMExecutorCount: Longint; read write;

Allows the specification of the number of virtual machines.

property TVMProtector.VMSectionName: String; read write;

Allows the specification of the names of VM sections.

property TVMProtector.SEMasterPassword: String; read write;

Allows the specification of the master key password (available only in SenseLock Edition).

property TVMProtector.SEUserPIN: String; read write;

Allows the specification of the user PIN (available only in SenseLock Edition).

property TVMProtector.SEKeyID: Int64; read write;

Allows the specification of the key identifier (available only in SenseLock Edition).

property TVMProtector.SELicense: Byte; read write;

Allows the specification of the license number if common licensing options are used for protected blocks of the code (available only in SenseLock Edition).

property TVMProtector.SEVersion: Byte; read write;

Allows the specification of the version number if common licensing options are used for protected blocks of the code (available only in SenseLock Edition).

property TVMProtector.InternalDlls:TInternalDlls; read;

Gets a list of bundled DLL (Ultimate version only).

property TVMProtector.Messages[Index: TMessageType]: String; read write;

Allows to read and modify the messages, shown at:

  • mtDebuggerFound - the detection of debugger;
  • mtVMToolsFound - the detection of virtualization tools;
  • mtFileCorrupted - the modification of the executable;
  • mtSerialNumberRequired - the lack of serial number.
property TVMProtector.LicenseManager:TLicenseManager; read;

Gets a license manager (available only in Ultimate).


Built-in functions

Besides methods and properties of classes available in the scripting language, the user of VMProtect can use functions making it possible to perform some basic operations. Among these functions there are both system functions allowing work with strings, dates and numbers, use of the command-line version of the program, display of various messages, and specialized functions allowing work with the VMProtect kernel and watermarks:

  • VMProtector
  • Watermarks
  • Writeln
  • ParamCount
  • ParamStr
  • ChangeFileExt
  • Random
  • MsgBox
  • CompareText
  • AnsiCompareText
  • Format
  • Now
  • Time
  • DateTimeToStr
  • TimeToStr
function VMProtector: TVMProtector;

Returns the instance of the TVMProtector class, which is the main object of the VMProtect kernel.

function Watermarks: TWatermarks;

Returns the instance of the TWatermarks class, which is the object for working with watermarks.

procedure Writeln(const S: String);

Writeln displays a text message in the script message window.

function ParamCount: Integer;

Returns the number of parameters passed to the program in the command line. The parameters are separated by spaces or tab characters. Double quotation marks put separate words together in one parameter (for example, long file names with spaces).

function ParamStr(Index: Integer): String;

Returns the parameter of the command line corresponding to the parameter with the Index number or an empty string if Index is larger than ParamCount. For example, if Index equals 2, the result of the function will be the second parameter in the command line. ParamStr(0) returns the path and name of the running program file (for example, C:\TEST\MYPROG.EXE).

function ChangeFileExt(const FileName, Extension: String): String;

Used to change the extension of the file passed in the FileName parameter for the extension passed in the Extension parameter. The ChangeFileExt function does not rename the source file, but only returns a string with the new file name.

function Random(Range: Integer): Integer;

Returns a random number within the following range: 0 <= X < Range.

function MsgBox(const Text, Caption: String; Flags: Integer): Integer;

Used to display a message dialog box with one or several buttons.

  • Text - message text, no 255-character length limitation. Long messages are automatically arranged in the dialog box.
  • Caption - dialog box title. The title may be longer than 255 characters, but is not arranged. A long title results in a wide message dialog box.
  • Flags - a set of bits determining the content and behavior of the dialog box. This parameter can be a combination of flags from the following groups:

    Dialog box button flag:
    MB_ABORTRETRYIGNORE | The dialog box has three buttons: Abort, Retry and Ignore.
    MB_OK | The dialog box has one button: OK. This is the default flag.
    MB_OKCANCEL | The dialog box has two buttons: OK and Cancel.
    MB_RETRYCANCEL | The dialog box has two buttons: Retry and Cancel.
    MB_YESNO | The dialog box has two buttons: Yes and No.
    MB_YESNOCANCEL | The dialog box has three buttons: Yes, No and Cancel.

    Dialog box icon flag:
    MB_ICONEXCLAMATION | The dialog box shows the exclamation mark icon.
    MB_ICONINFORMATION | The dialog box shows the "i" letter icon.
    MB_ICONQUESTION | The dialog box shows the question mark icon.
    MB_ICONHAND | The dialog box shows the Stop sign icon.

    Default button flags:
    MB_DEFBUTTON1 | The first button is the default one. MB_DEFBUTTON1 is the default value unlike MB_DEFBUTTON2, MB_DEFBUTTON3 or MB_DEFBUTTON4 given below.
    MB_DEFBUTTON2 | The second button is the default one.
    MB_DEFBUTTON3 | The third button is the default one.
    MB_DEFBUTTON4 | The forth button is the default one.

  • Return value
    0 | Not enough memory to create a dialog box.
    IDOK | The user clicked the OK button.
    IDCANCEL | The user clicked the Cancel button.
    IDABORT | The user clicked the Abort button.
    IDRETRY | The user clicked the Retry button.
    IDIGNORE | The user clicked the Ignore button.
    IDYES | The user clicked the Yes button.
    IDNO | The user clicked the Yes button.
function CompareText(const S1, S2: String): Integer;

Compares S1 and S2 and returns 0 if they are equal. If S1 is greater than S2, the function returns an integer larger than 0. If S1 is less than S2, CompareText returns an integer less than 0. The CompareText function is case-insensitive and its result does not depend on the current Windows locale.

function AnsiCompareText(const S1, S2: String): Integer;

Compares S1 and S2, case-insensitive. The result of the comparison operation depends on the current Windows locale. AnsiCompareText returns a value less than 0 if S1 < S2, a value greater than 0 if S1 > S2 and 0 if S1 = S2.

function Format(const Format: String; const Args: array of const): String;

Allows conversion of several items in an open array of arguments according to the instructions of the formatting string. Constants and/or variables of the integer or real-valued type, strings and pointers can be items in the array of arguments.

Format - the formatting string which is any string where you can insert the formatting specifier. The number of formatting specifiers must not be greater than the number of items in the Args array of arguments. During the operation of the function, every item from the array of arguments is put into the formatting string according to the sequence of formatting specifiers: the first specifier for the first argument, the second specifier for the second argument and so on. If the number of specifiers is less than the number of arguments, 'extra' arguments are ignored.

A formatting specifier always starts with the percent character and generally has the following structure (optional elements are enclosed in square brackets):

"%" [index ":"] ["-"] [width] ["." prec] type
  • index ":" - the index of the open array, this parameter can be used to explicitly specify the argument the specifier will process;
  • "-" defines if it is necessary to align the text formatted by the specifier to the left border of the area allocated for it;
  • width - the number defining the number of characters in the text processed by the specifier; if this number is less than the required number, this specifier parameter is ignored. If it is larger, the necessary number of spaces is added to the right side (if the "-" element is specified) or to the left side (if it is absent);
  • "." prec - defines the precision of integer and real-valued types;
  • type - the character defining the formatting type:
    d | Whole decimal number. If the prec parameter is specified, its symbolic representation must contain at least prec digits after the point: if the symbolic representation has fewer digits, the 0 characters are added to the left of it, if it has more digits, the prec parameter is ignored. If the argument is not a whole number, an exception occurs.
    u | Unsigned integer. It is used similarly to the d type, but the formatting result will be an unsigned integer.
    e | Real-valued number in the exponential representation. The symbolic representation looks like -d, dddddddddddE+ddd. If the number is positive, the leading minus is dropped. If its modulus is less than 1, the plus sign is changed to minus. It always contains one digit in the integral part, at least one digit in the fractional part and not less than three digits of the decimal exponent. If the precision parameter is specified, it defines the total number of digits before the ? character, but not less than two: if the precision parameter equals to 1 or 0, it is changed for 2. If the symbolic representation contains more characters than prec, it is approximated according to the first dropped digit. The default value of the precision parameter is 15.
    f | Real-value number in the form of -ddd, ddd (fixed representation). The precision parameter (2 by default) defines the number of digits in the fractional part. If it equals 0, only the intergral part of the number is displayed.
    g | Real-valued number in the shortest possible representation (exponential or fixed). The precision parameter (15 by default) defines the largest possible number of significant positions. If the number has fewer digits, it is not completed to prec and if the number has no decimal part, it is displayed as an integer (without a decimal point).
    n | Corresponds to the fixed representation, but uses the digit grouping symbol.
    m | Real-valued number in the money representation. Similar to the n type, but the currency sign is added to the right.
    p | Displays the content of the pointer in the form of 8 hexadecimal digits.
    s | The argument must be a character, a string or a string with a trailing zero. If the precision parameter is specified, it defines the longest possible string: if the string is longer, it is trimmed. If it is shorter, the precision parameter is ignored.
    x | Integer in the hexadecimal representation. The precision parameter determines the fewest possible number of hexadecimal digits (if the number is smaller, leading zeros are added to it).

The function is insensitive to the case of formatting characters. The index, prec and width parameters are specified explicitly (by numbers in the formatting string) or implicitly (with the "*" character). In this case, the value of the next argument in the list of arguments (it must be an integer) is taken as a parameter.

function Now: TDateTime;

Returns the current date and time.

function Time: TDateTime;

Returns the current time.

function DateTimeToStr(DateTime: TDateTime): String;

Converts the DateTime value into a formatted data and time string.

function TimeToStr(Time: TDateTime ): String;

Converts the Time value into a formatted time string.


Calling functions from external DLLs

The built-in scripting language of VMProtect can be used to call functions from external DLLs. It is possible to call both standard Win32 API functions from standard Windows DLLs and custom functions from custom libraries. Before calling a function from a DLL, its prototype should be described in the body of the script. There are two ways to describe it - their examples are shown below.

Example: it is possible to describe the following function:

function A(B: Integer): Integer;

in two different forms:

function A(B: Integer): Integer; external '<dll_function_name>@<dll_file_name>';

This form shows that the function from the DLL should be called with the default call type ('stdcall'). All standard Win32 API functions use 'stdcall'. The same call type is very often used by many functions from custom DLLs.

function A(B: Integer): Integer; external '<dll_function_name>@<dll_file_name> <call_type>';

This form shows that the library function should be called with a special type of call. Possible call types: 'stdcall' (by default), 'cdecl', 'pascal' and 'register'.


Events

The built-in scripting language is an effective tool for automatically creating protected applications with VMProtect. Procedures and functions necessary to create the protected file in various steps are called through events processed by the VMProtect kernel. While creating scripts, custom event handlers can be set for 5 events that the protector kernel calls in the following order:

  • OnBeforeCompilation
  • OnBeforeSaveFile
  • OnBeforePackFile
  • OnAfterSaveFile
  • OnAfterCompilation
>procedure OnBeforeCompilation;

This event is triggered when the list of protected objects is being created. You can use the OnBeforeCompilation handler to add new procedure to the project and also edit or remove those already existing.

procedure OnBeforeSaveFile;

This event is triggered before all objects created as a result of compilation are saved to the output file. You can use the OnBeforeSaveFile event handler to edit both the file itself and some of its properties (for example, the list of resources, list of exported functions, section names, etc) that will be later automatically written to the output file.

procedure OnBeforePackFile;

This event is triggered before the protected file is packed. You can use OnBeforePackFile to make changes in the file that will be packed. This event is triggered only if the "Pack output file" option is used.

procedure OnAfterSaveFile;

This event is triggered after all objects created as a result of compilation are saved to the output file. This event handler can be used to add new data to the output file or change the data already saved to it.

procedure OnAfterCompilation;

This event is triggered after all objects included in the project are compiled. In this step the compiled file can be accessed and anything necessary done with it (for example, an electronic signature can be added (certificate)).

While writing handlers, custom procedures and functions can be created. Events names are displayed in bold in the script editor.


Examples

  • Adding all markers to the project automatically
  • Changing section names
  • Deleting service resources (for programs in Delphi and Borland C Builder)
  • Deleting compid (for programs in Visual C)
  • The calculation of data size before and after compilation for each object

Adding all markers to the project automatically

procedure OnBeforeCompilation;
var I:Integer;
begin
  with VMProtector do
   for I:=0 to InputFile.MapRecords.Count-1 do
    with InputFile.MapRecords.Items[I] do
     if (CodeType=otMarker) or (CodeType=otAPIMarker) or (CodeType=otVBMarker) then
      begin
       AddByAddress(Address,ctVirtualization,True);
      end;
end;

Changing section names

function GetRandomSectionName: String;
var I:Integer;
    B:Byte;
begin
  Result:='';
  for I:=1 to 8 do
   begin
    B:=32+Random(Ord('z')-32);
    Result:=Result+Chr(B);
   end;
end;

procedure OnAfterSaveFile;
var I:Integer;
begin
  with VMProtector.OutputFile do
   for I:=0 to Sections.Count-1 do
    Sections.Items[I].Name:=GetRandomSectionName;
end;

Deleting service resources (for programs in Delphi and Borland C Builder)

procedure OnBeforeSaveFile;
var I,J:Integer;
    RCDataRecord:TPEResourceRecord;
    ResourceNames:TStringList;
begin
  with VMProtector.OutputFile do
   begin
    I:=ResourceRecords.IndexOfResType(rtRCData);
    if I>-1 then
     begin
      RCDataRecord:=ResourceRecords[I];
      ResourceNames:=TStringList.Create;
      try
       ResourceNames.Add('PACKAGEINFO');
       ResourceNames.Add('DVCLAL');
       for J:=0 to ResourceNames.Count-1 do
        begin
         I:=RCDataRecord.IndexOfName(ResourceNames[J]);
         if I>-1 then
          begin
           RCDataRecord.Delete(I);
           Writeln(Format('Resource "%s" is deleted',[ResourceNames[J]]));
          end;
        end;
      finally
       ResourceNames.Free;
      end;
     end;
   end;
end;

Deleting compid (for programs in Visual C)

procedure OnBeforeSaveFile;
var I:Integer;
    S:AnsiString;
begin
  S:='';
  for I:=1 to 64 do
   S:=S+Chr(0);
  with VMProtector.OutputFile do
   begin
    Position:=$80;
    Write(S,Length(S));
   end;
end;

The calculation of data size before and after compilation for each object

procedure OnAfterCompilation;
var M,N,I:Integer;
    P:Double;
begin
  with VMProtector do
   for I:=0 to Count-1 do
    with Procedures[I] do
     if IncludedInCompilation then
      begin
       M:=InputSize;
       N:=OutputSize;
       if M=0 then
        P:=1
       else
        P:=N/M;
       Writeln(Format('%s -> inputsize: %d bytes, outputsize: %d bytes (%d%%)',[Name,M,N,Round(P*100)]));
      end;
end;


Contact Information

Contact us with all questions regarding use or purchase of the program:
www: http://www.vmpsoft.com
e-mail: info@vmpsoft.com
icq uin: 65107137

News | Buy | Forum