23

I don't want reverse-engineers to read the plain-text of hardcoded strings in my application. The trivial solution for this is using a simple XOR-Encryption. The problem is I need a converter and in my application it will look like this:

//Before (unsecure)
char * cString = "Helllo Stackoverflow!";
//After (secure)
char * cString = XStr( 0x06, 0x15, 0x9D, 0xD5FBF3CC, 0xCDCD83F7, 0xD1C7C4C3, 0xC6DCCEDE, 0xCBC2C0C7, 0x90000000 ).c();

Is there the possibility to maintain clean code by using some construct like

//Before (unsecure)
char * cString = "Helllo Stackoverflow!";
//After (secure)
char * cString = CRYPT("Helllo Stackoverflow!");

It should also work for quite long strings ( 1000 characters? :-) ). Thank you in advance

Listing
  • 1,171
  • 2
  • 15
  • 31
  • Well, does it work if you try it? – Brian Roach Sep 01 '11 at 12:47
  • 4
    @Brian Of course not. The question is quite clear: is there a mechanism, possibly using macros, to *make* it work? Not a stupid question at all – as far as I know there is no exact solution but you can get quite close using templates. – Konrad Rudolph Sep 01 '11 at 12:47
  • 2
    Do keep in mind that this kind of encryption is trivial to break; the contents of "cString" are going to be unencrypted when your program starts up and visible to anyone with access to your RAM. This not even counting that XOR is trivial to break in and of itself. – Billy ONeal Sep 01 '11 at 12:49
  • @Billy: I know but you won't find the string using a debugger because it will not be separable from ordinary memory. Thats already all that I want. – Listing Sep 01 '11 at 12:51
  • Unfortunately C++ does not have full Dependent Types, so strings cannot be broken down by the template mechanisms (and of course, neither can they by the preprocessor since it does not split tokens). Your best bet is to have them externally defined in a generated file. – Matthieu M. Sep 01 '11 at 13:15

8 Answers8

16

Perfect solution does exist, here it is.

I also thought this wasn't possible, even though it's very simple, people wrote solutions where you need a custom tool to scan the built file afterwards and scan for strings and encrypt the strings like that, which wasn't bad but I wanted a package that's compiled from Visual Studio, and it's possible now!

What you need is C++ 11 (Visual Studio 2015 Update 1 out of the box)

the magic happens with this new command constexpr

By magic happens in this #define

#define XorString( String ) ( CXorString<ConstructIndexList<sizeof( String ) - 1>::Result>( String ).decrypt() )

It won't decrypt the XorString at compile-time, only at run-time, but it will encrypt the string only in compile-time, so the strings will not appear in the Executable file

printf(XorString( "this string is hidden!" ));

It will print out "this string is hidden!" but you won't find it inside Executable file as strings!, check it yourself with Microsoft Sysinternals Strings program download link: https://technet.microsoft.com/en-us/sysinternals/strings.aspx

The full source code is quite large but could easily be included into one header file. But also quite random so the encrypted string outputs will always change every new compile, the seed is changed based on the time it took it compile, pretty much solid,perfect solution.

Create a file called XorString.h

#pragma once

//-------------------------------------------------------------//
// "Malware related compile-time hacks with C++11" by LeFF   //
// You can use this code however you like, I just don't really //
// give a shit, but if you feel some respect for me, please //
// don't cut off this comment when copy-pasting... ;-)       //
//-------------------------------------------------------------//

////////////////////////////////////////////////////////////////////
template <int X> struct EnsureCompileTime {
    enum : int {
        Value = X
    };
};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//Use Compile-Time as seed
#define Seed ((__TIME__[7] - '0') * 1  + (__TIME__[6] - '0') * 10  + \
              (__TIME__[4] - '0') * 60   + (__TIME__[3] - '0') * 600 + \
              (__TIME__[1] - '0') * 3600 + (__TIME__[0] - '0') * 36000)
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
constexpr int LinearCongruentGenerator(int Rounds) {
    return 1013904223 + 1664525 * ((Rounds> 0) ? LinearCongruentGenerator(Rounds - 1) : Seed & 0xFFFFFFFF);
}
#define Random() EnsureCompileTime<LinearCongruentGenerator(10)>::Value //10 Rounds
#define RandomNumber(Min, Max) (Min + (Random() % (Max - Min + 1)))
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
template <int... Pack> struct IndexList {};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
template <typename IndexList, int Right> struct Append;
template <int... Left, int Right> struct Append<IndexList<Left...>, Right> {
    typedef IndexList<Left..., Right> Result;
};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
template <int N> struct ConstructIndexList {
    typedef typename Append<typename ConstructIndexList<N - 1>::Result, N - 1>::Result Result;
};
template <> struct ConstructIndexList<0> {
    typedef IndexList<> Result;
};
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
const char XORKEY = static_cast<char>(RandomNumber(0, 0xFF));
constexpr char EncryptCharacter(const char Character, int Index) {
    return Character ^ (XORKEY + Index);
}

template <typename IndexList> class CXorString;
template <int... Index> class CXorString<IndexList<Index...> > {
private:
    char Value[sizeof...(Index) + 1];
public:
    constexpr CXorString(const char* const String)
    : Value{ EncryptCharacter(String[Index], Index)... } {}

    char* decrypt() {
        for(int t = 0; t < sizeof...(Index); t++) {
            Value[t] = Value[t] ^ (XORKEY + t);
        }
        Value[sizeof...(Index)] = '\0';
        return Value;
    }

    char* get() {
        return Value;
    }
};
#define XorS(X, String) CXorString<ConstructIndexList<sizeof(String)-1>::Result> X(String)
#define XorString( String ) ( CXorString<ConstructIndexList<sizeof( String ) - 1>::Result>( String ).decrypt() )
////////////////////////////////////////////////////////////////////

UPDATED CODE BELOW, This is a better version below and supports char and wchar_t strings!

#pragma once
#include <string>
#include <array>
#include <cstdarg>

#define BEGIN_NAMESPACE( x ) namespace x {
#define END_NAMESPACE }

BEGIN_NAMESPACE(XorCompileTime)

constexpr auto time = __TIME__;
constexpr auto seed = static_cast< int >(time[7]) + static_cast< int >(time[6]) * 10 + static_cast< int >(time[4]) * 60 + static_cast< int >(time[3]) * 600 + static_cast< int >(time[1]) * 3600 + static_cast< int >(time[0]) * 36000;

// 1988, Stephen Park and Keith Miller
// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
// with 32-bit math and without division

template < int N >
struct RandomGenerator
{
private:
    static constexpr unsigned a = 16807; // 7^5
    static constexpr unsigned m = 2147483647; // 2^31 - 1

    static constexpr unsigned s = RandomGenerator< N - 1 >::value;
    static constexpr unsigned lo = a * (s & 0xFFFF); // Multiply lower 16 bits by 16807
    static constexpr unsigned hi = a * (s >> 16); // Multiply higher 16 bits by 16807
    static constexpr unsigned lo2 = lo + ((hi & 0x7FFF) << 16); // Combine lower 15 bits of hi with lo's upper bits
    static constexpr unsigned hi2 = hi >> 15; // Discard lower 15 bits of hi
    static constexpr unsigned lo3 = lo2 + hi;

public:
    static constexpr unsigned max = m;
    static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
};

template <>
struct RandomGenerator< 0 >
{
    static constexpr unsigned value = seed;
};

template < int N, int M >
struct RandomInt
{
    static constexpr auto value = RandomGenerator< N + 1 >::value % M;
};

template < int N >
struct RandomChar
{
    static const char value = static_cast< char >(1 + RandomInt< N, 0x7F - 1 >::value);
};

template < size_t N, int K, typename Char >
struct XorString
{
private:
    const char _key;
    std::array< Char, N + 1 > _encrypted;

    constexpr Char enc(Char c) const
    {
        return c ^ _key;
    }

    Char dec(Char c) const
    {
        return c ^ _key;
    }

public:
    template < size_t... Is >
    constexpr __forceinline XorString(const Char* str, std::index_sequence< Is... >) : _key(RandomChar< K >::value), _encrypted{ enc(str[Is])... }
    {
    }

    __forceinline decltype(auto) decrypt(void)
    {
        for (size_t i = 0; i < N; ++i) {
            _encrypted[i] = dec(_encrypted[i]);
        }
        _encrypted[N] = '\0';
        return _encrypted.data();
    }
};

//--------------------------------------------------------------------------------
//-- Note: XorStr will __NOT__ work directly with functions like printf.
//         To work with them you need a wrapper function that takes a const char*
//         as parameter and passes it to printf and alike.
//
//         The Microsoft Compiler/Linker is not working correctly with variadic 
//         templates!
//  
//         Use the functions below or use std::cout (and similar)!
//--------------------------------------------------------------------------------

static auto w_printf = [](const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vprintf_s(fmt, args);
    va_end(args);
};

static auto w_printf_s = [](const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vprintf_s(fmt, args);
    va_end(args);
};

static auto w_sprintf = [](char* buf, const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsprintf(buf, fmt, args);
    va_end(args);
};

static auto w_sprintf_ret = [](char* buf, const char* fmt, ...) {
    int ret;
    va_list args;
    va_start(args, fmt);
    ret = vsprintf(buf, fmt, args);
    va_end(args);
    return ret;
};

static auto w_sprintf_s = [](char* buf, size_t buf_size, const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsprintf_s(buf, buf_size, fmt, args);
    va_end(args);
};

static auto w_sprintf_s_ret = [](char* buf, size_t buf_size, const char* fmt, ...) {
    int ret;
    va_list args;
    va_start(args, fmt);
    ret = vsprintf_s(buf, buf_size, fmt, args);
    va_end(args);
    return ret;
};

//Old functions before I found out about wrapper functions.
//#define XorStr( s ) ( XorCompileTime::XorString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char >( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ).decrypt() )
//#define XorStrW( s ) ( XorCompileTime::XorString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t >( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ).decrypt() )

//Wrapper functions to work in all functions below
#define XorStr( s ) []{ constexpr XorCompileTime::XorString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char > expr( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ); return expr; }().decrypt()
#define XorStrW( s ) []{ constexpr XorCompileTime::XorString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t > expr( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ); return expr; }().decrypt()

END_NAMESPACE
SSpoke
  • 5,656
  • 10
  • 72
  • 124
  • 3
    There is no guarantee that the string will be "encrypted". Compiler may encrypt it in run-time, or remove this encrypt().decrypt() because effectively it's a no-op. – Abyx Aug 23 '17 at 16:44
8

This blog provides a solution for compile time string hashing in C++. I guess the principle is the same. Unfortunately You have to create a unique macro for each string length.

Amir
  • 3
  • 5
Constantinius
  • 34,183
  • 8
  • 77
  • 85
  • Thank you, I still wait a bit maybe there is a solution that also works for dynamic lengths. – Listing Sep 01 '11 at 12:51
  • Interesting blog entry. But it's quite complex and not really user friendly :) I prefer using a parser ... See my answer – neuro Sep 01 '11 at 12:55
  • Also the provided code hashes the string and doesn't encrypt it. I am looking for a method to encrypt it. – Listing Sep 01 '11 at 12:55
  • Its source code demonstrating a concept. It is not meant to be user friendly. Of course you prefer your own solution, what else would be the point of creating an answer. – Constantinius Sep 01 '11 at 12:56
  • 1
    @Listing: No, but the principles would be the same. Instead of hashing the string you have to apply the encryption to it. There are plenty of encryption algorithms out there for you to use. I'm sure you can adapt the example to your needs. – Constantinius Sep 01 '11 at 12:57
  • @Constantinius: Just talking. I mean it when I say it is interesting. I'm not comparing solutions as the choice depends on what is really needed. – neuro Sep 01 '11 at 13:02
  • I agree with @Constantinius, I don't think the preprocessor is powerful enough to cope with dynamic string lengths. Does anybody know if the preprocessor can compute the string length of fixed string? If so you can simply create, say, a thousand simple macros of the form `CRYPTnnn` that can cope with each of a thousand different string lengths and include this file in every .cpp file that needs compile time string encryption. It would slightly slow compilation but the generated code would be unaffected. – President James K. Polk Sep 01 '11 at 13:09
  • @GregS: It cannot. The preprocessor *does not* break tokens (it can catenate them) and a string is a single token as far as it is concerned. – Matthieu M. Sep 01 '11 at 13:16
  • 1
    The blog only demonstrates how the *optimizer* can fold an expression into a constant. But the computed hash value is not a compile time constant (which could be used a template non-type parameter etc.). As a consequence, it is still up to the optimizer/compiler if the input string shows up in the data segment or not. If I understand the OP right, then he wants a method which guarantees that his sensitive input strings can not show up in the binary. – Nordic Mainframe Sep 01 '11 at 14:05
  • 2
    @Listing: in C++11 and C++14, using `constexpr` methods would allow you to use regular methods for this; there are some constraints on what they can do, but encryption (without random values) should be within their reach. – Matthieu M. Apr 17 '14 at 13:46
  • @MatthieuM. thanks for the update, too bad that "constexpr" is currently not supported in my version of Visual Studio (which I use for development). – Listing Apr 17 '14 at 17:35
6

My preferred solution:

// some header
extern char const* const MyString;

// some generated source
char const* const MyString = "aioghaiogeubeisbnuvs";

And then use your favorite scripting language to generate this one source file where you store the "encrypted" resources.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
5

This is a late answer, but I'm sure there's a better solution.

Plese refer to the accepted answer here.

Basically, it shows how to use the ADVobfuscator lib to obfuscate strings as simple as:

#include "MetaString.h"

using namespace std;
using namespace andrivet::ADVobfuscator;

void Example()
{
    /* Example 1 */

    // here, the string is compiled in an obfuscated form, and
    // it's only deobfuscated at runtime, at the very moment of its use
    cout << OBFUSCATED("Now you see me") << endl;

    /* Example 2 */

    // here, we store the obfuscated string into an object to
    // deobfuscate whenever we need to
    auto narrator = DEF_OBFUSCATED("Tyler Durden");

    // note: although the function is named `decrypt()`, it's still deobfuscation
    cout << narrator.decrypt() << endl;
}
karliwson
  • 3,365
  • 1
  • 24
  • 46
1

If people are interrested by simple string encryption. I wrote a code sample describing string self decryption and tagging using a MACRO. An external cryptor code is provided to patch the binary (so the strings are crypted after program compilation). The strings are decrypted one at a time in memory.

http://www.sevagas.com/?String-encryption-using-macro-and

This will not prevent reverser with a debugger from eventually finding the strings but it will prevent strings listing from executable and memory dump.

sio
  • 11
  • 1
1

I cannot compile that, the compiler throws countless errors, I was looking for other solutions for fast string encryption and found out about this little toy https://www.stringencrypt.com (wasn't hard, 1st result in Google for string encryption keyword).

This is how it works:

  1. You enter the label name say sString
  2. You enter the string contents
  3. You click Encrypt
  4. It takes the string and encrypts it
  5. It generates decryption source code in C++ (many other language are supported)
  6. You paste this snippet in your code

Example string "StackOverflow is awesome!", the output code (every single time it generates slightly different code). It supports both ANSI (char) and UNICODE (wchar_t) type strings

ANSI output:

// encrypted with https://www.stringencrypt.com (v1.1.0) [C/C++]
// sString = "StackOverflow is awesome!"
unsigned char sString[26] = { 0xE3, 0x84, 0x2D, 0x08, 0xDF, 0x6E, 0x0B, 0x87,
                              0x51, 0xCF, 0xA2, 0x07, 0xDE, 0xCF, 0xBF, 0x73,
                              0x1C, 0xFC, 0xA7, 0x32, 0x7D, 0x64, 0xCE, 0xBD,
                              0x25, 0xD8 };

for (unsigned int bIFLw = 0, YivfL = 0; bIFLw < 26; bIFLw++)
{
        YivfL = sString[bIFLw];
        YivfL -= bIFLw;
        YivfL = ~YivfL;
        YivfL = (((YivfL & 0xFF) >> 7) | (YivfL << 1)) & 0xFF;
        YivfL ++;
        YivfL ^= 0x67;
        YivfL = (((YivfL & 0xFF) >> 6) | (YivfL << 2)) & 0xFF;
        YivfL += bIFLw;
        YivfL += 0x0F;
        YivfL = ((YivfL << 4) | ( (YivfL & 0xFF) >> 4)) & 0xFF;
        YivfL ^= 0xDA;
        YivfL += bIFLw;
        YivfL ++;
        sString[bIFLw] = YivfL;
}

printf(sString);

UNICODE output:

// encrypted with https://www.stringencrypt.com (v1.1.0) [C/C++]
// sString = "StackOverflow is awesome!"
wchar_t sString[26] = { 0x13A6, 0xA326, 0x9AA6, 0x5AA5, 0xEBA7, 0x9EA7, 0x6F27, 0x55A6,
                        0xEB24, 0xD624, 0x9824, 0x58A3, 0x19A5, 0xBD25, 0x62A5, 0x56A4,
                        0xFC2A, 0xC9AA, 0x93AA, 0x49A9, 0xDFAB, 0x9EAB, 0x9CAB, 0x45AA,
                        0x23CE, 0x614F };

for (unsigned int JCBjr = 0, XNEPI = 0; JCBjr < 26; JCBjr++)
{
        XNEPI = sString[JCBjr];
        XNEPI -= 0x5D75;
        XNEPI = (((XNEPI & 0xFFFF) >> 14) | (XNEPI << 2)) & 0xFFFF;
        XNEPI = ~XNEPI;
        XNEPI -= 0xA6E5;
        XNEPI ^= JCBjr;
        XNEPI = (((XNEPI & 0xFFFF) >> 10) | (XNEPI << 6)) & 0xFFFF;
        XNEPI --;
        XNEPI ^= 0x9536;
        XNEPI += JCBjr;
        XNEPI = ((XNEPI << 1) | ( (XNEPI & 0xFFFF) >> 15)) & 0xFFFF;
        sString[JCBjr] = XNEPI;
}

wprintf(sString);

Now I know it might not be the perfect solution, but it works for me and my compiler.

1

I think you have to do something like what is done when using gettext (i18n) :

  • use a macro like your CRYPT.
  • use a parser that will crypt string when it finds CRYPT.
  • write a function that decrypt, called by your macro.

For gettext, you use the _() macro that is used to generate the i18ned string dictionnary and that call the gettext function.

By the way, you have to manage i18n too :), you will need something like :

_CRYPT_()
_CRYPT_I18N_()

You have to manage that with your build system to make it maintenable. I do that with gettext...

My 2 cents

neuro
  • 14,948
  • 3
  • 36
  • 59
  • So you think it is not possible using ordinary c++ defines with the standard parser? – Listing Sep 01 '11 at 12:56
  • @Listing: I don't think so, but I have not put much on this. What I know is that internationalization is done this way. I suppose that if someone has found a way to do that with the preprocessor it would have been used ... But you never know :) – neuro Sep 01 '11 at 14:07
1

if you are willing to use C++11 features, variadic templates can be used to do compile-time encryption of variable length strings, an example would be this.

Also see this, which you might find better explained.

Community
  • 1
  • 1
Necrolis
  • 25,836
  • 3
  • 63
  • 101
  • 1
    unfortunately awful, you can group characters somewhat taking advantage of multibytes characters, and thus group them 4 at a time. – Matthieu M. Sep 01 '11 at 13:17
  • @Matthieu: I actually agree, though tbh, compile time encryption of strings is a waste of time (hashing is a little different), as anyone worth their salt can decrypt it once off and use the RVA to link the two etc. – Necrolis Sep 01 '11 at 13:25
  • I agree, encryption is basically useless here. – Matthieu M. Sep 01 '11 at 13:49