Вопрос

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

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

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

Обновления

Джош упоминает использование 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, но у меня пока нет его реализации.

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

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

Давайте разберем это понемногу:

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

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

* cannot be paged to disk.

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

* is incredibly hard to access through an attached debugger

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

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

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

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

Делаем дамп памяти.

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

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

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

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

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

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

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

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

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

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

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

int sodium_mprotect_noaccess(void *ptr);

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

int sodium_mprotect_readonly(void *ptr);

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

int sodium_mprotect_readwrite(void *ptr);

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

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

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

@Грэм

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

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

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

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

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

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

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

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

@Дерек:О, но с доверенными вычислениями вы можете использовать завеса памяти!:-P</devils-advocate>

@roo

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

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

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

@Крис

О, но с доверенными вычислениями вы можете использовать завесу памяти!:-П

Но тогда вы действительно должны быть готовы заплатить за компьютер, которым владеет кто-то другой.:п

@Дерек Парк

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top