Статья Прячем строки в программах. 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
 

pp11

Green Team
16.09.2018
201
82
BIT
1
Жду ваши мнения, критику, дополнения.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
320
1 430
BIT
588
Жду ваши мнения, критику, дополнения.
ну если дополнения, то я бы упомянул такие вещи..

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

pp11

Green Team
16.09.2018
201
82
BIT
1
ну если дополнения, то я бы упомянул такие вещи..

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 был показан в качестве примера. Не в коем случае не рекомендую его для шифрования важных данных.
Мысль в моей статье было такая: не хранить данные в открытом виде.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
320
1 430
BIT
588
Не отрицаю, XOR был показан в качестве примера.
..можно и хор, только чтобы ключ не светился в открытом виде. Но куда его спрятать? Да хоть прямо в саму строку. Например считаем контрольную сумму строки, и используем эту сумму как ключ. Так мы убиваем сразу два зайца - нет ключа в явном виде, и предотвращаем модификацию данных.
 
  • Нравится
Реакции: mrYODA и pp11

SISHA256

One Level
08.06.2019
8
17
BIT
0
Не, ну этот метод сам по себе напрашивается. Если пошла такая "пьянка", то можно использовать библиотечку "Crypto++". Алгоритм шифрования - BlowFish, так как он легкий и не требует серьезных вычислительных мощностей. Строк у нас много, поэтому это важно :)
 
  • Нравится
Реакции: pp11

Hardreversengineer

Green Team
20.08.2019
49
28
BIT
14
Меня радует как быстро народ с форума XSS(домен додумаете сами) ворует статьи отсюда.
Хайд бы чтоли поставили
 

h4x0r

Member
11.03.2019
11
-11
BIT
0
То есть брутфорс тебя не смущает? XOR ключи, даже самые сложные брутятся за минуту максимум.
 
  • Нравится
  • Не нравится
Реакции: Crazy Jack и pp11

pp11

Green Team
16.09.2018
201
82
BIT
1
То есть брутфорс тебя не смущает? XOR ключи, даже самые сложные брутятся за минуту максимум.
Смотри внимательнее тему и комментарии. Потом проанализируй прочтенное и пиши коммент. Выглядишь глупо.
 
Последнее редактирование:

Sombrero

Green Team
23.09.2019
67
4
BIT
0
Не совсем понятно как это должно работать для , допустим , 500 строк ?
 

Optimus1

New member
13.11.2020
2
0
BIT
0
ну если дополнения, то я бы упомянул такие вещи..

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

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

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

Marylin

Mod.Assembler
Red Team
05.06.2019
320
1 430
BIT
588
ведь, когда произойдет расшифровка строки, то она в расшифрованном виде уже будет находится в оперативной памяти в открытом виде. Что конкретно шифруется после ?
Суть динамического шифрования в том, что если имеются несколько строк "str1, str2, str3", которые выводятся в разный момент времени, то все они постоянно лежат в памяти в зашифрованном виде - снятие дампа в этом случае не помогает. Теперь, когда нужно будет вывести на экран "str1", процедура вывода динамически расшифровывает только "str1", не трогая остальных. После вывода, эта-же процедура опять криптует "str1" в памяти, и аналогично с остальными строками.

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

Optimus1

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


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

Обучение наступательной кибербезопасности в игровой форме. Начать игру!