Я хочу создать распределитель, который предоставляет память со следующими атрибутами:
Идея состоит в том, что он будет содержать конфиденциальную информацию (например, информацию о лицензии), которая должна быть недоступна для пользователя. Я провел обычное исследование в Интернете и спросил об этом еще несколько человек, но я не могу найти подходящего места для начала рассмотрения этой проблемы.
Обновления
Джош упоминает использование 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, но у меня пока нет его реализации.
Я также нашел Практическая криптография Нила Фургюсона и Брюса Шнайера весьма полезным.





То, что вы просите, обрабатывается на уровне ОС. Как только данные попадают в вашу программу, они могут быть выгружены.
Для доступа к памяти мотивированный человек может подключить аппаратный отладчик.
Давайте рассмотрим это понемногу:
I want to create an allocator which provides memory with the following attributes:
Это достаточно честно.
* cannot be paged to disk.
Это будет сложно. Насколько мне известно, вы не можете отключить виртуальную подкачку, поскольку она обрабатывается ОС. Если есть способ, то вы будете копаться в недрах ОС.
* is incredibly hard to access through an attached debugger
Вы можете запустить его через PGP и сохранить в зашифрованном виде в памяти и при необходимости расшифровать. Огромный удар по производительности.
The idea is that this will contain sensitive information (like licence information) which should be inaccessible to the user. I have done the usual research online and asked a few other people about this, but I cannot find a good place start on this problem.
Храните всю конфиденциальную информацию вне машины. Серьезно. Не храните конфиденциальную информацию в памяти. Напишите специальную процедуру удаления, которая будет автоматически удалять все данные из любых выполняемых вами распределений. Никогда не допускайте общего доступа к машине с чувствительными материалами. Если вы выполняете доступ к базе данных, убедитесь, что весь доступ очищен перед запуском. Доступ разрешен только людям с определенными учетными записями. Нет доступа к общей группе.
On a side note, what other methods are there of accessing the memory of a process other than attaching a debugger?
Делаем дамп памяти.
Поместите его на другую машину. Если вы оставите на машине что-либо чувствительное, злоумышленник сможет проверить и расшифровать это. Если вы вводите информацию, когда она нужна, и расшифровываете ее только тогда, когда к ней обращаются, а затем сразу же удаляете ее, становится сложнее. Однако ни одно устройство не является полностью надежным. Кто-то достаточно решительный, чтобы обойти это. Что вам нужно сделать, так это сделать его достаточно жестким, чтобы обмануть 99,9%, но недостаточно, чтобы раздражать те же 99,9%.
Вы не можете действительно защитить от доступа к памяти. Вы, вероятно, можете запретить разбиение на страницы, если вы работаете как администратор или как система, но вы не можете запретить администратору или системе читать вашу память. Даже если бы вы могли каким-то образом полностью заблокировать чтение вашей памяти другими процессами (чего вы не можете), другой процесс все равно мог бы внедрить новый поток в ваш процесс и таким образом прочитать память.
Даже если бы вы могли каким-то образом полностью заблокировать свой процесс и гарантировать, что ОС никогда позволит кому-либо еще получить доступ к вашему процессу, у вас все равно не будет полной защиты. Вся ОС может работать на виртуальной машине, которую можно приостановить и проверить в любое время.
Вы не можешь защищаете содержимое памяти от владельца системы. Голливуд и музыкальная индустрия страдали от этого годами. Если бы это было возможно, они бы это уже делали.
как насчет кодирования данных с помощью ключа до того, как они выйдут из процессора?
Если / поскольку это правда, как PlayReady (
TPM может использоваться для хранения конфиденциальной информации. (В случае защиты от копирования некоторые скажут, что это злоупотребление, поскольку TPM официально предназначен для повышения безопасности системы.) Для устройств iOS можно использовать Secure Enclave. Основная цель SE - защитить данные, когда устройство подвергается физическому вмешательству, как мы знаем из теперь печально известного расследования ФБР. Использование TPM / SE по-прежнему предполагает, что вы можете по крайней мере доверять аппаратной платформе и / или оператору. Что касается виртуальных машин, просто закройте, когда сможете их обнаружить ...
@graham
You could run it through PGP and store it encrypted in memory and unencrypt it as needed. Massive performance hit.
Тогда вам придется держать ключ в памяти. Это сделало бы это немного сложнее, но определенно не невозможно. Любой мотивированный человек все равно сможет получить данные из памяти.
@Derek: О, но с надежными вычислениями вы можете использовать занятие памяти! :-П
@roo
I was really hoping that is was possible, and that I just hadn't found it yet. Your example just made me realise that that is exactly what we are trying to do - only allow access to files in the context of our program and so preserve the IP.
I guess I have to accept that there is no truly secure way to store someone’s files on another computer, especially if at some point access is allowed to that file by the owner.
Это определенно проблема. Вы можете хранить что-то в безопасности до тех пор, пока вы никогда не предоставите доступ, но как только вы предоставите доступ, ваш контроль исчезнет. Вы можете немного усложнить задачу, но это все.
@Крис
Oh, but with trusted computing, you can use memory curtaining! :-P
Но тогда вы должны быть готовы платить за чужой компьютер. :п
@ Дерек Парк
Он только сказал жестче, не невозможно. PGP сделает это сложнее, а не невозможным.
Если вы разрабатываете для Windows, есть способы ограничить доступ к памяти, но полностью заблокировать другие невозможно. Если вы надеетесь сохранить секрет в секрете, прочтите Написание безопасного кода, в котором подробно рассматривается эта проблема, но имейте в виду, что у вас нет возможности узнать, выполняется ли ваш код на реальной или виртуальной машине. Для работы с криптовалютой существует множество API-интерфейсов Win32, которые обрабатывают такие вещи, включая безопасное хранение секретов - об этом говорится в книге. Вы можете посмотреть онлайн Microsoft CyproAPI для подробностей; разработчики ОС осознают эту проблему и необходимость защиты открытого текста (опять же, прочтите Написание безопасного кода).
Функция Win32 API VirtualAlloc является распределителем памяти на уровне ОС. Позволяет установить защиту доступа; что вы могли бы сделать, так это установить доступ к PAGE_GUARD или PAGE_NOACCESS и переключить доступ на что-то более дружелюбное, пока ваша программа читает, а потом сбросить его, но это просто горб, если кто-то действительно пытается подсмотреть ваш секрет.
Таким образом, взгляните на криптографические API-интерфейсы на своей платформе, они решат проблему лучше, чем то, что вы взламываете сами.
CryptProtectMemory - еще один вызов API, который может помочь. Я не совсем уверен, что он делает, но рекламируемое поведение, препятствующее прочтению страницы другими процессами.
Реализация Mono SecureString является хорошим ориентиром в этом отношении. По сути, вам нужно зашифровать все в памяти, чтобы оно не выгружалось на диск в виде открытого текста. Защита данных в памяти менее важна. Я использую Blowfish в своем кроссплатформенном классе SecureString, когда ОС изначально не поддерживает зашифрованную память.
В системах Unix вы можете использовать шуба (2) для блокировки страниц памяти в RAM, предотвращая их подкачку.
mlock() and mlockall() respectively lock part or all of the calling process’s virtual address space into RAM, preventing that memory from being paged to the swap area.
Существует ограничение на объем памяти, который может блокировать каждый процесс, он может отображаться с помощью ulimit -l и измеряется в килобайтах. В моей системе ограничение по умолчанию составляет 32 КБ на процесс.
You cannot protect memory contents from the owner of the system. Hollywood and the music industry have been aching for this for years. If it were possible, they'd already be doing it.
Вы видели Vista (и выше) Защищенные процессы (прямая .doc скачать). Я считаю, что усиленная защита операционной системы - это любезность индустрии развлечений.
Лучше всего реализовать что-то похожее на класс SecureString .NET, и будьте очень осторожны, чтобы обнулить все копии ваших данных в виде открытого текста, как только вы закончите (не забудьте очистить, даже если выбрасываются исключения). Хороший способ сделать это с помощью std :: string, например настраиваемый распределитель.
В Windows, если вы используете CryptProtectMemory (или RtlEncryptMemory для старых систем), пароль шифрования хранится в не выгружаемой памяти (ядро?). В моем тестировании эти функции чертовски быстрые, особенно. принимая во внимание защиту, которую они вам предоставляют.
На других системах мне нравится использовать Blowfish, так как это хорошее сочетание скорости и силы. В последнем случае вам придется случайным образом сгенерировать собственный пароль (16+ байтов энтропии для Blowfish) при запуске программы. К сожалению, вы не так много можете сделать для защиты этого пароля без поддержки ОС, хотя вы можете использовать общие методы обфускации, чтобы встроить жестко запрограммированное значение соли в свой исполняемый файл, которое вы можете комбинировать с паролем (каждый немного помогает).
В целом, эта стратегия - лишь часть более широкого подхода к эшелонированной защите. Также имейте в виду, что простые ошибки, такие как переполнение буфера и отсутствие очистки входных данных программы, остаются наиболее распространенными векторами атаки.
установить 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 () и рукописный код могут быть автоматически удалены оптимизирующим компилятором или компоновщиком.
Функция натрия_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().
«Держите всю конфиденциальную информацию подальше от машины». Простите меня, если я неправильно понял, но, черт возьми, вы тогда должны делать с конфиденциальной информацией? Вы просто отказываетесь от компьютеров и делаете это вручную?