5

阅读了这篇概述了调试堆损坏技术的有趣文章后,我开始想知道如何根据自己的需要对其进行调整。基本思想是提供一个自定义的 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 下是一个好主意,还是只是一个有趣的智力练习?

4

2 回答 2

4

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

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

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

大致你需要做的(你实际上应该找到构建 Windows 的页面大小 - 我太懒了,所以我将使用 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,而且我懒得下载编译器并查看它是否真的编译。[我知道这个原则是有效的,因为我们在几年前工作的地方做过类似的事情]。

于 2013-01-30T03:28:43.180 回答
2

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

于 2013-01-30T14:39:04.800 回答