|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIn 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. BackgroundIn 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 worksLet'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 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 SoftwareKey = g (MachineCode)
Note: In the code provided with this article, functions 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. Inside the codeCore classIn this section, we give a brief description of the class 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 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 The method The method The method The method The method The method Note: In the above 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
The method
Customer tool - “softwarekey_customertool.exe”The customer uses this tool in order to generate a machine code. This tool uses the following methods of
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
The usage is: softwarekey_vendortool -v machinecode
Hints to by-pass the software keyIn 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. ; 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 " In more detail, in order to remove the software key protection, a possible way is:
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 keyIn 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 detectionIn 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.
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 gAs said before, in the code provided with this article, functions A first trivial solution, valid both for In order to enforce function
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 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 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 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 On one side, if he knew the function On the other side, if he knew the function Alternatives to software keyIn the following section, we point up just a few well-known alternative approaches to the use of a software key. Remote code executionThe idea is to execute the code of the application on vendor side as much as possible. The major drawbacks are:
Hardware keyThe 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
The downloadThe download “softwarekey.zip” contains source code and executables ready to use. Unzipping this file, you can find the following directories:
History
| ||||||||||||||||||||||