String encryption using macro and cryptor
Self-decrypting strings in C/C++

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.

Article published on 29 June 2014
last modification on 28 May 2016

by Emeric Nasi


License : Copyright Emeric Nasi, some rights reserved
This work is licensed under a Creative Commons Attribution 4.0 International License.
Creative Commons License

I Presentation.

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).

Note : Protecting the decryption password is not really the subject here but for more security the access to password and decryption algorithm could be obfuscated or protected by anti-debug/anti-disassembly trick.

III Implementation.

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.

 

III.3 Usage

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.