阅读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下是一个好主意,还是只是一个有趣的智力练习?
答案 0 :(得分:5)
是的,您可以使用VirtualAlloc和VirtualProtect来设置受到读/写操作保护的内存部分。
您必须重新实现operator new
和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,我不能打扰下载编译器并查看它是否实际编译。 [我知道这个原则是有效的,因为我们做了几年前我工作的类似事情]。
答案 1 :(得分:2)
这就是Gaurd Pages的用途(请参阅此MSDN教程),它们在第一次访问页面时引发特殊异常,允许您在第一次无效页面访问时执行更多操作(以及捕获错误的读/写而不是NULL指针等。)