如何使用MSVC使malloc每次返回相同的地址?

如何使用MSVC使malloc每次返回相同的地址?,c,visual-c++,malloc,C,Visual C++,Malloc,出于调试目的,我希望malloc在每次执行程序时返回相同的地址,但是在MSVC中情况并非如此。 例如: #include <stdlib.h> #include <stdio.h> int main() { int test = 5; printf("Stack: %p\n", &test); printf("Heap: %p\n", malloc(4)); return 0; } …我每次都会得到相同的堆栈地址,但堆地址会更改

出于调试目的,我希望malloc在每次执行程序时返回相同的地址,但是在MSVC中情况并非如此。 例如:

#include <stdlib.h>
#include <stdio.h>

int main() {
    int test = 5;
    printf("Stack: %p\n", &test);
    printf("Heap: %p\n", malloc(4));
    return 0;
}
…我每次都会得到相同的堆栈地址,但堆地址会更改


我已经尝试添加注册表值HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\MoveImages,但它不起作用。

malloc返回的堆栈地址和指针每次都可能不同。事实上,当程序在Mac/OS上多次编译和运行时,两者都不同

编译器和/或操作系统可能会导致这种行为,使利用软件缺陷变得更加困难。在某些情况下,可能有一种方法可以防止这种情况发生,但如果您的目标是重播同一系列malloc地址,则其他因素可能会更改地址,例如时间敏感行为、文件系统副作用,更不用说非确定性线程行为。您应该尽量避免在测试中依赖此选项


还请注意,&test应转换为void*,因为%p需要一个void指针,但不能保证它具有与int*

相同的表示形式。事实证明,您可能无法从MSVC运行时库获得确定性行为。C/C++运行库的调试版本和生产版本最终都会调用名为_malloc_base的函数,该函数反过来调用Win32 API函数。不幸的是,无论是HeapAlloc还是提供其堆的函数,都没有记录标志或以其他方式获得确定性行为

正如@Enosh_Cohen所建议的那样,你可以在VirtualAlloc的基础上增加你自己的分配方案,但是你会失去MSVC分配函数提供的功能。

建议在VirtualAlloc的基础上创建一个新的malloc,所以我这样做了。事实证明这有点挑战性,因为VirtualAlloc本身不是确定性的,所以我正在记录我使用的过程

首先,抓住。到源的ftp链接已断开;使用

然后,将win32mmap函数替换为此处放入公共域的函数,就像Doug Lea的malloc本身一样:

static void* win32mmap(size_t size) {
  /* Where to ask for the next address from VirtualAlloc. */
  static char *next_address = (char*)(0x1000000);

  /* Return value from VirtualAlloc. */
  void *ptr = 0;

  /* Number of calls to VirtualAlloc we have made. */
  int tries = 0;

  while (!ptr && tries < 100) {
    ptr = VirtualAlloc(next_address, size,
                       MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
    if (!ptr) {
      /* Perhaps the requested address is already in use.  Try again
       * after moving the pointer. */
      next_address += 0x1000000;
      tries++;
    }
    else {
      /* Advance the request boundary. */
      next_address += size;
    }
  }

  /* Either we got a non-NULL result, or we exceeded the retry limit
   * and are going to return MFAIL. */
  return (ptr != 0)? ptr: MFAIL;
}
现在编译生成的malloc.c并将其与程序链接,从而覆盖MSVCRT分配器

有了这个,我现在得到了一致的malloc地址

但要注意:

我使用的确切地址0x1000000是通过枚举我的地址空间来选择的,用于查找一个大的、一致可用的漏洞。即使禁用ASLR,地址空间布局似乎也存在一些不可避免的不确定性。您可能需要调整该值

我确认,在我的特殊情况下,这种方法可以在100次连续运行中获得相同的地址。这对于我想要进行的调试来说已经足够好了,但是在足够多的迭代之后,或者在重新启动之后,这些值可能会改变,等等

此修改不应用于生产代码,仅用于调试。重试限制是一种攻击,我没有做任何事情来跟踪堆何时收缩


这不是malloc的保证行为,也从来没有在任何地方指定过。如果你有其他的要求,你应该考虑使用自己的包装器来满足你的要求。但请记住,只在需要的测试中使用该包装器。您可以在windows中使用VirtualAlloc来分配特定的地址。请参阅以下问题:@Someprogrammerdude虽然确定性返回值不是malloc的保证行为,并且在缺少虚拟内存硬件的系统中可能无法实现,但它是一个对调试有用的属性。同样,这也是伪随机生成器提供种子函数的原因之一。我建议在这个问题中添加windows标记,因为它适用于在windows下运行的所有程序,而不仅仅是那些使用MSVC编译的程序。
static void* win32mmap(size_t size) {
  /* Where to ask for the next address from VirtualAlloc. */
  static char *next_address = (char*)(0x1000000);

  /* Return value from VirtualAlloc. */
  void *ptr = 0;

  /* Number of calls to VirtualAlloc we have made. */
  int tries = 0;

  while (!ptr && tries < 100) {
    ptr = VirtualAlloc(next_address, size,
                       MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
    if (!ptr) {
      /* Perhaps the requested address is already in use.  Try again
       * after moving the pointer. */
      next_address += 0x1000000;
      tries++;
    }
    else {
      /* Advance the request boundary. */
      next_address += size;
    }
  }

  /* Either we got a non-NULL result, or we exceeded the retry limit
   * and are going to return MFAIL. */
  return (ptr != 0)? ptr: MFAIL;
}