Обфускация строк на этапе компиляции

Авторы: 
Токарев А.В.

Возник на днях у нас вопрос: «Как спрятать от любителей hex-редаторов строчки текста в скомпилированном приложении?». Но спрятать так, чтобы это не требовало особых усилий, так, между прочим…

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

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

Получившийся небольшой пример:

#include <string>
#include <iostream>

//хранилице зашифрованных строк
template<int SIZE>
struct hidedString
{
    //буффер для зашифрованной строки
     short s[SIZE + 1]; 

     //конструктор для создания объекта на этапе компиляции
     constexpr hidedString():s{0} { } 

     //функция дешифрации в процессе исполнения приложения
     std::string decoder() const
     {
          std::string rv;
          for(int i=0; i<SIZE; i++)
               rv.push_back(s[i] - 1);
          return rv;
     }
}; 

//вычисление размера строки на этапе компиляции
constexpr int sizeCalculate(const char* str)
{
     int cnt = 0;
     while (*str++) cnt++;
     return cnt;
}

//функция шифрации на этапе компиляции
template<int SIZE>
constexpr hidedString<SIZE> encoder(const char* str)
{
     hidedString<SIZE> s;

     for(int i = 0; i < SIZE; i++)
          s.s[i] = str[i] + 1;

     s.s[SIZE] = 0;
     return s;
}

//макрос для удобства использования
#define CRYPTEDSTRING(n,x) constexpr hidedString<sizeCalculate(x)> n = encoder<sizeCalculate(x)>(x)

int main()
{
    CRYPTEDSTRING(str,"Big big secret!");
    //выведем зашифрованную на этапе компиляции строку,
    //если посмотреть содержимое скомпилированного файла,
    //то оригинал там отсутствует
    std::cout << str.decoder();
    return 0;
}

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

ПС. Пример компилировался на Arch Linux с помощью clang 3.5.0 следующей командой:

clang++ -std=c++1y -stdlib=libc++ -lc++abi sample.cpp -o sample