是否可以保护WinAPI的内存区域?

时间:2013-01-30 03:03:54

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

阅读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下是一个好主意,还是只是一个有趣的智力练习?

2 个答案:

答案 0 :(得分:5)

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

您必须重新实现operator newoperator 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,我不能打扰下载编译器并查看它是否实际编译。 [我知道这个原则是有效的,因为我们做了几年前我工作的类似事情]。

答案 1 :(得分:2)

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