Другое

Как защитить секрет в памяти без доступа к ядру Windows

Практические методы защиты секретных ключей в памяти от атак WinApi-hooking, DLL-инъекций и дампов без привилегий ядра.

Как максимально обезопасить секрет в памяти при разработке чувствительного ПО без доступа к ядру системы?

Я разрабатываю чувствительное программное обеспечение и столкнулся с необходимостью защиты секретного ключа (массива байт) в оперативной памяти. Мне требуется защита от следующих методов атак:

  • WinApi-hooking
  • DLL-инъекции
  • Руткиты
  • Дампы памяти
  • Другие подобные методы

При этом у меня нет доступа к 0-му кольцу (привилегий ядра) и возможности использовать подписанные драйверы.

Основные вопросы:

  1. Существуют ли в Windows встроенные механизмы защиты для разработчиков без прав ядра?
  2. Какие техники можно применить для максимальной защиты секретного ключа в памяти?
  3. Как безопасно использовать ключ, который постоянно меняется и необходим для шифрования данных?

Ищу практические решения, которые можно реализовать без написания собственных драйверов.

Windows предоставляет несколько встроенных механизмов защиты для разработчиков без прав ядра, хотя абсолютная защита в режиме пользователя невозможна. Основные подходы включают использование DPAPI, изоляцию памяти, шифрование runtime и противодействие хукингу API.

Содержание

Встроенные механизмы защиты Windows

Windows предлагает несколько встроенных механизмов защиты, доступных разработчикам без привилегий ядра:

Core Isolation и Memory Integrity являются ключевыми технологиями защиты памяти в современных версиях Windows. Как указано в документации Microsoft, эта функция “предотвращает атаки путем запрета прямого доступа к памяти со стороны внешних устройств, особенно когда компьютер заблокирован или пользователь вышел из системы”.

DPAPI (Data Protection API) предоставляет криптографическую защиту для данных в режиме пользователя. Согласно официальной документации, функция CryptProtectData шифрует данные “с использованием сеансового ключа, который функция создает на учетных данных пользователя”. DPAPI генерирует сильный ключ MasterKey (512 бит случайных данных), который используется для создания фактического симметричного сеансового ключа.

Memory Protection обеспечивает базовую защиту памяти процесса. Как объясняется в документации по Win32, “память, принадлежащая процессу, неявно защищена ее частным виртуальным адресным пространством”.

Техники защиты секретов в памяти

1. Использование DPAPI для шифрования секретов

DPAPI является основным инструментом для защиты секретов в режиме пользователя:

cpp
#include <windows.h>
#include <dpapi.h>

// Пример шифрования данных с помощью DPAPI
BYTE* ProtectData(const BYTE* data, DWORD dataSize, DWORD& outSize) {
    DATA_BLOB inputData = { dataSize, const_cast<BYTE*>(data) };
    DATA_BLOB outputData;
    
    if (CryptProtectData(&inputData, NULL, NULL, NULL, NULL, 0, &outputData)) {
        outSize = outputData.cbData;
        return outputData.pbData;
    }
    return nullptr;
}

Преимущества:

  • Интеграция с Windows AuthN
  • Автоматическое управление ключами
  • Поддержка как пользовательского, так и системного режимов

2. Противодействие API Hooking

Для защиты от WinApi-hooking можно применять следующие техники:

Использование прямых системных вызовов (Syscalls) вместо стандартных API функций. Как отмечено в исследовании методов обхода EDR, “избегание вызовов приведет к отсутствию сканирования памяти вообще”.

Обфускация кода и API вызовов для затруднения анализа. В статье о пользовательских хуках указано, что “применение хуков в качестве механизма безопасности в собственном пространстве памяти процесса эквивалентно наличию механизмов безопасности в JavaScript, выполняющемся в веб-браузере”.

3. Защита от дампов памяти

Раздельное хранение ключа и использование его фрагментов в разных процессах или компонентах. Как предложено в исследовании о памяти, “Memory tagging может предотвратить чтение кода или данных друг друга разными процессами или виртуальными машинами”.

Очистка памяти после использования с помощью SecureZeroMemory. Согласно документации Microsoft, “когда вы закончили использовать конфиденциальную информацию, очистите ее из памяти, вызвав функцию SecureZeroMemory”.

4. Runtime шифрование

Шифрование ключей в реальном времени при использовании. Как описано в работе о защите приватности, “предлагается подход, который предотвращает несанкционированный доступ к конкретным данным реестра”, с “шифрованием данных ключей реестра при их записи”.


Безопасное использование динамически изменяющихся ключей

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

1. Генерация ключей на лету

cpp
// Пример генерации и использования временного ключа
BYTE* GenerateTemporaryKey(DWORD keySize) {
    BYTE* tempKey = new BYTE[keySize];
    BCryptGenRandom(NULL, tempKey, keySize, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
    return tempKey;
}

void UseKeyForEncryption(BYTE* key, DWORD keySize, const BYTE* data, DWORD dataSize) {
    // Использование ключа для шифрования
    // ...
    
    // Очистка ключа после использования
    SecureZeroMemory(key, keySize);
    delete[] key;
}

2. Иерархическая структура ключей

Мастер-ключ для шифрования производных ключей, которые используются для конкретных операций. Как объясняется в Threat Hunter Playbook, “DPAPI генерирует сильный ключ, называемый MasterKey, который никогда не используется в явных функциях шифрования/дешифрования”.

3. Ограниченное время жизни ключей

Автоматическое уничтожение ключей после истечения срока их действия. Как указано в документации DPAPI, “MasterKeys имеют срок действия, что означает, что после некоторого периода времени (жестко закодированное значение - три месяца) генерируется новый MasterKey”.


Практическая реализация защиты

Комплексный подход к защите

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

  1. Шифрование с DPAPI при хранении ключей
  2. Раздельное хранение ключа и его частей
  3. Обфускация кода для защиты от статического анализа
  4. Проверка целостности при загрузке и использовании ключа
  5. Очистка памяти после использования ключа

Пример защитного класса

cpp
class SecureKeyManager {
private:
    BYTE* encryptedKey;
    DWORD encryptedKeySize;
    bool isInitialized;
    
public:
    SecureKeyManager() : encryptedKey(nullptr), encryptedKeySize(0), isInitialized(false) {}
    
    ~SecureKeyManager() {
        Clear();
    }
    
    bool Initialize(const BYTE* key, DWORD keySize) {
        // Шифрование ключа с помощью DPAPI
        encryptedKey = ProtectData(key, keySize, encryptedKeySize);
        if (encryptedKey) {
            isInitialized = true;
            return true;
        }
        return false;
    }
    
    bool GetKey(BYTE* outKey, DWORD& outKeySize) {
        if (!isInitialized) return false;
        
        // Расшифровка ключа только на время использования
        DATA_BLOB encrypted = { encryptedKeySize, encryptedKey };
        DATA_BLOB decrypted;
        
        if (CryptUnprotectData(&encrypted, NULL, NULL, NULL, NULL, 0, &decrypted)) {
            memcpy(outKey, decrypted.pbData, decrypted.cbData);
            outKeySize = decrypted.cbData;
            
            // Очистка расшифрованных данных
            SecureZeroMemory(decrypted.pbData, decrypted.cbData);
            LocalFree(decrypted.pbData);
            
            return true;
        }
        return false;
    }
    
    void Clear() {
        if (encryptedKey) {
            SecureZeroMemory(encryptedKey, encryptedKeySize);
            LocalFree(encryptedKey);
            encryptedKey = nullptr;
            encryptedKeySize = 0;
            isInitialized = false;
        }
    }
};

Ограничения и дополнительные меры

Ограничения режима пользователя

Важно понимать, что без доступа к ядру абсолютная защита невозможна. Как отмечено в ответе на Stack Overflow, “вы не можете полностью защититься от хуков. Хакер все еще может изменить исполняемый файл на диске так, чтобы он не устанавливал хуки”.

Дополнительные меры защиты

  1. Использование аппаратного обеспечения (TPM, HSM) для дополнительной защиты ключей
  2. Многофакторная аутентификация для доступа к защищаемым функциям
  3. Регулярное обновление и проверка целостности исполняемого файла
  4. Мониторинг попыток несанкционированного доступа к памяти

Заключение

Хотя полной защиты от всех видов атак в режиме пользователя достичь невозможно, комбинирование DPAPI, раздельного хранения ключей, обфускации кода и очистки памяти значительно повышает безопасность секретных данных. Ключевым является многоуровневый подход, где каждый слой защиты затрудняет злоумышленнику достижение конечной цели.

Источники

  1. Device Security in the Windows Security App - Microsoft Support
  2. How to Prevent Unauthorized Memory Access in Windows Applications?
  3. A tale of EDR bypass methods
  4. windows - How to prevent memory editing to prevent hooking - Stack Overflow
  5. Memory Protection - Win32 apps | Microsoft Learn
  6. User Privacy Protection via Windows Registry Hooking and Runtime Encryption
  7. User-mode API hooks and bypasses
  8. Data Protection API — Threat Hunter Playbook
  9. CryptProtectData function (dpapi.h) - Win32 apps | Microsoft Learn
  10. DPAPI - Extracting Passwords - HackTricks
Авторы
Проверено модерацией
Модерация