Get Demo
  • Windows
  • MacOS
  • Linux

Step 2.5: Locking the code to a serial number

One of the most common ways to crack programs is to locate the place where the serial number is checked and the nearby conditional jump that follows it. If the serial number is correct, the program follows one execution path; if not, it follows another. A hacker locates this jump and replaces it with a jump to the “correct” path. Let’s “crack” our test program using this technique directly in the source code. Let’s “switch off” our conditional jump:

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

Now the program accepts any serial number and works normally. Of course, if the file is protected with VMProtect, even an experienced hacker would spend months locating and modifying the conditional jump as we did here. And considering that the program checks the serial number multiple times and under different conditions, even such a simple check becomes quite robust. But let’s go further.

Locking the code to a serial number

The VMProtect licensing system allows you to lock the code of one or more functions to a serial number so that they will not work without a valid serial number. The body of the function is virtualized, then encrypted, and can only be decrypted with the correct serial number. This means that even if a hacker finds and fixes the conditional jump in the serial number check, functions locked to the serial number will still not work. Let’s try this. In the “Functions” section, choose the foo() function and in the right panel set the “Lock to Serial Number” option to “Yes”.

Then protect the application. Since we have already “hacked” it, put an arbitrary text into the serial.txt file and run the application. The following text appears in the console:

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

This means the hacker “fixed” the conditional jump and the program follows the “correct” path. But when foo() is invoked, the program displays a message:

Since we locked the foo() function to the serial number and the hacker does not have a valid one, an attempt to decrypt the function code results in failure and the program cannot continue execution. When “OK” is pressed, the program shuts down and the “done” message is never displayed in the console.

What should be locked to a serial number?

It makes sense to lock to a serial number any function that should only run in the registered version of the program. Since locking requires virtualization, you should consider the performance cost. For instance, if a text editor does not allow saving in the demo version, you can lock the save-document function to a serial number. If that function calls other functions, it is not necessary to lock them as well, since they are not useful without the main function.

You should also remember that invoking a locked function without a valid serial number leads to application shutdown, with no opportunity to save the user’s work. That is why you should thoroughly test the application to ensure it does not call such functions in trial mode. In the example above, the text editor must disable the “Save” command in demo mode and also ignore the Ctrl+S shortcut. It should also not prompt to save the document on exit. Otherwise, users may perceive it as a “buggy” demo version.

Locking to a serial number and invalid serial numbers

When the VMProtectSetSerialNumber() function is invoked, the licensing module checks the serial number passed to it. Encrypted code fragments are executed only if the serial number is fully valid at the time of the check — not blacklisted, not expired, and matching the correct hardware identifier, and so on. In this case, all encrypted procedures are executed until the application is closed or VMProtectSetSerialNumber() is called again.

Some limitations may “trigger” during program execution: for example, the operation time may expire or the serial number expiration date may be reached. In such cases, the licensing module still continues executing functions locked to the serial number. This is because it is difficult for the application to reliably detect the exact moment such limitations occur and adjust behavior accordingly (for example, disabling menu items). If the licensing module suddenly stopped executing locked code fragments, it would likely cause application malfunction. Therefore, the decision is made when the serial number is set, and the corresponding execution mode is selected.

Last updated 11 days ago