Шифрование / запутывание строкового литерала во время компиляции - PullRequest
14 голосов
/ 04 августа 2011

Я хочу зашифровать / кодировать строку во время компиляции, чтобы исходная строка не появлялась в скомпилированном исполняемом файле.

Я видел несколько примеров, но они не могут принимать строковый литерал в качестве аргумента. Смотрите следующий пример:

template<char c> struct add_three {
    enum { value = c+3 };
};

template <char... Chars> struct EncryptCharsA {
    static const char value[sizeof...(Chars) + 1];
};

template<char... Chars>
char const EncryptCharsA<Chars...>::value[sizeof...(Chars) + 1] = {
    add_three<Chars>::value...
};

int main() {   
    std::cout << EncryptCharsA<'A','B','C'>::value << std::endl;
    // prints "DEF"
}

Я не хочу предоставлять каждый символ отдельно, как он. Моя цель - передать строковый литерал следующим образом:

EncryptString<"String to encrypt">::value

Есть также несколько примеров, подобных этому:

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

Но это ограничивает размер строки.

Есть ли способ добиться того, чего я хочу?

Ответы [ 4 ]

7 голосов
/ 04 августа 2011

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

Итак, вот что вы начинаете:

  1. encrypted_string.cpp и encrypted_string.h (которые являются пустыми)
  2. Сценарий или автономное приложение, которое принимает текстовый файл в качестве входных данных и перезаписывает файлы encrypted_string.cpp и encrypted_string.h

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

4 голосов
/ 14 февраля 2016

Думаю, этот вопрос заслуживает обновленного ответа.

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

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

ADVobfuscator - это библиотека запутывания, созданная Себастьеном Андриветом, которая использует C ++ 11/14 для генерации запутанного кода во время компиляции без использования какого-либо внешнего инструмента, только кода C ++. Нет необходимости создавать дополнительные этапы сборки, просто включите его и используйте. Я не знаю лучшей реализации шифрования / обфускации строк во время компиляции, которая не использует внешние инструменты или этапы сборки. Если вы делаете, пожалуйста, поделитесь.

Он не только запутывает строки, но и имеет другие полезные вещи, такие как FSM времени компиляции ( Finite State Machine ), который может случайным образом запутывать вызовы функций, и генератор псевдослучайных чисел времени компиляции, но это выходит за рамки этого ответа.

Вот простой пример обфускации строки с использованием ADVobfuscator:

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

Вы можете заменить макросы DEF_OBFUSCATED и OBFUSCATED вашими собственными макросами. Eg.:

#define _OBF(s) OBFUSCATED(s)

...

cout << _OBF("klapaucius");

Как это работает?

Если вы посмотрите на определение этих двух макросов в MetaString.h , вы увидите:

#define DEF_OBFUSCATED(str) MetaString<andrivet::ADVobfuscator::MetaRandom<__COUNTER__, 3>::value, andrivet::ADVobfuscator::MetaRandomChar<__COUNTER__>::value, Make_Indexes<sizeof(str) - 1>::type>(str)

#define OBFUSCATED(str) (DEF_OBFUSCATED(str).decrypt())

По сути, существует три различных варианта класса MetaString (ядро обфускации строки). У каждого свой алгоритм обфускации. Один из этих трех вариантов выбирается случайным образом во время компиляции с использованием генератора псевдослучайных чисел библиотеки (MetaRandom) вместе со случайным char, который используется выбранным алгоритмом для xor строковых символов.

"Привет, но если мы выполним математику, 3 алгоритма * 255 возможных ключей символов (0 не используется) = 765 вариантов запутанной строки"

Ты прав. Одну и ту же строку можно запутать только 765 разными способами. Если у вас есть причина для чего-то более безопасного (параноик / ваше приложение требует повышенной безопасности), вы можете расширить библиотеку и реализовать свои собственные алгоритмы, используя более сильную запутывание или даже шифрование ( Криптография White-Box - это в дорожной карте библиотеки).


Где и как хранятся запутанные строки?

Одной вещью, которую я нахожу интересной в этой реализации, является то, что она не хранит запутанную строку в разделе данных исполняемого файла. Вместо этого он статически сохраняется в самом объекте MetaString (в стеке), и алгоритм декодирует его на месте во время выполнения. Такой подход значительно затрудняет поиск запутанных строк, статически или во время выполнения.

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

3 голосов
/ 04 августа 2011

Причина, по которой найденные примеры не могут принимать строковые литералы в качестве аргумента шаблона, заключается в том, что это не разрешено стандартом ISO C ++.Это потому, что, хотя в c ++ есть строковый класс, строковый литерал все еще является const char *.Таким образом, вы не можете или не должны изменять его (приводит к неопределенному поведению), даже если вы можете получить доступ к символам такого строкового литерала времени компиляции.

Единственный способ, которым я вижу, это использованиеопределяет, как они обрабатываются препроцессором перед компилятором.Возможно, в этом случае boost даст вам руку помощи.

2 голосов
/ 04 августа 2011

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

CRYPT(m y _ s t r i n g)

Где _ - это заполнитель для пробельного символа.Ужасно грязно, и я предпочел бы любое другое решение, чем это.

Что-то подобное могло бы сделать это, хотя последовательность Boost.PP не делает ее более красивой.

#include <iostream>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define GARBLE(x) GARBLE_ ## x
#define GARBLE_a x
#define GARBLE_b y
#define GARBLE_c z

#define SEQ (a)(b)(c)
#define MACRO(r, data, elem) BOOST_PP_STRINGIZE(GARBLE(elem))

int main() {
  const char* foo = BOOST_PP_SEQ_FOR_EACH(MACRO, _, SEQ);
  std::cout << foo << std::endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...