С++: как шифровать строки во время компиляции?

Я хочу скрыть некоторые строки в моем .exe, чтобы люди просто не могли просто открыть файл .exe и посмотреть на все строки там. Меня не волнует сила метода шифрования, поэтому я, вероятно, буду использовать XOR и т.д.

Как я могу сделать это во время компиляции? Таким образом, мои строки не будут храниться в .exe, но зашифрованные версии будут. Затем я бы просто использовал свою функцию дешифрования каждый раз, чтобы отобразить эти строки на экране.

Ответ 1

вы можете зашифровать его с помощью макросов или написать собственный препроцессор

#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");

Ответ 2

Я также думал, что это было невозможно, хотя это было очень просто, люди писали решения, в которых вам нужен пользовательский инструмент для последующего сканирования встроенного файла, сканирования строк и шифрования строк, что было неплохо, но я хотел пакет, скомпилированный из Visual Studio, и это возможно сейчас!

Что вам нужно, так это C++ 11 (обновление 1 для Visual Studio 2015 из коробки)

магия происходит с этой новой командой constexpr

По волшебству происходит в этом #define

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

Он не будет расшифровывать XorString во время компиляции, только во время выполнения, но он будет шифровать строку только во время компиляции, поэтому строки не будут отображаться в исполняемом файле.

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

Он выведет "this string is hidden!" но вы не найдете его в исполняемом файле в виде строк! Проверьте сами с Microsoft Sysinternals Strings ссылки для загрузки программы Microsoft Sysinternals Strings: https://technet.microsoft.com/en-us/sysinternals/strings.aspx

Полный исходный код довольно большой, но его легко можно включить в один заголовочный файл. Но также довольно случайным образом, поэтому выходные данные зашифрованной строки всегда будут меняться при каждой новой компиляции, начальное число изменяется в зависимости от времени, которое потребовалось для его компиляции, в значительной степени надежное, идеальное решение.

Создайте файл с именем 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() )
////////////////////////////////////////////////////////////////////

Ответ 3

Это, вероятно, не относится к древнему компилятору вопроса, но для более современных реализаций С++ мы можем использовать шаблон оператора строкового литерала, объявивший constexpr для реализации обфускации времени компиляции. Для этого я использовал GCC 7.2.0 с -std=c++17 (и полный набор параметров предупреждения, конечно).

Во-первых, мы определяем тип для хранения наших обфускационных строковых данных с оператором преобразования для создания строки обычного текста по запросу:

#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;
    }
};

Теперь, буквальный шаблон оператора для преобразования литерала исходного кода в обфускацию строки:

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

Чтобы продемонстрировать:

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

Мы можем проверить объектный код с помощью программы strings. Бинарный файл не содержит squeamish ossifrage где угодно; вместо этого он имеет rptd`lhri!nrrhgs`fd. Я подтвердил это с помощью ряда уровней оптимизации, чтобы продемонстрировать, что преобразование обратно в std::string не получает предварительных вычислений, но я советую вам проводить собственные тесты, когда вы меняете компилятор и/или настройки.

(Я намеренно игнорирую, стоит ли это делать, просто представляя техническое решение).

Ответ 4

О том, как сделать то, что вы предлагаете, - это создать действительно ужасный макрос. Но вот несколько альтернатив.

  • Храните зашифрованные строки в файле данных.
  • Соберите строки в одном исходном файле, а затем в сборке перед собственно компиляцией перейдите к нему с помощью инструмента, который зашифрует их (например, sed). Вы можете автоматизировать этот шаг.
  • Используйте мощный редактор, чтобы вы могли без задержек шифровать/расшифровывать строки.

Ответ 5

Если вы только пытаетесь скрыть строки, вы можете просто попробовать сжать свой исполняемый файл с помощью UPX.

Ответ 6

Вы не можете шифровать строки (строковые литералы) компилятором или препроцессором C++, но вы можете написать инструмент предварительной сборки, который будет анализировать ваш исходный код и шифровать строки.

Или вы можете попробовать использовать boost:: mpl:: string.

Ответ 7

Независимо от деталей вашего решения, он будет включать шифрование строк с использованием какой-либо программы шифрования.

Вы можете написать script для шифрования литералов в исходном коде.

Или для Windows exe вы можете шифровать литералы в файле [.rc], вставляя строки в качестве ресурса таблицы строк в exe.

Но, вероятно, лучшим решением является не пытаться скрывать.

Когда Microsoft попыталась скрыть трюк XOR в коде Windows, который произвольно предупреждал о недопустимости и несовместимости несовместимых с DOS-серверами других компаний, он просто обманул их. Конечно, идея говорить плохие вещи о конкурентах и ​​связывать это плохое высказывание с Windows была в самом деле глупой идеей. Но, пытаясь скрыть код, это превратило его в публичное смущение: никто не замечал предупреждений, полученных кодом, но когда люди открывали "зашифрованный" код, естественно, их любопытство было занято, им просто нужно было выяснить, что это такое, и писать статьи об этом.

Приветствия и hth.,

Ответ 8

Любой криптограмм, который выполняется во время компиляции, также должен быть отменен в необработанном EXE (если вы не делаете что-то действительно странное). И ваше приложение работает на своем оборудовании. Doomed... запрет DRM, который (на мой взгляд) злой.

Ответ 9

Опираясь на ответ SSPoke, это немного более простое и надежное решение. Протестировано с MSVC 2017 и gcc 7.3 https://godbolt.org/z/7fc3Zi

Изменения:

  • Исправлено целочисленное предупреждение о переполнении длинных строк
  • Исправьте/убедитесь, что MSVC оценивает encrypt_character() во время компиляции даже для очень длинных строк (в исходной версии некоторые строки не шифровались во время компиляции)
  • Поддержка широких символьных строк
  • Более простые шаблоны кода
#include <iostream>

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

// compile-time seed
#define 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)

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

// @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) : (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;
}