Безопасный распределитель памяти в C ++ - PullRequest
11 голосов
/ 12 августа 2008

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

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

Идея состоит в том, что она будет содержать конфиденциальную информацию (например, информацию о лицензии), которая должна быть недоступна для пользователя. Я провел обычное онлайн-исследование и спросил несколько других людей об этом, но не могу найти хорошее место для решения этой проблемы.

Обновление

Джош упоминает об использовании VirtualAlloc для установки защиты в области памяти. Я создал собственный распределитель (показанный ниже). Я обнаружил, что с помощью функции VirtualLock она ограничивает объем памяти, который я могу выделить. Похоже, это по замыслу. Поскольку я использую его для небольших объектов, это не проблема.

//
template<class _Ty>
class LockedVirtualMemAllocator : public std::allocator<_Ty>
{
public:
    template<class _Other>
    LockedVirtualMemAllocator<_Ty>& operator=(const LockedVirtualMemAllocator<_Other>&)
    {   // assign from a related LockedVirtualMemAllocator (do nothing)
        return (*this);
    }

    template<class Other>
    struct rebind {
        typedef LockedVirtualMemAllocator<Other> other;
    };

    pointer allocate( size_type _n )
    {
        SIZE_T  allocLen = (_n * sizeof(_Ty));
        DWORD   allocType = MEM_COMMIT;
        DWORD   allocProtect = PAGE_READWRITE;
        LPVOID pMem = ::VirtualAlloc( NULL, allocLen, allocType, allocProtect );
        if ( pMem != NULL ) {
            ::VirtualLock( pMem, allocLen );
        }
        return reinterpret_cast<pointer>( pMem );
    }
    pointer allocate( size_type _n, const void* )
    {
        return allocate( _n );
    }

    void deallocate(void* _pPtr, size_type _n )
    {
        if ( _pPtr != NULL ) {
            SIZE_T  allocLen = (_n * sizeof(_Ty));
            ::SecureZeroMemory( _pPtr, allocLen );
            ::VirtualUnlock( _pPtr, allocLen );
            ::VirtualFree( _pPtr, 0, MEM_RELEASE );
        }
    }
};

и используется

 //a memory safe std::string
 typedef std::basic_string<char, std::char_traits<char>, 
                           LockedVirtualMemAllocato<char> > modulestring_t;

Тед Персиваль упоминает mlock, но у меня пока нет реализации этого.

Я нашел Практическая криптография Нила Фургусона и Брюса Шнайера также весьма полезной.

Ответы [ 13 ]

19 голосов
/ 12 августа 2008

Вы не можете реально защитить от доступа к памяти. Вероятно, вы можете запретить подкачку страниц, если вы работаете как администратор или как система, но вы не можете запретить администратору или системе читать вашу память. Даже если бы вы могли каким-то образом полностью заблокировать другие процессы от чтения вашей памяти (чего вы не можете), другой процесс мог бы фактически внедрить новый поток в ваш процесс и прочитать память таким образом.

Даже если бы вы могли как-то полностью заблокировать ваш процесс и гарантировать, что ОС никогда не позволит кому-либо еще получить доступ к вашему процессу, у вас все равно не будет полной защиты. Вся ОС может работать на виртуальной машине, которая может быть приостановлена ​​и проверена в любое время.

Вы не можете защитить содержимое памяти от владельца системы. Голливуд и музыкальная индустрия болеют за это годами. Если бы это было возможно, они бы уже это сделали.

6 голосов
/ 02 сентября 2008

В системах Unix вы можете использовать mlock (2) для блокировки страниц памяти в ОЗУ, предотвращая их подкачку.

mlock () и mlockall () соответственно блокируют часть или все вызывающие обрабатывать виртуальное адресное пространство в ОЗУ, предотвращая осуществляется подкачка к области обмена.

Существует ограничение на количество памяти, которое может заблокировать каждый процесс, оно может быть показано с помощью ulimit -l и измеряется в килобайтах. В моей системе ограничение по умолчанию составляет 32 КБ на процесс.

5 голосов
/ 26 августа 2008

Если вы разрабатываете для Windows, есть способы, которыми вы можете ограничить доступ к памяти, но абсолютная блокировка других невозможна. Если вы хотите сохранить в секрете секрет, прочитайте Написание защищенного кода - что решает эту проблему довольно долго, но помните, что у вас нет возможности узнать, является ли ваш код работает на реальной машине или виртуальной машине. Есть множество вещей Win32 API для криптозащиты, которые обрабатывают такие вещи, включая безопасное хранение секретов - в книге говорится об этом. Вы можете посмотреть онлайн Microsoft CyproAPI для деталей; разработчики ОС осознают эту проблему и необходимость обеспечения безопасности открытого текста (опять же, прочитайте Написание безопасного кода ).

Функция Win32 API VirtualAlloc - это распределитель памяти уровня ОС. Позволяет установить защиту доступа; то, что вы могли бы сделать, это установить доступ к PAGE_GUARD или PAGE_NOACCESS, перевернуть доступ к чему-то более дружественному, пока ваша программа читает, и сбросить его после этого, но это просто горб скорости, если кто-то действительно очень старается заглянуть в ваш секрет .

В общем, взгляните на крипто API на вашей платформе, они решат проблему лучше, чем то, что вы взломали сами.

5 голосов
/ 12 августа 2008

Давайте рассмотрим это по очереди:

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

Достаточно справедливо.

* cannot be paged to disk.

Это будет сложно. Насколько мне известно, вы не можете отключить виртуальный пейджинг, поскольку он обрабатывается ОС. Если есть способ, то вы будете пьяны в недрах ОС.

* is incredibly hard to access through an attached debugger

Вы можете запустить его через PGP, сохранить его в зашифрованном виде в памяти и при необходимости расшифровать. Массивный удар по производительности.

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

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

Заметьте, что есть другие методы? там доступа к памяти процесс, кроме прикрепления отладчик?

Снятие дампа памяти.

4 голосов
/ 06 ноября 2015

установите Libsodium, используйте механизмы распределения по #include <sodium.h>

Защищенные выделения кучи

Медленнее, чем malloc () и друзья, им требуется 3 или 4 дополнительных страницы виртуальной памяти.

void *sodium_malloc(size_t size);

Выделите память для хранения конфиденциальных данных, используя sodium_malloc() и sodium_allocarray(). Вам нужно будет сначала позвонить sodium_init(), прежде чем использовать этих охранников кучи.

void *sodium_allocarray(size_t count, size_t size);

Функция sodium_allocarray() возвращает указатель, из которого можно получить доступ к объектам подсчета, размер которых составляет один байт памяти. Он обеспечивает те же гарантии, что и sodium_malloc(), но также защищает от арифметических переполнений, когда count * size превышает SIZE_MAX.

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

Кроме того, защита областей памяти, выделенных таким образом, может быть изменена с помощью операций блокировки памяти: sodium_mprotect_noaccess(), sodium_mprotect_readonly() и sodium_mprotect_readwrite().

После sodium_malloc вы можете использовать sodium_free(), чтобы разблокировать и освободить память. На этом этапе вашей реализации рассмотрите возможность обнуления памяти после использования.

обнулить память после использования

void sodium_memzero(void * const pnt, const size_t len);

После использования конфиденциальные данные должны быть перезаписаны, но memset () и рукописный код могут быть незаметно удалены оптимизирующим компилятором или компоновщиком.

Функцияodium_memzero () пытается эффективно обнулить len байтов, начиная с pnt, даже если к коду применяется оптимизация.

блокировка выделения памяти

int sodium_mlock(void * const addr, const size_t len);

Функция sodium_mlock() блокирует не менее 1 байт памяти, начиная с адреса. Это может помочь избежать передачи конфиденциальных данных на диск.

int sodium_mprotect_noaccess(void *ptr);

Функцияodium_mprotect_noaccess () делает область, выделенную с помощьюodium_malloc () илиodium_allocarray (), недоступной. Его нельзя прочитать или записать, но данные сохраняются. Эта функция может использоваться, чтобы сделать конфиденциальные данные недоступными, за исключением случаев, когда это действительно необходимо для конкретной операции.

int sodium_mprotect_readonly(void *ptr);

Функцияodium_mprotect_readonly () помечает область, выделенную с помощьюodium_malloc () илиodium_allocarray (), только для чтения. Попытка изменить данные приведет к завершению процесса.

int sodium_mprotect_readwrite(void *ptr);

Функция sodium_mprotect_readwrite() помечает область, выделенную с помощью sodium_malloc() или sodium_allocarray(), как читаемую и доступную для записи, после того как она была защищена с помощью sodium_mprotect_readonly() или sodium_mprotect_noaccess().

2 голосов
/ 12 августа 2008

То, что вы запрашиваете, обрабатывается на уровне ОС. Как только данные в вашей программе, они могут быть выгружены.

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

1 голос
/ 12 августа 2008

@ Грэм

Вы можете запустить его через PGP, сохранить его в зашифрованном виде в памяти и при необходимости расшифровать. Массивный удар по производительности.

Тогда вам придется держать ключ в памяти. Это сделало бы это немного сложнее, но определенно не невозможно. Любой мотивированный человек все равно сможет получить данные из памяти.

0 голосов
/ 01 ноября 2011

Лучше всего реализовать что-то похожее на класс SecureString .NET, и будьте очень осторожны, чтобы обнулить любые незашифрованные копии ваших данных, как только вы закончите (не забывайте очищать, даже когда генерируются исключения). Хороший способ сделать это с помощью std :: string и так далее - использовать пользовательский распределитель .

В Windows, если вы используете CryptProtectMemory (или RtlEncryptMemory для более старых систем), пароль шифрования сохраняется в нестраничной (ядро?) Памяти. В моем тестировании эти функции были чертовски быстрыми, особенно принимая во внимание защиту, которую они вам дают.

В других системах мне нравится использовать Blowfish, так как это хорошее сочетание скорости и силы. В последнем случае вам придется случайным образом генерировать свой собственный пароль (16+ байтов энтропии для Blowfish) при запуске программы. К сожалению, не так много можно сделать, чтобы защитить этот пароль без поддержки ОС, хотя вы можете использовать общие методы запутывания для встраивания жестко запрограммированного значения соли в ваш исполняемый файл, который вы можете комбинировать с паролем (это помогает немного).

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

0 голосов
/ 22 августа 2010

Вы не можете защитить содержимое памяти от владельца системы. Голливуд и музыкальная индустрия болеют за это годами. Если бы это было возможно, они бы уже это сделали.

Вы смотрели на Vista (и выше) Защищенные процессы (прямая .doc загрузка ) Я считаю, что защита с использованием операционной системы любезно предоставлена ​​индустрией развлечений.

0 голосов
/ 12 августа 2008

@ Дерек Парк

Он сказал только сильнее, не невозможно. PGP усложнит, а не сделает невозможным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...