18

I want to hide some strings in my .exe so people can't simply just open the .exe and look at all the strings there. I don't care about the strength of the encrypting method, so I will probably use XOR etc.

How can I do this at compile time? That way my strings won't be stored in the .exe but the encrypted versions would. Then, I would just use my decrypting function every time to display those strings on screen.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Newbie
  • 1,143
  • 5
  • 20
  • 30
  • 1
    Possible duplicate of [Encrypting / obfuscating a string literal at compile-time](https://stackoverflow.com/questions/6934217/encrypting-obfuscating-a-string-literal-at-compile-time) – karliwson Nov 29 '17 at 00:31

10 Answers10

16

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() )
////////////////////////////////////////////////////////////////////

THIS IS A UPDATED CODE BELOW, WORKS on both char and wchar_t thanks to arkan

#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
  • They are several bugs in this code snippet, when the string is too large it does not get encrypted (I can find it in clear in the binary), I will keep investigating though. In addition '''LinearCongruentGenerator''' throws overflow warnings, it needs to be re-wrote with unsigned long long and the modulo operator to clamp over 0xFFFFFFFF – arkan Jul 01 '19 at 07:22
15

you can encrypt it using macros or write your own preprocessor

#define CRYPT8(str) { CRYPT8_(str "\0\0\0\0\0\0\0\0") }
#define CRYPT8_(str) (str)[0] + 1, (str)[1] + 2, (str)[2] + 3, (str)[3] + 4, (str)[4] + 5, (str)[5] + 6, (str)[6] + 7, (str)[7] + 8, '\0'

// calling it
const char str[] = CRYPT8("ntdll");
Yevhen
  • 1,897
  • 2
  • 14
  • 25
  • so this macro can only encrypt as many letters as i repeat the marco code there? :D argh.. well its all i got, better use it then., – Newbie Nov 05 '10 at 12:54
  • is there some way to use a predefined key there, instead of manually typing +1 +2 +3... ? also without revealing the key in the exe... – Newbie Nov 05 '10 at 13:07
  • you will get encrypted string in your .exe file there will not be this encryption algorithm. – Yevhen Nov 06 '10 at 15:35
  • 1
    (str)[0] if you write macros it's variables and parameters are put into brackets. because in some cases you can get other thing that you want. – Yevhen Nov 06 '10 at 15:37
  • @Newbie: consider e.g. `CRYPT(myString+12)` – peterchen Jan 07 '11 at 10:42
8

Building on SSPoke's answer here is a slightly simpler and more robust solution. Tested with MSVC 2017 and gcc 7.3 https://godbolt.org/z/7fc3Zi

Changes:

  • Fix integer overflow warning for long strings
  • Fix/Make sure MSVC evaluate encrypt_character() at compile time even for very long strings (in the original version some strings would not get encrypted at compile time)
  • Support for wide character strings
  • Simpler code templates
#include <iostream>

// =============================================================================
namespace crypt {
// =============================================================================

// convert __TIME__ == "hh:mm:ss" to a sum of seconds this gives us a compile-time seed
// Note: in some weird cases I've seen the seed being different from encryption
// to decryption so it's safer to not use time and set the seed manually
#if 0
#define TBX_XSTR_SEED ((__TIME__[7] - '0') * 1ull    + (__TIME__[6] - '0') * 10ull  + \
                       (__TIME__[4] - '0') * 60ull   + (__TIME__[3] - '0') * 600ull + \
                       (__TIME__[1] - '0') * 3600ull + (__TIME__[0] - '0') * 36000ull)
#else
#define TBX_XSTR_SEED (3600ull)
#endif

// -----------------------------------------------------------------------------

// @return a pseudo random number clamped at 0xFFFFFFFF
constexpr unsigned long long linear_congruent_generator(unsigned rounds) {
    return 1013904223ull + (1664525ull * ((rounds> 0) ? linear_congruent_generator(rounds - 1) : (TBX_XSTR_SEED) )) % 0xFFFFFFFF;
}

// -----------------------------------------------------------------------------

#define Random() linear_congruent_generator(10)
#define XSTR_RANDOM_NUMBER(Min, Max) (Min + (Random() % (Max - Min + 1)))

// -----------------------------------------------------------------------------

constexpr const unsigned long long XORKEY = XSTR_RANDOM_NUMBER(0, 0xFF);

// -----------------------------------------------------------------------------

template<typename Char >
constexpr Char encrypt_character(const Char character, int index) {
    return character ^ (static_cast<Char>(XORKEY) + index);
}

// -----------------------------------------------------------------------------

template <unsigned size, typename Char>
class Xor_string {
public:
    const unsigned _nb_chars = (size - 1);
    Char _string[size];

    // if every goes alright this constructor should be executed at compile time
    inline constexpr Xor_string(const Char* string)
        : _string{}
    {
        for(unsigned i = 0u; i < size; ++i)
            _string[i] = encrypt_character<Char>(string[i], i);
    }

    // This is executed at runtime.
    // HACK: although decrypt() is const we modify '_string' in place
    const Char* decrypt() const
    {
        Char* string = const_cast<Char*>(_string);
        for(unsigned t = 0; t < _nb_chars; t++) {
            string[t] = string[t] ^ (static_cast<Char>(XORKEY) + t);
        }
        string[_nb_chars] = '\0';
        return string;
    }

};

}// END crypt NAMESPACE ========================================================

#define XorS(name, my_string)    constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(char)), char> name(my_string)
// Because of a limitation/bug in msvc 2017 we need to declare crypt::Xor_string() as a constexpr 
// otherwise the constructor is not evaluated at compile time. The lambda function is here to allow this declaration inside the macro
// because there is no such thing as casting to 'constexpr' (and casting to const does not solve this bug).
#define XorString(my_string) []{ constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(char)), char> expr(my_string); return expr; }().decrypt()

// Crypt normal string char*
#define _c( string ) XorString( string )

#define XorWS(name, my_string)       constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(wchar_t)), wchar_t> name(my_string)
#define XorWideString(my_string) []{ constexpr crypt::Xor_string<(sizeof(my_string)/sizeof(wchar_t)), wchar_t> expr(my_string); return expr; }().decrypt()

// crypt  wide characters
#define _cw( string ) XorWideString( string )


int main(void ) {

    std::cout << _c("0obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze0\n"
                    "1obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze1\n"
                    "2obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze2\n"
                    "3obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze3\n"
                    "4obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze4\n"
                    "5obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze5\n"
                    "6obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze6\n"
                    "7obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze7\n"
                    "8obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze8\n"
                    "9obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze9\n" ) << std::endl;

    std::cout << "Wide strings" << std::endl;

    std::wcout << _cw(L"0obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze0\n"
                       "1obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze1\n"
                       "2obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze2\n"
                       "3obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze3\n"
                       "4obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze4\n"
                       "5obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze5\n"
                       "6obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze6\n"
                       "7obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze7\n"
                       "8obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze8\n"
                       "9obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzze obfuscate me pleazzzzzzeobfuscate me pleazzzzzze obfuscate me pleazzzzzze9\n")  << std::endl;

    return 0;
}

BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185
arkan
  • 610
  • 6
  • 13
4

This probably doesn't apply to the ancient compiler of the question, but on more modern C++ implementations, we can use a string literal operator template that's declared constexpr to implement compile-time obfuscation. For this, I've used GCC 7.2.0 with -std=c++17 (and a full set of warning options, of course).

Firstly, we define a type to hold our obfuscated string data, with a conversion operator to produce a plaintext string on demand:

#include <array>
#include <string>

template<typename Char>
static const Char SECRET = 0x01;

template<typename Char,
         typename std::basic_string<Char>::size_type Length>
struct obfuscated_string
{
    using String = std::basic_string<Char>;

    const std::array<const Char, Length> storage;

    operator String() const
    {
        String s{storage.data(), Length};
        for (auto& c: s)
            c ^= SECRET<Char>;
        return s;
    }
};

Now, the literal operator template to convert a source-code literal to an obfuscated string:

template<typename ctype, ctype...STR>
constexpr obfuscated_string<ctype, sizeof... (STR)> operator ""_hidden()
{
    return { { (STR ^ SECRET<ctype>)... } };
}

To demonstrate:

#include <iostream>
int main()
{
    static const auto message = "squeamish ossifrage"_hidden;
    std::string plaintext = message;
    std::cout << plaintext << std::endl;
}

We can inspect the object code with the strings program. The binary doesn't contain squeamish ossifrage anywhere; instead it has rptd`lhri!nrrhgs`fd. I've confirmed this with a range of optimisation levels to demonstrate that the conversion back to std::string doesn't get pre-computed, but I advise you conduct your own tests whenever you change compiler and/or settings.

(I'm deliberately ignoring whether this is an advisable thing to do - merely presenting a technical working solution).

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • 2
    With `-O3 -std:c++20`, GCC has [reached the point](https://godbolt.org/z/Tcb5vs) where encryption and decryption are optimized away, mainly due to C++20’s [constexpr containers](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0784r7.html). It still places the string in register constants instead of laying it out linearly in memory, so it is chopped into 8-B pieces, but short strings are now already readable. – Krishty Dec 08 '20 at 07:31
1

About the only way to do exactly what you suggest is to write a truly horrible macro. But here are some alternatives.

  1. Store the encrypted strings in a data file.
  2. Collect the strings in a single source file, then in the build, before actually compiling, go over it with a tool that will encrypt them (e.g. sed). You can automate this step.
  3. Use a powerful editor so that you can encrypt/decrypt the strings effortlessly, while you work.
Beta
  • 96,650
  • 16
  • 149
  • 150
0

You can't encrypt strings (string literals) by С++ compiler or preprocessor, but you can write a pre-build tool which will parse your source code, and encrypt strings.

Or, you can try to use boost::mpl::string.

Abyx
  • 12,345
  • 5
  • 44
  • 76
0

No matter the details of your solution it will involve encrypting the strings using some encryption program.

You might write a script to encrypt literals in your source code.

Or for a Windows exe you might encrypt literals in an [.rc] file, embedding the strings as a string table resource in the exe.

But probably the best solution is to not try any hiding.

When Microsoft tried the XOR hiding trick on the Windows code that arbitrarily warned about non-Microsoft DOS-es being unreliable and incompatible with this and that, it just backfired on them. Of course the idea of saying bad things about competitors and bundling that bad-saying with Windows was a Really Stupid Idea in the first place. But, trying to hide the code was what made it into a public embarrassment: nobody had really noticed the warnings the code produced, but when people discovered "encrypted" code, naturally their curiosity was engaged, they just had to find out what it was, and write articles about it.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • The article *[Examining the Windows AARD Detection Code](http://fringe.davesource.com/Fringe/NonZen_Companies/Microsoft/Tactics/1993.09.01.Locks_Out_DrDOS.html)* from the Sept. 1993 Dr. Dobbs Journal is still a good read. – Stephen P Nov 05 '10 at 00:03
  • Whoever downvoted, please explain why so others can learn what's incorrect with the answer, or with the downvote. :-) – Cheers and hth. - Alf Nov 06 '10 at 22:46
0

If you are only trying to hide the strings, then you could just try compressing your executable with something like UPX.

gwell
  • 2,695
  • 20
  • 20
  • no, that doesnt hide anything, anyone could just uncompress it... and besides, i dont want a slowdown to the loading time. not to mention 7zip is way better than zip... – Newbie Nov 05 '10 at 12:47
  • Actually UPX supports 7zip compression. Indeed load time may suffer, however, built-in encryption of strings is not much stronger since the encryption algorithm is embedded in the binary. – arkan Apr 16 '19 at 04:52
  • UPX and derivaties are post-compile time tools. The OP is asking about compile time. I won't even go into other things like how bad UPX is. – Mecanik Jul 01 '20 at 12:07
0

Here is what I currently use it has hacks to support sprintf functions which spilled plain-text in compiled binary file. You could now use w_sprintf_s instead of sprintf, like so

char test[256] = { 0 };
w_sprintf_s(test, 256, XorStr("test test :D %d %+d\n"), 1, 1337);

or

wchar_t test[256] = { 0 };
w_sprintf_s(test, 256, XorStrW(L"test test from unicode :D %d %+d\n"), 1, 1337);


May add support for wchar_t wide strings like arkan did.. but I have no use for them right now as I don't write anything in symbols / unicode.

#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
-1

Any crypto that is done at compile time must also be undoable in the raw EXE (unless you're doing something Really Strange). And your app is running on their hardware. Doomed... barring DRM, which is (in my mind) Evil.

Mark Storer
  • 15,672
  • 3
  • 42
  • 80
  • 2
    yeah of course someone could decrypt it, but that is very unlikely they find the correct key or even the correct data. im just trying to avoid some n00bs looking at the exe :P sure everything can be cracked... im not trying to make it uncrackable. – Newbie Nov 05 '10 at 12:50