Статья Прячем строки в программах. C++

Доброго времени суток!

Сегодня речь пойдет о шифровке данных внутри программы и будет показан простой пример.

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

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

Могу привести пример - вирусописание. Зачастую у неопытных малварь-писателей в билдах остаётся информация, по которой можно выйти на них.

Помимо метаданных, которые некоторые компиляторы оставляют, есть еще и строки. Ссылки, пути, другая информация.

Сразу скажу, этот метод защитит от поверхностного анализа файла. То есть, если пользователь целенаправленно ищет слово "remote" или "rms", то он его не найдет.

И так приступим. Я буду проводить тест на Kali Linux 2019.2
Не обращайте внимание на Linux, под Windows есть подобные программы.

Что нам понадобится?
  1. Начальное знание C++ (так как тесты я как раз на нем буду проводить)
  2. Утилита "strings". ( Strings - *nix-утилита, применяемая для поиска печатных строк в двоичных файлах )
  3. Компилятор C++.
Начинаем.
Давайте для начала посмотрим, что мы имеем, когда создаем билд с обычной строкой.

Пишем код:
C++:
#include <string.h> //библиотека для работы с классом std::string
int main(){ //функция входа
    std::string mystr = "abcd123"; //Строка со значением "abcd123". Эту строку мы будем искать, ее нужно будет скрыть.
    //Заметьте мы просто вставляем строку в ячейку памяти. Мы не выводим ее.
    return 0; //Успешно завершаем выполнение
}
Компилируем:
Bash:
mkdir builds  # Папка для билдов
c++ uncrypted.cpp -o builds/uncrypted  # В моем случае я использую компилятор "c++", указываю файл исходного кода uncrypted.cpp и сохраняю в builds под именем uncrypted
Смотрим строки:
Bash:
strings uncrypted
И видим нашу строку:

1.png


Она написана открытым текстом. Что же я предлагаю сделать? Я предлагаю банально зашифровать строку при помощи XOR. (XOR берется в качестве примера)

Напишем наш криптор строк:
C++:
#include <iostream> // потоки ввода/вывода
#include <string.h> // библиотека для работы с классом std::string
int main(){ //функция входа
std::string mystr = "abcd123"; // Строка. Вы можете вводить строку и ключ с помощью потоков ввода, делайте на своё усмотрение.
std::string crypted = ""; // Переменная для сохранение зашифрованной строки

for(int i = 0; i < strlen(mystr.c_str()); i++) // Создаем цикл для шифровки каждого символа
crypted += mystr[i] ^ [B]2[/B]; //проводим операцию xor с ключом 2

std::cout << crypted << std::endl; // выводим результат

return 0; //Успешно завершаем выполнение.
}
Компилируем:
c++ crypter.cpp -o builds/cryptor
Запускаем:
./builds/cryptor
Видим, что совершенно понятный нам текст превратился в не понятный набор символов.
Код:
c`af301
Согласитесь, это выглядит куда загадочнее и не понятнее чем наш текст.

Ну и теперь напишем основную программу для теста:
C++:
#include <iostream> //потоки ввода/вывода
#include <string.h> // std::string

int main(){
    std::string mystr = "c`af301"; //Зашифрованный текст
    std::string a = ""; //Строка под расшифрованный текст

    for(int i = 0; i < strlen(mystr.c_str()); i++) //Запускаем цикл для каждого символа
    a += mystr[i] ^ 2; // Расшифровываем и заполянем переменную.
    std::cout << a << std::endl; //Выводим
    return 0; //Успешное завершение.
}
Заметьте, ключи при шифрование и расшифровке должны быть одинаковы. Если разные - результат будет другой.

Компилируем:
c++ crypted.cpp -o builds/crypted

Запускаем:
./builds/crypted
Видим правильный вывод строки.

Теперь заглянем внутрь файла.
strings builds/crypted
2.png


Видим только зашифрованный текст.

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

Всем спасибо за внимание, пишите свои мнения и критику.

Жду PE криптор с использованием XOR xD
 
Жду ваши мнения, критику, дополнения.
 
Жду ваши мнения, критику, дополнения.
ну если дополнения, то я бы упомянул такие вещи..

1. Ключ шифрования для операции XOR.
Меняются только те биты двоичного числа, которые в маске имеют значение(1). То-есть, если ключ как в примере выше =2, то изменится всего 1 бит в оригинальном байте, т.к. 02 = 0000.0010. У однобайтного ключа рассеиваемость должна быть намного больше - минимум 4 бита из 8-ми, например 0110.1011 = 0x6b, или что-то в этом роде.

2. Длинна ключа для операции XOR.
1-байтный ключ имеет всего 255 возможных вариантов, и подобрать его перебором не составит труда. А вот 2-байтный ключ имеет уже 65'535 вариантов, 4-байтный вообще 4'294'967'295 и т.д. Плюс не забываем про рассеиваемость единиц в нём.

3. Интересной особенность операции XOR является то, что если текст везде ксорится одним ключом, то по исходному и зашифрованному тексту можно найти ключ. То-есть:

C-подобный:
A xor 1234 = C
С xor 1234 = A
--------------
С xor A = 1234

Поэтому шифровать строки можно/нужно разными ключами, чтобы по одной строке взломщик не смог расшифровать другие. В этом случае, помогает обычный инкремент ключа на каждом байте. Например, чтобы зашифровать строку "HELLO" выбираем стартовый ключ(0x77) и шифруем: "H"=0x77, "E"=0х78, "L"=0х79, "L"=0х7a, "O"=0х7b, и т.д. Такая обычная смена алгоритма уже не позволит вычислить ключ по С xor A = 1234

4. А вообще, шифровать нужно блочным шифром, например 512 или 1К-битным ключом (сразу по 64/128 символов соответственно). Не знаю как в никсах, а винда имеет специальные криптографические API-функции типа CryptGenRandom() из Advapi32.dll и ей подобных. Эти функции позволяют задавать длину ключа в аргументах. Теоритически, такие строки нельзя будет расшифровать вообще, или потребуется минимум 1000 и одна ночь. Если мы шифруем не свои рекорды в косынке или солитёр, а например номера банковских карт, то лучше использовать эти API.

5. Хорошим тоном является "динамическое шифрование" в программах, когда перед вывод строки на экран мы её расшифровываем, и после - опять шифруем. Иначе, после загрузки программы в память, можно просто сдампить в файл секцию, и получим все строки в явном виде. В вашем-же случае, строки остаются закодированы только на диске, а в памяти - всё в стиле "ню"
 
ну если дополнения, то я бы упомянул такие вещи..

1. Ключ шифрования для операции XOR.
Меняются только те биты двоичного числа, которые в маске имеют значение(1). То-есть, если ключ как в примере выше =2, то изменится всего 1 бит в оригинальном байте, т.к. 02 = 0000.0010. У однобайтного ключа рассеиваемость должна быть намного больше - минимум 4 бита из 8-ми, например 0110.1011 = 0x6b, или что-то в этом роде.

2. Длинна ключа для операции XOR.
1-байтный ключ имеет всего 255 возможных вариантов, и подобрать его перебором не составит труда. А вот 2-байтный ключ имеет уже 65'535 вариантов, 4-байтный вообще 4'294'967'295 и т.д. Плюс не забываем про рассеиваемость единиц в нём.

3. Интересной особенность операции XOR является то, что если текст везде ксорится одним ключом, то по исходному и зашифрованному тексту можно найти ключ. То-есть:

C-подобный:
A xor 1234 = C
С xor 1234 = A
--------------
С xor A = 1234

Поэтому шифровать строки можно/нужно разными ключами, чтобы по одной строке взломщик не смог расшифровать другие. В этом случае, помогает обычный инкремент ключа на каждом байте. Например, чтобы зашифровать строку "HELLO" выбираем стартовый ключ(0x77) и шифруем: "H"=0x77, "E"=0х78, "L"=0х79, "L"=0х7a, "O"=0х7b, и т.д. Такая обычная смена алгоритма уже не позволит вычислить ключ по С xor A = 1234

4. А вообще, шифровать нужно блочным шифром, например 512 или 1К-битным ключом (сразу по 64/128 символов соответственно). Не знаю как в никсах, а винда имеет специальные криптографические API-функции типа CryptGenRandom() из Advapi32.dll и ей подобных. Эти функции позволяют задавать длину ключа в аргументах. Теоритически, такие строки нельзя будет расшифровать вообще, или потребуется минимум 1000 и одна ночь. Если мы шифруем не свои рекорды в косынке или солитёр, а например номера банковских карт, то лучше использовать эти API.

5. Хорошим тоном является "динамическое шифрование" в программах, когда перед вывод строки на экран мы её расшифровываем, и после - опять шифруем. Иначе, после загрузки программы в память, можно просто сдампить в файл секцию, и получим все строки в явном виде. В вашем-же случае, строки остаются закодированы только на диске, а в памяти - всё в стиле "ню"
Спасибо за дополнение. Особенно был полезен 5 пункт, как-то не подумал про дамп памяти.

. Если мы шифруем не свои рекорды в косынке или солитёр, а например номера банковских карт, то лучше использовать эти API
Не отрицаю, XOR был показан в качестве примера. Не в коем случае не рекомендую его для шифрования важных данных.
Мысль в моей статье было такая: не хранить данные в открытом виде.
 
Не отрицаю, XOR был показан в качестве примера.
..можно и хор, только чтобы ключ не светился в открытом виде. Но куда его спрятать? Да хоть прямо в саму строку. Например считаем контрольную сумму строки, и используем эту сумму как ключ. Так мы убиваем сразу два зайца - нет ключа в явном виде, и предотвращаем модификацию данных.
 
  • Нравится
Реакции: mrYODA и pp11
Не, ну этот метод сам по себе напрашивается. Если пошла такая "пьянка", то можно использовать библиотечку "Crypto++". Алгоритм шифрования - BlowFish, так как он легкий и не требует серьезных вычислительных мощностей. Строк у нас много, поэтому это важно :)
 
  • Нравится
Реакции: pp11
Меня радует как быстро народ с форума XSS(домен додумаете сами) ворует статьи отсюда.
Хайд бы чтоли поставили
 
  • Нравится
Реакции: Hardreversengineer
То есть брутфорс тебя не смущает? XOR ключи, даже самые сложные брутятся за минуту максимум.
 
  • Нравится
Реакции: Crazy Jack
То есть брутфорс тебя не смущает? XOR ключи, даже самые сложные брутятся за минуту максимум.
Смотри внимательнее тему и комментарии. Потом проанализируй прочтенное и пиши коммент. Выглядишь глупо.
 
Последнее редактирование:
Не совсем понятно как это должно работать для , допустим , 500 строк ?
 
ну если дополнения, то я бы упомянул такие вещи..

5. Хорошим тоном является "динамическое шифрование" в программах, когда перед вывод строки на экран мы её расшифровываем, и после - опять шифруем. Иначе, после загрузки программы в память, можно просто сдампить в файл секцию, и получим все строки в явном виде. В вашем-же случае, строки остаются закодированы только на диске, а в памяти - всё в стиле "ню"

Здравствуйте,

Подскажите пожалуйста, неМного не понятен этот момент, ведь, когда произойдет расшифровка строки, то она в расшифрованном виде уже будет находится в оперативной памяти в открытом виде. Что конкретно шифруется после ?
 
ведь, когда произойдет расшифровка строки, то она в расшифрованном виде уже будет находится в оперативной памяти в открытом виде. Что конкретно шифруется после ?
Суть динамического шифрования в том, что если имеются несколько строк "str1, str2, str3", которые выводятся в разный момент времени, то все они постоянно лежат в памяти в зашифрованном виде - снятие дампа в этом случае не помогает. Теперь, когда нужно будет вывести на экран "str1", процедура вывода динамически расшифровывает только "str1", не трогая остальных. После вывода, эта-же процедура опять криптует "str1" в памяти, и аналогично с остальными строками.

На программном уровне реализуется это просто, зато даёт хороший эффект - взломщику приходится снимать уже не один, а три дампа памяти, отдельно для каждой из строк. По большому счёту, динамически шифруют обычно не строки, а целые блоки кода (функции или процедуры), тогда профит становится очевидным.
 
  • Нравится
Реакции: Hardreversengineer
Извините, за непонятливость, объясните пожалуйста:
После вывода, эта-же процедура опять криптует "str1" в памяти, и аналогично с остальными строками.
После запуска программы - в оперативную память погрузился сам исполняемый код и все константные переменные и константные строки в стек. Строки соответственно на стеке находятся в зашифрованном виде.
Когда пришло время использовать строку "str1" - код ее расшифровывает и помещает в переменную уже в динамической(куче) памяти в расшифрованном виде - взломщик снимает дамп и видит текст расшифрованным.
Не совмем понимаю, что знчит "опять криптует" ? Опять криптует что ? Расшифрованный текст в динамической памяти или перекриптовывает первоначально зашифрованный текст на стеке ?


По большому счёту, динамически шифруют обычно не строки, а целые блоки кода (функции или процедуры), тогда профит становится очевидным.
Если, как зашифровать статический текст отдаленно, но понятно, то как зашифровать функцию, прям даже отдаленно не понятно.
 
есть ли подобная утилита для c#?
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab