C++中的安全内存分配器

roo*_*roo 11 c++ memory security ram-scraping

我想创建一个分配器,它提供具有以下属性的内存:

  • 无法分页到磁盘.
  • 很难通过附加的调试器访问

这个想法是,它将包含敏感信息(如许可证信息),这些信息应该是用户无法访问的.我在网上做了一般的研究,并向其他几个人询问了这个问题,但我找不到一个好的地方开始解决这个问题.

更新

Josh提到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 );
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

并被使用

 //a memory safe std::string
 typedef std::basic_string<char, std::char_traits<char>, 
                           LockedVirtualMemAllocato<char> > modulestring_t;
Run Code Online (Sandbox Code Playgroud)

Ted Percival提到了mlock,但我还没有实现.

我发现Neil Furguson和Bruce Schneier的实用密码学也很有帮助.

Der*_*ark 19

你无法真正防止内存访问.如果您以管理员或系统身份运行,则可能会阻止分页,但您无法阻止管理员或系统读取内存.即使你可以某种方式完全阻止其他进程读取你的内存(你不能),另一个进程仍然可以实际注入一个新线程进入你的进程并以这种方式读取内存.

即使您可以以某种方式完全锁定您的流程并保证操作系统永远不会允许其他人访问您的流程,您仍然没有完全的保护.整个操作系统可以在虚拟机中运行,可以随时暂停和检查.

无法保护系统所有者的内存内容.多年来,好莱坞和音乐界一直在为此做好准备.如果可能的话,他们已经在做了.


Ted*_*val 6

在Unix系统上,你可以使用mlock(2)将内存页锁定到RAM中,防止它们被分页.

mlock()和mlockall()分别将部分或全部调用进程的虚拟地址空间锁定到RAM中,防止将内存分页到交换区域.

每个进程可以锁定的内存有一个限制,它可以显示ulimit -l为以千字节为单位并以千字节为单位进行测量.在我的系统上,默认限制为每个进程32 kiB.


gra*_*eds 5

让我们一点一点:

我想创建一个分配器,它提供具有以下属性的内存:

这很公平.

* cannot be paged to disk.
Run Code Online (Sandbox Code Playgroud)

那将是艰难的.据我所知,您无法禁用虚拟分页,因为它由操作系统处理.如果有办法,那么你将在操作系统的内容中进行探索.

* is incredibly hard to access through an attached debugger
Run Code Online (Sandbox Code Playgroud)

您可以通过PGP运行它并将其加密存储在内存中并根据需要对其进行解密.大规模的性能打击.

这个想法是,它将包含敏感信息(如许可证信息),这些信息应该是用户无法访问的.我在网上做了一般的研究,并向其他几个人询问了这个问题,但我找不到一个好的地方开始解决这个问题.

将所有敏感信息保留在机器上.认真.不要将敏感信息存储在内存中.编写一个自定义删除例程,它将自动删除您执行的任何分配中的所有数据.绝不允许一般访问带有敏感材料的机器.如果执行数据库访问,请确保在触发之前清除所有访问权限.只允许具有特定登录的人员访问.没有一般组访问权限.

另外,除了附加调试器之外,还有哪些其他方法可以访问进程的内存?

转储内存.


Jos*_*osh 5

如果您正在为Windows开发,有一些方法可以限制对内存的访问,但绝对阻止其他内容是不可行的.如果您希望保守秘密,请阅读编写安全代码 - 它可以解决此问题,但要注意您无法知道您的代码是在真机还是虚拟机上运行.有一堆Win32 API来处理加密处理这类事情,包括安全存储秘密 - 本书谈到了这一点.您可以查看在线Microsoft CyproAPI了解详细信息; 操作系统设计人员认识到这个问题以及保持明文安全的必要性(再次,阅读编写安全代码).

Win32 API函数VirtualAlloc是OS级别的内存分配器.它允许您设置访问保护; 你可以做的是设置访问权限,PAGE_GUARD或者PAGE_NOACCESS在你的程序读取时翻转访问更友好的东西,然后重置它,但如果有人真的很难偷看你的秘密,那只是一个速度突破.

总而言之,看看您平台上的加密API,它们将比您自己解决的问题更好地解决问题.


Ben*_*dan 5

安装 Libsodium,通过 #include 使用分配机制 <sodium.h>

受保护的堆分配

比 malloc() 和朋友慢,它们需要 3 或 4 个额外的虚拟内存页。

void *sodium_malloc(size_t size);
Run Code Online (Sandbox Code Playgroud)

使用sodium_malloc()和分配内存以存储敏感数据sodium_allocarray()sodium_init()在使用这些堆守卫之前,您需要先调用。

void *sodium_allocarray(size_t count, size_t size);
Run Code Online (Sandbox Code Playgroud)

sodium_allocarray()函数返回一个指针,从中可以访问每个大小为内存字节的 count 个对象。它提供相同的保证,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);
Run Code Online (Sandbox Code Playgroud)

使用后,敏感数据应该被覆盖,但是 memset() 和手写代码可以被优化编译器或链接器悄悄剥离。

即使对代码应用了优化,sodium_memzero() 函数也会尝试从 pnt 开始有效地将 len 字节归零。

锁定内存分配

int sodium_mlock(void * const addr, const size_t len);
Run Code Online (Sandbox Code Playgroud)

sodium_mlock()函数从 addr 开始锁定至少 len 字节的内存。这有助于避免将敏感数据交换到磁盘。

int sodium_mprotect_noaccess(void *ptr);
Run Code Online (Sandbox Code Playgroud)

钠_mprotect_noaccess() 函数使使用sodium_malloc() 或sodium_allocarray() 分配的区域不可访问。它不能被读取或写入,但数据被保留。此功能可用于使机密数据不可访问,除非特定操作实际需要。

int sodium_mprotect_readonly(void *ptr);
Run Code Online (Sandbox Code Playgroud)

钠_mprotect_readonly() 函数将使用sodium_malloc() 或sodium_allocarray() 分配的区域标记为只读。尝试修改数据将导致进程终止。

int sodium_mprotect_readwrite(void *ptr);
Run Code Online (Sandbox Code Playgroud)

所述sodium_mprotect_readwrite()功能使用的标记的区域分配sodium_malloc()sodium_allocarray()作为可读写的,使用具有被保护后sodium_mprotect_readonly()sodium_mprotect_noaccess()