Безопасный распределитель памяти в C ++

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

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

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

Обновления

Джош упоминает об использовании 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;

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

Я нашел практическое Cryptography Нил Furguson и Брюс Шнайер весьма полезным , а также.

12.08.2008 04:18:09
13 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

19
12.08.2008 05:17:09
как насчет кодирования данных ключом до того, как они выйдут из процессора?
v.oddou 25.06.2014 00:46:52
Если / поскольку это правда, как PlayReady (<3.0), Widevine и т. Д. Работают без взлома?
davidkomer 20.06.2016 08:53:17
TPM может быть использован для хранения конфиденциальной информации. (В случае защиты от копирования некоторые скажут, что это злоупотребление, поскольку TPM официально предназначен для повышения безопасности системы.) Для устройств iOS можно использовать Secure Enclave. Главная цель SE - защитить данные, когда устройство физически подделано, как мы знаем из печально известного в настоящее время расследования ФБР. Использование TPM / SE по-прежнему предполагает, что вы, по крайней мере, можете доверять аппаратной платформе и / или оператору. Что касается виртуальных машин, просто выйдите, когда сможете их обнаружить ...
Arne Vogel 19.06.2018 11:25:32

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

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

2
12.08.2008 05:07:08

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

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

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

* cannot be paged to disk.

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

* is incredibly hard to access through an attached debugger

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

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

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

Кстати, какие существуют другие способы доступа к памяти процесса, кроме подключения отладчика?

Принимая дамп памяти.

5
30.10.2013 01:43:12
«Храните всю конфиденциальную информацию вне машины». Простите, если я неправильно понял, но почему вы должны делать с конфиденциальной информацией? Вы просто угробляете компьютеры и делаете это вручную?
Parker Kemp 19.07.2017 12:18:31
Поместите это на другую машину. Если вы оставите на компьютере что-нибудь чувствительное, то злоумышленник может проверить и расшифровать его. Если вы вводите информацию, когда она необходима, и расшифровываете ее только тогда, когда к ней обращаются, а затем сразу удаляете ее, она становится труднее. Однако ни одно устройство не является полностью надежным. Кто-то решительно настроен обойти это. Что вам нужно сделать, это сделать его достаточно жестким, чтобы обмануть 99,9%, но не настолько, чтобы раздражать те же самые 99,9%.
graham.reeds 19.07.2017 13:25:43

@graham

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

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

1
12.08.2008 05:21:33

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

-1
12.08.2008 06:03:16

@roo

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

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

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

-1
12.08.2008 06:12:44

@Крис

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

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

-1
12.08.2008 06:15:27

@Derek Park

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

-1
12.08.2008 08:10:27

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

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

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

5
26.08.2008 01:25:40
CryptProtectMemory, это еще один вызов API, который может помочь. Я не уверен точно, что это делает, но рекламируемое поведение, чтобы мешать другим процессам читать страницу.
deft_code 26.08.2009 18:40:41
Реализация Mono SecureString является хорошим справочным материалом в этом отношении. По сути, вам необходимо зашифровать все данные в памяти, чтобы они не передавались на диск в виде открытого текста. Защита данных в памяти менее важна. Я использую Blowfish в своем кроссплатформенном классе SecureString, когда ОС изначально не поддерживает зашифрованную память.
kgriffs 31.10.2011 21:31:17

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

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

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

6
2.09.2008 02:25:23

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

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

0
18.04.2016 16:28:40

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

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

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

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

0
2.11.2011 15:39:05

установить Libsodium, использовать механизмы выделения от <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()функции блокировки по крайней мере LEN байт памяти , начиная с адреса. Это может помочь избежать обмена конфиденциальными данными на диск.

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().

4
6.11.2015 13:45:38