代码之家  ›  专栏  ›  技术社区  ›  roo

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

  •  9
  • roo  · 技术社区  · 18 年前

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

    • 很难通过附加的调试器访问

    其想法是,这将包含用户不可访问的敏感信息(如许可证信息)。我在网上做了通常的研究,并询问了其他一些人,但我找不到解决这个问题的好起点。

    更新

    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 也很有帮助。

    13 回复  |  直到 8 年前
        1
  •  19
  •   Derek Park    18 年前

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

    不能 保护内存内容不受系统所有者的影响。好莱坞和音乐产业多年来一直在为此而苦恼。如果可能的话,他们已经在做了。

        2
  •  8
  •   T Percival Daniel Gehriger    18 年前

    在Unix系统上,您可以使用 mlock(2)

    mlock()和mlockall()分别锁定调用的一部分或全部 将虚拟地址空间处理到RAM中,防止该内存 正在分页到交换区。

    每个进程可以锁定多少内存是有限制的,可以用 ulimit -l

        3
  •  5
  •   Josh    18 年前

    Writing Secure Code -这在一定程度上解决了这个问题,但请注意,您无法知道您的代码是在真实机器上运行还是在虚拟机器上运行。有一堆Win32 API的东西来处理加密处理这类事情,包括秘密的安全存储-这本书谈到了这一点。你可以在网上看看 Microsoft CyproAPI 详情请参阅;;操作系统设计者意识到了这个问题,并意识到需要保持明文安全(同样,阅读) 编写安全代码

    Win32 API函数 VirtualAlloc 是操作系统级内存分配器。它允许您设置访问保护;您可以做的是设置访问权限 PAGE_GUARD 或 PAGE_NOACCESS

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

        4
  •  5
  •   Anthony Atmaram    12 年前

    为内存提供以下内容 属性:

    这很公平。

    * cannot be paged to disk.
    

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

    * is incredibly hard to access through an attached debugger
    

    您可以通过PGP运行它,并将其加密存储在内存中,然后根据需要取消加密。巨大的性能打击。

    想法是,这将包含 敏感信息(如许可证) 用户无法访问。我已经做了 很少有人知道这件事,但我 问题

    将所有敏感信息远离机器。认真地不要将敏感信息存储在内存中。编写一个自定义删除例程,该例程将自动从您执行的任何分配中删除所有数据。禁止对带有敏感材料的机器进行一般性操作。如果执行数据库访问,请确保在触发之前对所有访问进行了消毒。只有具有特定登录的用户才允许访问。没有常规组访问权限。

    没有访问内存的方法 调试器?

        5
  •  5
  •   Bent Cardan    10 年前

    安装LibNade,使用分配机制,包括 <sodium.h>

    保护堆分配

    void *sodium_malloc(size_t size);
    

    使用分配内存来存储敏感数据 sodium_malloc() sodium_allocarray() sodium_init() 在使用这些堆防护之前。

    void *sodium_allocarray(size_t count, size_t size);
    

    这个 马洛克钠() 但也可以防止出现算术溢出 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);
    

    函数的作用是:从pnt开始有效地将len字节归零,即使对代码进行了优化。

    锁定内存分配

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

    这个 sodium_mlock()

    int sodium_mprotect_noaccess(void *ptr);
    

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

    int sodium_mprotect_readonly(void *ptr);
    

    int sodium_mprotect_readwrite(void *ptr);
    

    这个 马洛克钠() 或 钠合金 在使用 钠保护只读() 或 .

        6
  •  2
  •   Mark Harrison    18 年前

    为了访问内存,受激励的个人可以附加一个硬件调试器。

        7
  •  1
  •   Derek Park    18 年前

    @格雷厄姆

    您可以通过PGP运行它,并将其加密存储在内存中,然后根据需要取消加密。巨大的性能打击。

        8
  •  0
  •   kgriffs    14 年前

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

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

    总的来说,这一战略只是更广泛的纵深防御方法的一部分。还请记住,缓冲区溢出和未清理程序输入等简单错误仍然是最常见的攻击向量。

        9
  •  0
  •   Kuba hasn't forgotten Monica    10 年前

    无法保护内存内容不受系统所有者的影响。 如果可能的话,他们已经在做了。

    你看过Vista(及以上版本)吗 Protected Processes (直接 .doc download

        10
  •  -1
  •   C. K. Young    18 年前

    memory curtaining ! :-P</魔鬼代言人>

        11
  •  -1
  •   Community Mohan Dere    6 年前

    @袋鼠

    我想我必须承认,没有真正安全的方式将某人的文件存储在另一台计算机上,特别是如果在某一点上,所有者允许访问该文件。

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

        12
  •  -1
  •   Derek Park    18 年前

    @克里斯

    但你必须愿意为别人拥有的电脑买单P

        13
  •  -1
  •   graham.reeds    18 年前

    @德里克公园

    他只是说得更努力,不是不可能。PGP会让事情变得更难,而不是不可能。