Let us take a look at a very simple application consisting of a single form (Form1), a text input field (Edit1), and a button (Button1). The application works as follows: when Button1 is clicked, the application checks whether the entered password is correct and displays the corresponding message.

The password is verified using a very simple algorithm: first, it is converted into a numeric value, then the remainder of division by 17 is calculated. The password is considered correct if the remainder of dividing the numeric representation of the entered password by 17 equals 13. The implementation of the password verification procedure in Delphi looks as follows:
function TForm1.CheckPassword: Boolean;
begin
Result:=(StrToIntDef(Edit1.Text, 0) mod 17=13);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if CheckPassword then
MessageDlg('Correct password', mtInformation, [mbOK], 0)
else
begin
MessageDlg('Incorrect password', mtError, [mbOK], 0);
Edit1.SetFocus;
end;
end;
Procedures and functions to protect can be selected in three different ways:
- Using a PDB/MAP file generated by the compiler together with the executable file of the application. The MAP file contains all necessary information about the names and addresses of all procedures and functions in the application. When a MAP file is used, procedures and functions can be selected for protection by name. In addition, whenever the project is recompiled, VMProtect automatically determines the new addresses of procedures and functions.
- Using markers inserted directly into the application source code. Markers are special labels used by VMProtect to determine the boundaries of protected code fragments. VMProtect also supports markers with predefined compilation types. Using markers is useful when you want to protect only a specific part of a function or procedure. Markers also allow you to define code regions where protected string constants will later be placed.
- By specifying the addresses of protected procedures directly in the executable file. Compared to the previous two methods, this approach is less convenient. Every time the application is modified and recompiled, all addresses must be specified again. This type of protection is mainly recommended for applications where the source code is unavailable.
Using a MAP file to define the boundaries of protected code provides another important advantage that deserves closer attention. Almost any procedure or function that contains local variables or uses the stack to save registers and/or intermediate calculation results includes a so-called prologue and epilogue located respectively at the beginning and the end of the compiled procedure or function:
push ebp \
mov ebp, esp \ prologue
push 00 /
push ebx /
...
pop ebx \
pop ecx \ epilogue
pop ebp /
ret /
Due to the way modern compilers work, code markers never include the prologue and epilogue of a function, even if the entire body of the CheckPassword function between begin and end is enclosed within markers. In this case, it would be enough for a hacker to modify the function prologue so that the virtualized code is never executed. For the CheckPassword function, this can be done as follows:
mov eax, 1
ret
!Important
If a PDB/MAP file is used to select code fragments for virtualization, the function prologue and epilogue are virtualized as well, significantly improving the tamper resistance of the protected application. Furthermore, if one virtualized function calls another virtualized function, control is transferred between them without actually jumping to the address of the called function. In this case, the call becomes a simple jump to another address within the bytecode of the virtual machine interpreter. This further strengthens application protection because any modifications made by a hacker to the function entry point become ineffective. When working with virtualized functions, control is transferred to the entry point of a virtualized function only when it is called from an unprotected or mutated code fragment.