【问题标题】:Secure Memory Allocator in C++C++ 中的安全内存分配器
【发布时间】:2010-09-05 17:25:08
【问题描述】:

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

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

我们的想法是,这将包含用户无法访问的敏感信息(如许可证信息)。我已经在网上进行了常规研究,并就此询问了其他一些人,但我找不到一个很好的起点来解决这个问题。

更新

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 );
        }
    }
};

并且被使用

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

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

我发现Practical Cryptography by Neil Furguson and Bruce Schneier 也很有帮助。

【问题讨论】:

    标签: c++ security memory ram-scraping


    【解决方案1】:

    您无法保护系统所有者的内存内容。 多年来,好莱坞和音乐界一直在为此苦恼。 如果可能的话,他们早就这样做了。

    你看过 Vista(及以上)Protected Processes(直接.doc download)。我相信操作系统强制保护是娱乐行业的礼貌。

    【讨论】:

      【解决方案2】:

      安装 Libsodium,通过 #include &lt;sodium.h&gt; 使用分配机制

      受保护的堆分配

      比 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() 函数返回一个指针,从中可以访问每个 size 字节的内存对象。它提供与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() 和手写代码可以由优化编译器或链接器静默剥离。

      sodium_memzero() 函数尝试有效地将从 pnt 开始的 len 个字节归零,即使正在对代码进行优化。

      锁定内存分配

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

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

      int sodium_mprotect_noaccess(void *ptr);
      

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

      int sodium_mprotect_readonly(void *ptr);
      

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

      int sodium_mprotect_readwrite(void *ptr);
      

      sodium_mprotect_readwrite() 函数将使用sodium_malloc()sodium_allocarray() 分配的区域标记为可读可写,在使用sodium_mprotect_readonly()sodium_mprotect_noaccess() 保护之后。

      【讨论】:

        【解决方案3】:

        让我们一次一点:

        我想创建一个分配器 提供以下内存 属性:

        这很公平。

        * cannot be paged to disk.
        

        这会很难。据我所知,您不能禁用虚拟分页,因为它是由操作系统处理的。如果有办法,那么您将在操作系统的内部进行探索。

        * is incredibly hard to access through an attached debugger
        

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

        我们的想法是这将包含 敏感信息(如许可证 信息),这应该是 用户无法访问。我已经做好了 平时在网上查了一下,问了一个 其他人很少,但我 找不到一个好的开始 问题。

        让所有敏感信息远离机器。严重地。不要将敏感信息存储在内存中。编写一个自定义删除例程,该例程将自动从您执行的任何分配中删除所有数据。切勿允许对带有敏感材料的机器进行一般访问。如果您执行数据库访问,请确保在触发之前对所有访问进行清理。只有具有特定登录名的人才能访问。没有一般的组访问权限。

        顺便说一句,还有哪些其他方法 那里访问的内存 附加一个以外的过程 调试器?

        转储内存。

        【讨论】:

        • “让所有敏感信息远离机器。”如果我误解了,请原谅我,但是你应该处理敏感信息吗?您是否只是放弃计算机并手动进行操作?
        • 把它放在另一台机器上。如果你在机器上留下任何敏感的东西,那么恶意用户可以检查和解密它。如果您在需要时引入信息,并且仅在访问时对其进行解密,然后立即将其删除,则变得更加困难。然而,没有设备是完全万无一失的。有足够决心的人会绕过它。你需要做的是让它变得足够强大,可以愚弄 99.9%,但又不足以惹恼同样的 99.9%。
        【解决方案4】:

        最好的办法是实现类似于 .NET 的 SecureString 类的东西,并在完成后非常小心地将数据的任何纯文本副本清零(即使抛出异常也不要忘记清理)。使用 std::string 执行此操作的一个好方法是使用 custom allocator

        在 Windows 上,如果您使用 CryptProtectMemory(或旧系统的 RtlEncryptMemory),则加密密码存储在不可分页(内核?)内存中。在我的测试中,这些功能非常快,尤其是。考虑到他们为您提供的保护。

        在其他系统上,我喜欢使用 Blowfish,因为它是速度和力量的完美结合。在后一种情况下,您必须在程序启动时随机生成自己的密码(对于 Blowfish,熵为 16+ 字节)。不幸的是,在没有操作系统支持的情况下,您无法保护该密码,尽管您可以使用一般的混淆技术将硬编码的盐值嵌入到您可以与密码结合的可执行文件中(每一点都有帮助)。

        总体而言,此策略只是更广泛的纵深防御方法的一部分。另请注意,缓冲区溢出和未清理程序输入等简单错误仍然是迄今为止最常见的攻击媒介。

        【讨论】:

          【解决方案5】:

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

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

          每个进程可以锁定多少内存是有限制的,可以用ulimit -l显示,以千字节为单位。在我的系统上,默认限制是每个进程 32 kiB。

          【讨论】:

            【解决方案6】:

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

            Win32 API 函数VirtualAlloc 是操作系统级别的内存分配器。它允许您设置访问保护;您可以做的是设置对PAGE_GUARDPAGE_NOACCESS 的访问权限,并在程序读取时将访问权限翻转到更友好的内容,然后再将其重置,但这只是一个减速带,如果有人真的很努力地偷看你的秘密.

            总之,看看你平台上的加密 API,它们比你自己破解的东西更能解决问题。

            【讨论】:

            • CryptProtectMemory 是另一个可能有帮助的 API 调用。我不确定它到底做了什么,但广告中阻止其他进程读取页面的行为。
            • Mono SecureString 实现在这方面是一个很好的参考。基本上,您需要加密内存中的所有内容,这样它就不会以明文形式被分页到磁盘。保护内存中的数据不太重要。当操作系统本身不支持加密内存时,我在我的跨平台 SecureString 类中使用 Blowfish。
            【解决方案7】:

            @德里克公园

            他只是说更难,不是不可能。 PGP 会让它变得更难,而不是不可能。

            【讨论】:

              【解决方案8】:

              @克里斯

              哦,但是通过可信计算,您可以使用内存帘幕! :-P

              但是,您实际上必须愿意为别人拥有的计算机付费。 :p

              【讨论】:

                【解决方案9】:

                @roo

                我真的希望这是可能的,但我还没有找到它。你的例子让我意识到这正是我们想要做的——只允许访问我们程序上下文中的文件,从而保留 IP。

                我想我必须承认,没有真正安全的方法可以将某人的文件存储在另一台计算机上,尤其是在所有者允许在某个时候访问该文件的情况下。

                这绝对是问题所在。只要您从不授予访问权限,您就可以安全地存储某些内容,但是一旦您授予访问权限,您的控制权就消失了。你可以让它更难一点,但仅此而已。

                【讨论】:

                  【解决方案10】:

                  @Derek:哦,但是有了可信计算,你可以使用memory curtaining! :-P

                  【讨论】:

                    【解决方案11】:

                    @格雷厄姆

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

                    那么你必须把钥匙放在记忆里。这会让事情变得更难一些,但绝对不是不可能的。任何有动力的人仍然会设法从内存中获取数据。

                    【讨论】:

                      【解决方案12】:

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

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

                      不能保护系统所有者的内存内容。多年来,好莱坞和音乐界一直在为此苦恼。如果可能的话,他们早就这样做了。

                      【讨论】:

                      • 如何在数据离开 CPU 之前使用密钥对数据进行编码?
                      • 如果/既然这是真的,那么 PlayReady (
                      • A TPM 可用于存储敏感信息。 (在复制保护的情况下,有些人会说这是一种滥用,因为 TPM 正式旨在提高系统安全性。)对于 iOS 设备,可以使用 Secure Enclave。 SE 的主要目的是在设备被物理篡改时保护数据,正如我们从现在臭名昭著的 FBI 调查中所知道的那样。 TPM/SE 的使用仍然假设您至少可以信任硬件平台和/或运营商。至于虚拟机,只要你能检测到一个就退出……
                      【解决方案13】:

                      您要求的是在操作系统级别处理的。一旦数据在您的程序中,很可能会被调出。

                      为了访问内存,有动力的人可以附加一个硬件调试器。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-10-15
                        • 2021-12-30
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多