【问题标题】:Is it possible to protect a region of memory from WinAPI?是否可以从 WinAPI 保护内存区域?
【发布时间】:2013-01-30 03:03:54
【问题描述】:

阅读this interesting article 概述了一种调试堆损坏的技术后,我开始想知道如何根据自己的需要对其进行调整。基本思想是提供一个自定义的 malloc() 来分配整个内存页面,然后为这些页面启用一些内存保护位,以便程序在被写入时崩溃,并且可以在该行为中捕获有问题的写入指令.示例代码是 Linux 下的 C(mprotect() 用于启用保护),我很好奇如何将其应用于本机 C++ 和 Windows。 VirtualAlloc() 和/或 VirtualProtect() 看起来很有希望,但我不确定使用场景会是什么样子。

Fred *p = new Fred[100];
ProtectBuffer(p);
p[10] = Fred(); // like this to crash please

我知道在 Windows 中存在用于调试内存损坏的专用工具,但我仍然很好奇是否可以使用这种方法“手动”完成。

编辑:另外,这在 Windows 下是一个好主意,还是只是一个有趣的智力练习?

【问题讨论】:

    标签: c++ winapi memory-management heap-corruption


    【解决方案1】:

    是的,您可以使用 VirtualAlloc 和 VirtualProtect 来设置内存部分,使其免受读/写操作的影响。

    您必须重新实现 operator newoperator delete(以及它们的 [] 亲属),以便您的内存分配由您的代码控制。

    请记住,它只会以每页为基础,并且每次分配您将使用(至少)三页的虚拟内存 - 在 64 位系统上不是一个大问题,但可能如果您在 32 位系统中有许多分配,则会导致问题。

    大概你需要做的(你实际上应该找到构建 Windows 的 page-size - 我太懒了,所以我将使用 4096 和 4095 来表示 pagesize 和 pagesize-1 - 你还需要比这段代码做更多的错误检查!!!):

    void *operator new(size_t size)
    {
        Round size up to size in pages + 2 pages extra.
        size_t bigsize = (size + 2*4096 + 4095) & ~4095; 
    
        // Make a reservation of "size" bytes. 
        void *addr = VirtualAlloc(NULL, bigsize, PAGE_NOACCESS, MEM_RESERVE);
    
        addr = reinterpret_cast<void *>(reinterpret_cast<char *>(addr) + 4096);
    
        void *new_addr = VirtualAlloc(addr, size, PAGE_READWRITE, MEM_COMMIT); 
    
        return new_addr;
    }
    
    void operator delete(void *ptr)
    {
        char *tmp = reinterpret_cast<char *>(ptr) - 4096;
    
        VirtualFree(reinterpret_cast<void*>(tmp)); 
    }
    

    正如我所说的那样,我没有尝试编译此代码,因为我只有一个 Windows VM,而且我懒得下载编译器并查看它是否真的编译。 [我知道这个原则是有效的,因为我们在几年前工作时做过类似的事情]。

    【讨论】:

    • 我特别喜欢reinterpret_cats 部分;)
    • 嗯,它是 C++,你不能根据标准向 void 指针添加东西...... ;) 嗯,我明白你现在的意思了 - 已经很晚了,我应该去早在我开始写之前就睡了,真的....
    • 我不明白;为什么是两个 VirtualAlloc?
    • 第一个保留一个(比如说)12KB 的区域,使用 MEM_RESERVE 无法访问 - 第二个使用 REAL 内存(MEM_COMMIT)填充它,因此您可以实际写入它。这假设您实际上想要使用“中间”的内存。基本思想是你在实际内存的每一侧都得到一个页面。
    • 好的,所以我还是得在这个new实现返回的地址上使用VirtualProtect?
    【解决方案2】:

    这就是 Gaurd Pages 的用途(参见这个 MSDN 教程),它们在第一次访问页面时引发一个特殊异常,允许您做的不仅仅是在第一次访问无效页面时崩溃(并捕获错误读/写,而不是 NULL 指针等)。

    【讨论】:

    • 出于好奇,您有没有试过这个并且可以确认它工作可靠?我似乎记得 Windows 本身使用保护页面来提交超出最小提交大小的堆栈页面。那么,触发和处理保护页面错误可能会干扰正常操作?
    • @Damon:我自己没有使用保护页,纯粹是因为我不需要它们,但我见过一些系统使用它来捕获对外部程序的未经授权的访问。除此之外,您的程序异常处理程序只会接收内存 you 保护的保护页面异常(如果某些愚蠢的窗口将其保护异常传递给您,您可以通过检查地址轻松地将其过滤掉) .
    猜你喜欢
    • 1970-01-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 2014-11-29
    • 1970-01-01
    相关资源
    最近更新 更多