Click here to Skip to main content
15,886,664 members
Articles / Programming Languages / C++
Article

A simple software key useful to protect software components

Rate me:
Please Sign up or sign in to vote.
4.92/5 (59 votes)
29 Nov 200418 min read 360.9K   9.3K   306   62
This article shows a way to implement a base software key that could be useful for protecting software components.

Introduction

In this article, we’ll show a way to implement a simple software key that could be useful for protecting software components (e.g., EXE, DLL, COM, etc.) against misuse and for keeping track of installations.

We want to highlight that this is just an example. We are aware that it is quite simple by-passing the protection offered by the software key presented in this article.

However, we think that this article could be useful in allowing us to better understand the mechanisms underlain a software key.

By the way, in the following sections, we'll mention how to by-pass this protection and some ideas that could be useful to make attackers work harder.

Background

In order to explain the background idea, we need to describe the context in which our software key could be useful.

Let’s suppose we have a software product encapsulated in an executable (EXE) with a policy fee based on installations. Note that the following notes are suitable for other types of components as well (e.g., DLL, COM, etc.). Let’s assume we would like to control how many installations our customers do. The software key we propose in this article, with the mentioned limitations, allows us to get the above intention. In particular, it allows us to grant to the customer the right to use our component (EXE) only on a certain machine. If he tries to install our executable on a different machine, the component won’t run.

How it works

Let's suppose we are the vendor of a certain component “protected_comp.exe”. In order to use our component, the customer needs to generate a “machine code” using the tool “softwarekey_customertool.exe” we provide to him. Then he needs to send this “machine code” to us. This “machine code” can be viewed as a signature of the machine in which the tool “softwarekey_customertool.exe” has been run and in which the “protected_comp.exe” will be executed.

Substantially, this signature is obtained using the MAC (Medium Access Control) address (i.e., usually an Ethernet address) as a seed. We can view the machine code generation as the application of a function f to the MAC address:

MachineCode = f (MACaddress)

Note that this process can be quite easily encapsulated in a web page, for instance, making the console application “softwarekey_customertool.exe” an Active X.

Using this “machine code”, we are able to create a “software key” (also known as “license number”) using the tool “softwarekey_vendortool.exe”. This tool, getting as input the “machine code”, will produce a “software key” (i.e., a “licence number”) that will work only for the machine on which the “machine code” has been generated. We can view the software key generation as the application of a function g to the machine code:

SoftwareKey = g (MachineCode)

Note: In the code provided with this article, functions f and g are just simple permutations of the MAC address. In order to improve protection, more complex algorithms should be used. Moreover, it is possible to use other data than the MAC address as a seed (F.Y.I., CPU identifier, Windows serial number, etc.), but our choice seems to be easier and quite general. We will introduce some possible techniques in the following sections.

After generating the “software key”, we need to send it to the customer. On receiving the “software key”, the customer has to install it in the file “protected_comp.ini”. At this point, the customer can use our component “protected_comp.exe”. For simplicity, we save the software key in an “ini” file. It is possible to save it in the registry. Furthermore, it is possible to create a tool that automates the software key installation.

The following picture tries to illustrate the steps needed to produce and install a software key valid for a certain machine A.

Figure 1 - Flow diagram for our software key

Inside the code

Core class

In this section, we give a brief description of the class CSoftwareKey whose methods will be used in “protected_comp.exe”, “softwarekey_vendortool.exe” and “softwarekey_customertool.exe”.

class CSoftwareKey
{
public:

    static RETVALUE RetrieveMACAddress(
            BYTE            pMACaddress[MAC_DIM]);

    static RETVALUE ComputeMachineCode(
            const BYTE      pMACaddress[MAC_DIM], 
            BYTE            pMachineCode[MACHINE_CODE_DIM]); 

    static RETVALUE ComputeSoftwareKey(
            const BYTE      pMachineCode[MACHINE_CODE_DIM], 
            BYTE            pSoftwareKey[SOFTWAREKEY_DIM]); 

    static RETVALUE VerifySoftwareKey(
            const char*      pSoftwareKeyString, 
            bool*            pIsValid);

    static RETVALUE GetSoftwareKeyStringFromIniFile(
            const char*       pFilePath, 
            char**            pSoftwareKeyString);

    static RETVALUE Buffer2String(
            const BYTE*        pBuffer, 
            const unsigned int pBufferSize, 
            char**             pString);

    static RETVALUE String2Buffer(
            const char*        pString,
            BYTE**             pBuffer, 
            unsigned int*      pBufferSize);
};

The method RetrieveMACAddress retrieves the MAC. For doing that, if the operating system is Windows 2000, ME or XP, we have used the API UuidCreateSequential; otherwise, if the operating system is NT, we have used CoCreateGuid. This is due to the fact that, in Windows XP/2000, the UuidCreate function internally called by CoCreateGuid, generates for security reasons an UUID that cannot be traced to the Ethernet/token ring address of the computer on which it was generated. However, as MSDN library states, in Windows XP/2000, the API UuidCreateSequential returns a UUID that is a function of the MAC. We could have used UuidCreateSequential for all operating systems, but unfortunately, it is not always present in Windows NT, depending on the service pack that has been installed.

Note: in the code related to NT, we didn't check the service pack version installed. This implies that this function doesn't work properly in Windows NT, if a certain service pack has been installed.

Another problem arises on computers without any network card. APIs UuidCreate and UuidCreateSequential will return a constant UUID. This means that the same machine code will be generated for two different machines both without a network card. A possibility to solve this problem, and in general to improve the protection offered by the software key, is to generate a machine code starting from a combination of values instead of starting from the MAC address only. This approach will be detailed later.

The method ComputeMachineCode generates the machine code starting from the MAC address. It is the implementation of the above introduced function f. The created machine code is just a simple permutation of the MAC address.

The method ComputeSoftwareKey generates the software key using the machine code as a seed. It is the implementation of the above introduced function g. The created software key is just a simple permutation of the machine code.

The method VerifySoftwareKey checks whether the software key installed in the “.ini” file is valid. It computes a software key starting from the MAC address, and checks it against the software key saved in the file “protected_comp.ini”.

The method GetSoftwareKeyStringFromIniFile retrieves the software key installed in the “.ini” file.

The method Buffer2String converts a buffer, i.e., a sequence of bytes, into a string. This is useful for producing a printable version of the machine code and the software key. In this way, they can be easily delivered, for instance, inside the text of an e-mail, without requiring the use of MIME. In our example, we translate each byte into a triplet of decimal numeric digits. For instance, the byte 0xCD will be translated in the numerical decimal digit triplet (i.e., a string) “205”.

The method String2Buffer converts a string in a sequence of bytes. Note that the function assumes that each byte is represented as a triplet of numerical decimal digits in the string. For instance, the numerical digit triplet “056” will be translated into the byte “0x38”.

Note: In the above methods Buffer2String and String2Buffer, we could have used the “base64” algorithm, but for simplicity, we have chosen the above-described method. Summarizing, the “base64” algorithm represents each three bytes of the input buffer as an output string of four printable characters of a given alphabet. In this way, the dimension of the resulting string is smaller than the string produced by our algorithm. In fact, our algorithm generates nine characters (decimal digit characters) for each three bytes. A possible improvement that would allow us to generate a smaller output string, is to convert each byte into a couple of hexadecimal digit characters. In that case, the byte 0xCD will be translated in "CD". Therefore, for each three bytes in the input stream, we could obtain six hexadecimal characters. However, since our machine code and software key are very short and this is just an example, we won't use these more powerful algorithms (although it should be quite simple to encapsulate them in the above two methods).

In the following sections, we give a brief description of the way in which the above class will be used in the executables presented in this article.

Protected component - “protected_comp.exe”

It is a simulation of the software component to be protected. It assumes that the software key, obtained by the vendor, is installed in the file “protected_comp.ini”. This simple console application will show a message box indicating if a valid software key has been installed or not.

This component uses the following methods of CSoftwareKey:

  • GetSoftwareKeyStringFromIniFile;
  • VerifySoftwareKey.

The method VerifySoftwareKey internally uses the following methods:

  • RetrieveMACAddress;
  • ComputeMachineCode;
  • ComputeSoftwareKey;
  • Buffer2String.

Customer tool - “softwarekey_customertool.exe”

The customer uses this tool in order to generate a machine code.

This tool uses the following methods of CSoftwareKey:

  • RetrieveMACAddress;
  • ComputeMachineCode;
  • Buffer2String.

The usage is:

softwarekey_customertool    -g

Vendor tool - “softwarekey_vendortool.exe”

The vendor uses this tool in order to generate a software key matching the given machine code. This tool uses the following methods of CSoftwareKey:

  • String2Buffer;
  • ComputeSoftwareKey;
  • Buffer2String.

The usage is:

softwarekey_vendortool     -v machinecode

Hints to by-pass the software key

In this section, we give just some hints to bypass the software key protection. We can open the executable file “protected_comp.exe” with a disassembler or a debugger, with the goal of searching for the places in which the API for showing a message box is called. In particular, we look for a message box displaying the message "wrong software key" hoping to find in around it the code that verifies the installed software key.

As our example is quite trivial, and most importantly, as we have written it :), we easily find the code that verifies the installed software key. In the following snippet, we show a dump of this code, with some comments, excerpted using an assembler-level debugger.

ASM
; Call of method CSoftwareKey::GetSoftwareKeyStringFromIniFile(...)
004012E0  SUB ESP,8
004012E3  LEA EAX,DWORD PTR SS:[ESP+4]
004012E7  MOV DWORD PTR SS:[ESP+4],0
004012EF  PUSH EAX
004012F0  PUSH 004070B4                    
004012F5  CALL 004011A0

; Call of method CSoftwareKey::VerifySoftwareKey(...)
004012FA  MOV EDX,DWORD PTR SS:[ESP+C]
004012FE  LEA ECX,DWORD PTR SS:[ESP+B]
00401302  PUSH ECX
00401303  PUSH EDX
00401304  MOV BYTE PTR SS:[ESP+13],0
00401309  CALL 00401030

0040130E  MOV AL,BYTE PTR SS:[ESP+13]
00401312  ADD ESP,10
00401315  TEST AL,AL
00401317  PUSH 0              ; Style = MB_OK|MB_APPLMODAL


; Hex pattern of the following instruction and some context is 
; (....0x6A 0x00 0x75 0x1B 0x68 0xAC....).
; This information should be made accessible by the debugger.
00401319  JNZ SHORT 00401336                                        


; Call of function MessageBoxA contained in user32.dll
0040131B  PUSH 004070AC                               ; Title = "error"
00401320  PUSH 00407098                               ; Text = "wrong software key"
00401325  PUSH 0                                      ; hOwner = NULL
00401327  CALL DWORD PTR DS:[<&USER32.MessageBoxA>]   ; MessageBoxA
0040132D  MOV EAX,1
00401332  ADD ESP,8
00401335  RETN

; Call of function MessageBoxA contained in user32.dll
00401336  PUSH 00407094                    ; Title = "ok"
0040133B  PUSH 00407074                    ; Text = "correct software key installed"
00401340  PUSH 0                           ; hOwner = NULL
00401342  CALL DWORD PTR DS:[<&USER32.MessageBoxA>] ; MessageBoxA
00401348  XOR EAX,EAX
0040134A  ADD ESP,8
0040134D  RETN

Having a little knowledge of assembler, we can easily understand that the instruction contained at address “00401319” verifies the software key. In fact, at address "00401336", we can see the code that shows the message box "correct software key installed". Now, we can replace this instruction (the conditional jump instruction "JNZ SHORT 00401336") with an unconditional jump “JMP SHORT 00401336”. Doing that, we can easily bypass the protection.

In more detail, in order to remove the software key protection, a possible way is:

  • Using an assembler-level debugger, we determine the hex pattern (i.e., sequence of bytes) representing the instruction we would like to change. In our case, the hex pattern belonging to "JNZ SHORT" is “0x75”. It is useful to consider the bytes around as well. So, we consider the pattern “0x6A 0x00 0x75 0x1B 0x68 0xAC”.

    As we’ll see later, this will help us in searching in the executable file the "right" bytes to change. It is important that the tool we are using allows us to view the hex dump of the executable file corresponding to every assembler instruction;

  • Now, we can open the file “protected_comp.exe” with a binary editor (F.Y.I., MS Visual Studio) as a binary file;
  • After that, we search for the binary path “0x6A 0x00 0x75 0x1B 0x68 0xAC” (note that the byte “0x75” correspond to the JNZ SHORT instruction, while the remaining, useful for simplifying the research, are the bytes around this instruction …);
  • Then we can replace the byte “0x75” with “0xEB” (note that “0xEB” is the numerical value for JMP SHORT) and can save this tampered file as “protected_comp_patched.exe”;
  • Now, running this executable with a wrong software key installed in file "protected_comp.ini", we’ll see a message box showing “correct software key installed” instead of the expected one showing "wrong software key".

Note that, in this simple example, it has been enough changing just one byte in the executable to bypass the software key protection. In the following section, we’ll show some possible approaches to make this work harder. A ready to use copy of “protected_comp_patched.exe” is available for download in “softwarekey_demo.zip”.

Hints to improve the software key

In this section, we point up just a few ideas that could be useful to improve the protection offered by the software key presented in this article. The aim is just to provide some useful notes. All the approaches presented here can be bypassed. They can only augment the effort needed by an attacker.

These approaches can be divided in two groups detailed in the following paragraphs:

  • Code obfuscation and tampering detection: these techniques try either to complicate the work of determining the "right place" in the code to modify or to detect code tampering;
  • Enforce functions f and g: these techniques try to complicate functions f and g. The aim here is to make harder the work of determining the software key starting from a well-known machine code.

Code obfuscation and tampering detection

In order to bypass the protection offered by our software key, in the previous paragraph we have modified the code. The suggestions we provide in this paragraph try either to complicate the work of determining the "right place" in the code to modify or to detect code tampering.

  • There are coding techniques that allow making the assembly (both data and code) as tricky as possible to read and manipulate. It could be useful to forget every good programming paradigm, as object oriented programming, data encapsulation, and so on. Rowly speaking, we can say that the more our code looks like "spaghetti code", the best it is. The purpose of doing that is to force our compiler to produce a tricky machine code as well. The issue has been studied in detail and has been formalized under the name of "code obfuscation";
  • It is a good idea to keep the code that verifies the software key as away as possible from the code that signals to the user the presence of a wrong key. For instance, in a previous paragraph, we have easily recognized the byte to tamper as the conditional instruction was next to the one showing the message box indicating that a wrong key was installed. Further, we can add several checks instead of just one;
  • Instead of using a message box for signaling that a wrong software key has been installed, in that case, we can force some bugs in the code. In this way, the program won't run correctly and it will be more difficult for the user to understand the point of code to tamper;
  • An approach useful to discover tampering of code or data is to compute and verify checksums for data and code that could be tampered;
  • In order to disorientate an attacker, we can modify some parts of the code dynamically (F.Y.I., inserting software key verifications).

A combination of these techniques (and others) can make the work of individuating and modifying the assembly code that manages the software key more difficult.

Enforce functions f and g

As said before, in the code provided with this article, functions f and g are just simple permutations. In order to improve protection, more complex algorithms should be used. The suggestions we provide in this paragraph try to complicate these functions.

A first trivial solution, valid both for f and g, is to use a bigger size (i.e., number of bytes) for the machine code and the software key.

In order to enforce function f, we can generate a machine code starting from a combination of the subsequent values instead of using only the MAC address:

  • Processor ID (usually accessible using a specific machine code instruction);
  • Windows serial number (accessible in the registry, usually saved in the key with path “\\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ProductID”);
  • Hard disk volume ID (in some cases, accessible using API GetVolumeInformation or specific interrupt address);
  • Other “quite” stable system information.

This means making the machine code computation a function of the above-mentioned parameters:

MachineCode = f (MACaddress, ProcessorID, WindowsSerialNumber, …)

The use of this information could be a heavy task as their retrieval could be different with different hardware devices or operating systems.

In order to enforce the function g, we can use public key cryptography. A possible solution is drawn in the following. In that case, for each customer's machine, the vendor generates a pair of keys <"PrivateKey", "PublicKey">. Then, the vendor sends to the customer a package containing "softwarekey_customertool.exe", "protected_comp.exe", "protected_comp.ini" and "PublicKey". As usual, customer generates its machine code using "softwarekey_customertool.exe" and sends it to the vendor.

MachineCode = f (MACaddress,...)

On receiving the machine code, the vendor generates the software key encrypting the machine code using "PrivateKey". Now we have:

SoftwareKey = g (MachineCode) = Encrypt(PrivateKey, MachineCode)

where Encrypt is a function that encrypts MachineCode using the key PrivateKey. At this point, the vendor sends the software key to the customer, who installs it in "protected_comp.ini". For checking the installed software key, "protected_comp.exe" will perform some code described by the following pseudocode.

sk = CSoftwareKey::GetSoftwareKeyFromIniFile(...);

mc = Decrypt(PublikKey, sk);

mac = CSoftwareKey::RetrieveMACAddress(...);

mc1 = CSoftwareKey::ComputeMachineCode(mac); // this is the function "f"

if (mc != mc1)
{
    "wrong software key"
}

In this way, we have made the task of discovering the function g as complex as the public cryptography can. Note that the strength of encryption is related to the difficulty of discovering the key, which in turn depends on both the cipher suite used and the length of the key.

Also note that with the suggested approach, "PrivateKey" is not necessary on customer side.

Note: Let's drop some observations about the ways a customer can follow to bypass the protection offered by our software key. These thoughts should help us in understanding how it is important to use complex functions for f and g. Let's suppose the customer has obtained a valid software key for a machine A and wants to use "protected_comp.exe" on a machine B for which he doesn't dispose of a valid software key. In our scenario, customer has the tool for generating machine codes (substantially, he has an implementation of the function f).

On one side, if he knew the function g, he could (obviously) generate software keys by himself for whatever machine he wants. Therefore, it is straightforward the need of hiding g.

On the other side, if he knew the function f and its parameters p1,...,pn , he would tamper them on a machine B (if it is possible) in such a way that the machine code for B, computed using f is the same as the one generated for a machine A (for which he has a valid software key). For instance, if we use only the MAC address as parameter for f, moving the network adapter on a machine B should be enough to make the software key obtained for A valid for machine B as well. Therefore, it is important to chose p1,...,pn in such a way we can get "a more reliable as possible" signature of the machine. Considering what is mentioned above, it seems that using a complex function for f could be useful at least for hiding the set of parameters p1,...,pn used in the computation of the machine code.

Alternatives to software key

In the following section, we point up just a few well-known alternative approaches to the use of a software key.

Remote code execution

The idea is to execute the code of the application on vendor side as much as possible. The major drawbacks are:

  • it requires an active connection. Note that in some situations this could be a problem;
  • it needs to assure integrity and confidentiality of the data transferred between customer and vendor site;
  • it requires strong server on the vendor site in which executes part of the application.

Hardware key

The idea is to keep some part of the code needed to execute the component inside the hardware key (in the worst case, only the software key). The major drawback is that, since they are not for free, they are not be suitable for simple or "personal" components.

Limitations

  • For the sake of simplicity, we have used simple permutations for functions f and g. These algorithms should be made more complex. For the same reason, we have used short dimensions (in bytes) for machine code and software key. In a real scenario, they should be augmented. As said before, a better solution can be obtained using cryptography. Further, function f depends only on MAC address. It could be a good idea to make it depending on other values such as processor ID, Windows serial number, etc.;
  • For the sake of clearness, we haven’t used any of the above mentioned techniques for improving software key protection;
  • We have not tested the method “CSoftwareKey::RetrieveMACAddress” in machines with more than one network cards (usually, Ethernet cards) or with particular operating system configurations (e.g., clustering). In such cases, if some problems arise, the hidden API “GetAdaptersInfo” contained in “IPHLPAPI.DLL” could be useful;
  • As stated before, in the code related to NT of CSoftwareKey::RetrieveMACAddress method, we've not checked the service pack version installed. This implies that this function won't work properly in Windows NT, if a certain service pack has been installed;
  • In the attached code, a lot of error situations are there; also where they are envisioned are not management.

The download

The download “softwarekey.zip” contains source code and executables ready to use. Unzipping this file, you can find the following directories:

  • "protected_comp" contains source code for the component “protected_comp.exe”;
  • "softwarekey_customertool" contains source code for the tool "softwarekey_customertool.exe”;
  • "softwarekey_vendortool" contains source code for the tool "softwarekey_vendortool.exe”;
  • "softwarekey_demo” contains executables ready to use.

History

  • November 2004 – First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Italy Italy
I'm graduated in computer science and I'm working as a software analyst and programmer in the field of medical information technology.

Comments and Discussions

 
QuestionAwesome! Pin
Member 119546574-Sep-15 15:50
Member 119546574-Sep-15 15:50 
GeneralMy vote of 5 Pin
Rupesh Baikar17-Oct-12 9:39
Rupesh Baikar17-Oct-12 9:39 
GeneralMy vote of 5 Pin
apandey214-Sep-12 20:29
apandey214-Sep-12 20:29 
QuestionVery Urgent Help Pin
Rohit Sinha5418-Sep-09 1:11
Rohit Sinha5418-Sep-09 1:11 
GeneralProtect DLL Pin
tuanpm26-Aug-07 23:30
tuanpm26-Aug-07 23:30 
GeneralProblem with Windows Vista Business Pin
mrtrantuan™13-Jul-07 18:49
mrtrantuan™13-Jul-07 18:49 
GeneralRe: Try PELock instead Pin
Bartosz Wójcik19-Dec-07 4:29
Bartosz Wójcik19-Dec-07 4:29 
GeneralMemory leaks... Pin
JKJKJK22-Dec-05 16:11
JKJKJK22-Dec-05 16:11 
GeneralAnother way to protect your program Pin
-asm-26-Jul-05 1:17
suss-asm-26-Jul-05 1:17 
GeneralBiometrics... Pin
M i s t e r L i s t e r20-Jan-05 3:57
M i s t e r L i s t e r20-Jan-05 3:57 
QuestionHow to avoid IF (a = b) ... Pin
burek1237-Jan-05 6:56
burek1237-Jan-05 6:56 
QuestionWhat about ASProtect ? Pin
Defenestration5-Jan-05 9:34
Defenestration5-Jan-05 9:34 
AnswerRe: What about ASProtect ? Pin
Bartosz Wójcik19-Dec-07 6:36
Bartosz Wójcik19-Dec-07 6:36 
Generalmy five cents Pin
fafasoft31-Dec-04 22:41
fafasoft31-Dec-04 22:41 
GeneralInternet validation of license keys works Pin
Jon Person30-Dec-04 21:37
Jon Person30-Dec-04 21:37 
GeneralRe: Internet validation of license keys works Pin
Ganti427022-Apr-09 18:44
Ganti427022-Apr-09 18:44 
GeneralBad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) PinPopular
Krzysztof Wojdon29-Dec-04 23:32
Krzysztof Wojdon29-Dec-04 23:32 
I really hate it, hate it, hate it... Not this article, no! I realy hate it when I am going to complain. And I am. Not that this article is bad - no, it's not. It would be quite good for an article giving general point of view on some subject to wide publicity. The problem is that this article is NOT "a way to implement a simple software key that could be useful for protecting software components". Even if you make your homework and implement your protection precisely as described you will not end up with a useful protection. Why? Because no matter what you do on high-level, it will be cracked by most teenage crackers in two hours.

1) First important thing: all sensible software protections must be done with some low level (assembly) programming and you cannot dream that you make cracker fool without it. Why? Because crackers do not work in high level. They do not care about all that OOP stuff and they even do not have to understand all your complicated ideas behind your copy protections (unfortunately, they often will). They live in hex and this is how how they see your code. And at high level languages simply do not have enough control over the code they see. Also you cannot defeat disassemblers and debuggers in a serious way from C++. Also you cannot make real self-modyfing code from C++. And as long as it can be run under low-level debugger (Softice, etc.) and disassembled with good dissasembler (like IDA) your code is like an open book to everyone (believe me). And you cannot fight against these tools at high level language - calling some borrowed functions "detectSoftice()" does not count here - such "protections" can be easily spotted and deactivated.


2) This is general pain in all protections related articles: authors jump over most important (and most complicated parts) in a few sentences. This is general problem in all scientific articles - authors skip hard parts - I know, because I did it and then one mathematician told me that this is completely normal even in academic world. Smile | :)

In this article such part is "Code obfuscation and tampering detection". This paragraph describes real battlefield between "protectionists" and "crackers". Programmer has to protect code from being read/understand and then modified by cracker. This is where you can win - not in complications of the f() & g() functions. Copy protection author must bore crackers, annoy them, make them think "this app is not worth my time", but you have to protect your code against being reverse engineered by unauthorized people. If you let cracker understand your code, you lost the war. I understand that the problem is that by treating seriously anti-debugging/anti-disassembling tricks one could easily write a big book completely undreadable to C/C++ people, so I do not complain much here. I just want to spot the fact, that readers should remember that without low level language you cannot do anything wise according to the advices in the obfuscation&tampering detection paragraph.


3) You can use all the cryptography you want in functions f() & g(), but crackers who just want to "break the app" will look for a key comparison, and will just skip all the key calculations. Let's make assuption that we wrote both f() & g() totally uncrackable. We still are in problem, because in this part:

---
sk = CSoftwareKey::GetSoftwareKeyFromIniFile(...);
mc = Decrypt(PublikKey, sk); // this is "g" - my note (KW)

mac = CSoftwareKey::RetrieveMACAddress(...);
mc1 = CSoftwareKey::ComputeMachineCode(mac); // this is the function "f"

if (mc != mc1)
{
"wrong software key"
}
---

cracker-boy (or cracker-girl) does not care about complexity of Decrypt()/ComputeMachineCode() - crackers are looking for the GOOD_BOY/BAD_BOY branch to make it permanent GOOD_BOY jump. In this example this part is
"if (mc != mc1)", and this part is going to be traced and patched.

The only case when they study key calculation algorithms is when they try to make a keygen, which is done by them mostly for fun (so they can show how good they are). Fortunately for us this idea can be easily defeated by using any of the well known strong cipher methods (what author wisely stated). When you make f() & g() working OK - you are free from keygen disease. And this is why you should carefully implement f() and g(). Not because it makes protection much better (nevertheless, your application will be cracked), but because it is _MUCH_ better to fight against cracks than keygens. Why?

A) Because when someone will publish keygen for your software you have to change the serial/key algorithms in the next application release and it means annoying all legal users with serial numbers (aka keys) change procedure.

B) When you release update your application application files changes in the way that make old cracks unusable (crack changes specified location in the files) - read it: crackers must crack your application again. "Again" because if you are smart enough you changed protection methods once you know it was cracked. Smile | :)


4) If anybody out there is thinking "Wow, I will do my protection!" after reading this article - do not waste your time. It is simply not your time yet. This article is just a good "foreword" to the problem of a single type of protection. It does not even cover "high level protection most obvious mistakes", e.g. using "if()" to make any protection stuff, using built-in language (strcmp() Smile | :) ) comparison functions, leaving cracker-readable strings in the code or - heavens save us! - function names in the dll libraries).

If you really want to make a real protection - and it is a hard work, much harder that normal high level programming - learn to crack protections first. You have a few hundreds of articles to read on the net (hint: old school "Fravia" stuff is a good reading for a begginers). Then go visit some crackers boards to get hang of the "current trade tools and methods". After some time you will be able to make a protection that will be a problem to some young to-be-crackers and will be cracked in no time by some more experienced board member. Good side is that meanwhile you will get a well paid work and when you spend all your efforts on protections - you will finally go with something really tough aka "useful". Smile | :) If you do not want to dedicate yourself to protections - give low level stuff to someone who loves to live in hex-world and concentrate on the high level programming. Join your efforts with a low-level guy and most crackers will break their teeth. When those real crackers will jump on your software and crack it - it means you have such a really good product and you will spend more time on coordination of programmers team that work for you and not how to write serial number comparison routine. Smile | :)

I know it seems strange, but you must be a good cracker to start thinking about being real anti-cracker. It is much harder to make a protection than break it! It sounds extremely stupid, but it works just like this. Remember that your code is left alone with a cracker, his tools and brain. You must predict as many as possible of his steps, there is not time later to fix anything when you give him your code.


5) Protection cannot be closed in a one class/function set (do not even think about something like protection.dll). If you do not link code with protection very carefully in many places it will be very easy to separate protection and remove. It does not matter if you use hardware keys, these keys when used without practice also can be removed easily and are not any better then a good software key.


General conclusion:

If you remember that this article is 10% to make useful protection and not 100% you are doing well, and the author is, as the article is a really well written introduction to the described protection scheme. But protection as described in the article is not a protection, it is a plain legal check scheme and nothing more. If you are satisfied with "better this than nothing" - it is OK and article can be article of the month. If you think about real protection - this article does have it.

Best regards to the author and all readers,
Krzysztof Wojdon


Completely off-topic the article yet a little related problems I would like to discuss if anybody wants to talk with me after all my complaints. Wink | ;-)


1) CONTROLLED ERRORS - "Instead of using a message box for signaling that a wrong software key has been installed, in that case, we can force some bugs in the code. In this way, the program won't run correctly and it will be more difficult for the user to understand the point of code to tamper;"

In this case "user" means "cracker" and the sentence simply states that you have to make calculations in your code depending on the keys. I assume reader understands that idea like the one below is a nonsense, because cracker with one eye blind will smell it from the first view at disassembly:

if (code == GOOD_CODE)
result = 5*result;
else
result = 5.1*result;

and it is better to replace it with something like this:

result = 5+0.1*(code-GOOD_CODE);

(which is better, but also useless if "code" variable memory address is known as a protection-related to hacker).

I understand you can use CONTROLLED ERROR when you make a game, painting program or jumping Picachu screensaver. But I am not sure if you can use CONTROLLED ERRORS in serious application that results are important for any real-life projects (e.g. related to human life/health like e.g. building, medical, etc.). It is a little morality problem, but can programmers take the risk of a serious catastrophe just to protect income? Even if you think you can, remember what can broken protection od this type do to legal users.

BTW, this kind of protection was used in one of the Settlers games - first bad cracks make users nervous when Iron-Melting facility produced... pigs. Big Grin | :-D


2) OBFUSCATION - I am not sure if it can be used for real, big projects. I am simply afraid that keeping control over normal code is hard enough for most teams and working/fixing/debugging obfuscated code is just simply crazy and as a result obfuscating works best in academic discussions or small home projects. To be clear - if you obfuscate only parts of the protection, you take a big risk of the "chain is as strong as the smallest part" problem - I mean: obfuscated code will be skipped and protection will be cracked in unobfuscated part. If someone ever worked on a big projects that was largely obfuscated - please let me know.


3) GENERAL PROTECTION LOGIC - Why protections do not work in most cases? Because good protection should be threaded through all the project. And how can you do it when a team of programmers work on a project? Project leader cannot ask all the programmers in the team to concentrate on the protection. It is simply not possible, because they are focused on the project itself. So in most cases programmers are "blessed" with some set of functions that are expected to be "unbreakable protection". So in best cases they dig through the protection documentation and try to make calls to "unbreakable protection" in some parts of their code, but it is simply not enough to make a protection. And it is neither their fault nor protection author. It is just a weakest link problem.


GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
Manuele Sicuteri30-Dec-04 2:37
Manuele Sicuteri30-Dec-04 2:37 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
Krzysztof Wojdon30-Dec-04 8:50
Krzysztof Wojdon30-Dec-04 8:50 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
Anonymous31-Dec-04 6:22
Anonymous31-Dec-04 6:22 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
Marek Grzenkowicz30-Dec-04 4:18
Marek Grzenkowicz30-Dec-04 4:18 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
Tiger11-Jan-05 10:21
professionalTiger11-Jan-05 10:21 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
pocjoc30-Jan-05 23:04
pocjoc30-Jan-05 23:04 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
Krzysztof Wojdon31-Jan-05 11:31
Krzysztof Wojdon31-Jan-05 11:31 
GeneralRe: Bad news, fellow programmers, C++ protection will be cracked in one evening. No matter what. ;-) Pin
pocjoc1-Feb-05 0:10
pocjoc1-Feb-05 0:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.