【问题标题】:How to make malloc return the same address every time using MSVC?如何使 malloc 每次使用 MSVC 返回相同的地址?
【发布时间】:2020-05-11 21:51:23
【问题描述】:

出于调试目的,我希望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;
}

使用cygwin的gcc编译,每次都得到相同的Stack地址和Heap地址,而在关闭aslr的情况下使用MSVC编译...

cl t.c /link /DYNAMICBASE:NO /NXCOMPAT:NO

...我每次都得到相同的 Stack 地址,但是 Heap 地址发生了变化。

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

【问题讨论】:

  • 这不是malloc 的保证行为,并且从未在任何地方指定。如果您有其他要求,您应该考虑使用自己的 malloc 包装器来满足您的要求。但请记住,仅将该包装器用于您需要的测试。
  • 你可以在windows中使用VirtualAlloc()来分配一个特定的地址。请参阅以下问题:stackoverflow.com/questions/10364582/…
  • @Someprogrammerdude 虽然确定性返回值不是 malloc 的保证行为,并且在缺乏虚拟内存硬件的系统中可能无法实现,但它是一个对调试有用的属性。同样,这也是伪随机生成器提供种子函数的原因之一。
  • 我建议在这个问题中添加“windows”标签,因为它适用于在 Windows 下运行的所有程序,而不仅仅是那些使用 MSVC 编译的程序。

标签: c visual-c++ malloc


【解决方案1】:

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

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

另请注意,&amp;test 应转换为 (void *),因为 %p 需要一个 void 指针,但不能保证与 int * 具有相同的表示形式。

【讨论】:

    【解决方案2】:

    事实证明,您可能无法从 MSVC 运行时库中获得确定性行为。 C/C++ 运行时库的调试版和生产版最终都会调用名为_malloc_base() 的函数,而该函数又会调用Win32 API 函数HeapAlloc()。不幸的是,HeapAlloc() 和提供其堆的函数HeapCreate() 都没有记录标志或其他方式来获得确定性行为。

    按照@Enosh_Cohen 的建议,您可以在VirtualAlloc() 之上汇总您自己的分配方案,但随后您将失去MSVC 分配函数提供的debug functionality

    【讨论】:

      【解决方案3】:

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

      首先,获取Doug Lea's malloc。 (到源的 ftp 链接已损坏;请使用this http alternative。)

      然后,用这个替换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 是通过使用VirtualQuery 枚举我的地址空间来选择的,以寻找一个大的、始终可用的漏洞。即使禁用了 ASLR,地址空间布局似乎也有一些不可避免的不确定性。您可能需要调整该值。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-26
        • 2021-11-05
        • 2012-07-04
        • 2021-07-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多