I’ve found out a lot of people want to be able to encrypt string in a C or C++ software. There are a lot of methods available, some of them not very user friendly. Here is my own method of encrypting string and have them decrypted at runtime.
by Emeric Nasi
License : Copyright Emeric Nasi, some rights reserved
This work is licensed under a Creative Commons Attribution 4.0 International License.
I.1 Strings encryption
I’ve found out a lot of people want to be able to encrypt strings in a C or C++ software. There are a lot of methods available, some of them not very user friendly. I present here my own proof of concept code for encrypting string and have them decrypted at runtime.
I.2 The requirements
We want the process to be user friendly, which means:
- We are able to tell which string is going to be encrypted and which is not
- All strings must be stored in clear-text in the source code (not in encrypted unreadable form).
- Encryption/Decryption password is randomly generated for each build without manual intervention
We want the process to be easy:
- A single MACRO function is used to mark the secured strings and to decrypt them at runtime.
- Encryption is done on the binary code, not on the source code.
Memory must be protected against strings listing:
- Strings must be kept encrypted in memory
- Limited number of decrypted string at the same time
II The method.
II.1 Mark Strings
In our case we want to be sure we encrypt only strings, and we also want to choose which strings are going to be encrypted. For that we will mark the strings in a way they are recognized after compilation.
The way I choose to mark a string is to append a ’.’ and a NULL character at the end of the string. It is pretty easy to do with a MACRO and during encryption phase cryptor can find the pattern.
Example for the ASCII string "wyziwig":
Normal string -> 'w','y','z','i','w','i','g','\0' Marked string -> 'w','y','z','i','w','i','g','\0','.','\0'
In both case printf(%s), strlen, strcmp and other string manipulation functions will stop at the first ’\0’. That means when you need it, the string is decrypted then you can manipulate it like a regular ACSII char *.
II.2 Encryption phase
We want the encryption to be done on the binary code. For that we have to build a cryptor application which is able to recognized the strings which must be secured on the binary executable. This means we are going to have two steps generation. First, the application executable is generated without encryption, next the encryption is done by the cryptor software.
So the first thing is, how are we going to recognize our strings in the binary code?
First, it is important to know there is no way to give an exact and precise list of string from a binary code. There are tools like Strings from Sysinternal which can display a list of strings. It basically returns all series of 3 or more string characters found in string followed 00 character (NULL) which ends all strings in C/C++. The problem with Strings tool is it will return a lot of strings which are in fact just code instruction happening to match the pattern.
We are going to be sure we can find all the strings to secured without false positives by applying two filters:
- First we use a method similar to the one used by strings listing programs.
- Second we scan the results of operation one for marked strings (characters ’\0’, ’.’, ’\0’ ending the string).
II.3 Decryption phase
Decryption is done one string at a time at runtime. When a char * which is encrypted must be used, it is automatically decrypted and stored in temporary buffer. This is easy to do with a MACRO surrounding declared strings.
In the method described here, only one string at the time is in clear text in the program. This is more secured but leads to limitations in the application source code (see section V for more about that).
III.1 Encrypted string target application
First we build a XOR string decryption function. Strings are decrypted one at a time and stored in global variable buffer.
Next we declare a macro to wrap up strings which should be tagged and decrypted.
III.2 Cryptor application
The cryptor application will:
- Generate random key
- Read target file in memory
- Search for marked strings
- Encrypted using key
- Replace secStrKey value by random key
- Replace target by new code
Here is the implementation of the binStrstr() function which is used to find the key signature in the binary.
We also need a XOR encryption feature in cryptor.
Using this method is pretty simple. There are only two additional steps for the developer:
- Use SEC_STR on strings which must be encrypted
- Run cryptor on generated binary before it is started/distributed.
Here are some example on how to use the code in the target application:
IV Going further.
One limitation in the above code is it does not support multi-threading (one decryption store shared by all the threads). There are several possibilities regarding that issue.
Personally I use a dedicated thread storage for strings which must be encrypted by a given thread (one storage per thread). This means the number of decrypted string at runtime is equal to the number of threads.
The current method also brings some limitation in the usage of source code, for example in the same thread, it is not possible to call a function with two parameters being encrypted strings. If you don’t want to worry about that, you can increase the number of strings which can be decrypted at the same time. You could also decide it is OK for you to have all strings decrypted at runtime, in this case you don’t need string storage.
Another possibility for the C++ users is to modify the decryptStr function to make it manipulate String C++ objects. With that method you don’t need explicit string storage as memory management is done dynamically behind the scene.