有没有办法可靠地 malloc 与先前释放的块相同的内存块,然后访问先前在其中的内容?

Is there a way to reliably malloc the same block of memory as a previously freed block, then access the content that was previously in it?

我有以下 C 程序,它请求一些内存 (str1),将文件内容读入 space 然后释放它。接下来,请求一个相同大小(str2)的块,并将内容打印到标准输出。

我想要的是 str2 包含 str1 的内容,以便输出始终是文件的内容。

我知道我正在做的是未定义的行为,因为我不能保证已分配的内存内容将包含什么。但是,我正在尝试做一些卑鄙的事情来进行演示,其中文件中的数据可以在代码审查中不明显的情况下被泄露。

几乎所有时间,我在 str1str2 的相同地址收到一块内存,而 大多数时候 当我运行 macOS 上的程序和Windows,打印文件的内容。它似乎永远不会发生在 Linux 上(在 Linux 上,调用 free() 似乎将内存块归零)。

有没有办法让它在 Windows 和 macOS 上更可靠,是否有任何解释为什么它在 Linux 上根本不起作用?

我的代码是:

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

int main() {
  FILE *file = fopen("data.txt", "r");
  char *str1 = malloc(4096*sizeof(char));
  fread(str1, 1, 4096, f);
  free(str1);
  
  char *str2 = malloc(4096);
  printf("Content: %s\n", str2);
  free(str2);
}

Is there a way to reliably malloc the same block of memory as a previously freed block

是的,使用 realloc 而不是 free + malloc。否则,没有可靠或安全的方法可以在同一地址获得准确的金额。

I receive a block of memory at the same address for both str1 and str2

好吧,这个简单的程序中没有太多其他内容,所以这也许不足为奇。虽然没有保证。此外,除非您实际对堆进行写访问,否则实际上可能不会调用内存分配。所以 str2 可能只是一些随机地址,以防整个 malloc 调用被优化掉。或者,调用 malloc 但 OS 从不分配任何实际内存。

is there any explanation for why it doesn't work at all on Linux?

我不知道,但我怀疑 ASLR 可能与此有关。某些 Linux 专家将不得不回答该部分。

本质上,当你分配和释放时发生的事情对你来说是一个黑盒子。绝对没有可靠的方法来获得相同的地址。调用 free 意味着您告诉 OS 您已完成内存处理,并且无法撤消此操作。

What I want is for str2 to contain the content of str1 so that the output is always the content of the file.

这里基本上有三个选项。

  1. 等待 free
  2. 的呼叫
  3. 在调用之前复制缓冲区 free
  4. 编写您自己的 mallocfree
  5. 实现

来自评论:

imagine that I'm going to allocate some memory then read something sensitive (e.g. a private key) into that block and do something with it. Later, I allocate some memory of the same size and stick some data into it that will be saved to a file. If I don't overwrite all the data in the block then it may contain some sensitive info that would get saved to the file. In that case, it may not be obvious from a code review that some sensitive data exfiltration is possible. I want to demonstrate that sensitive data can be exfiltrated in a non-obvious manner.

不错,但这类攻击几乎总是依赖于未定义的行为。正如您自己所说,这是一个安全问题。因此,提供可靠的方法来做到这一点真的没有意义。

这是我在 Fedora Linux.

上工作的片段
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    char *s = malloc(100);
    const char str[] = "Hello, World. Prepare to meet your doom.";
    strcpy(s, str);
    free(s);
    for(int i=0; i<strlen(str); i++) 
        putchar(s[i]);
    puts("");
}

我的输出:

$ ./a.out 
��epare to meet your doom.

如您所见,我得到了部分数据,但不是全部。为了演示这个未定义的行为,这里是具有不同优化的输出:

$ gcc k.c -O1
$ ./a.out 
0Separe to meet your doom.
$ gcc k.c -O2
$ ./a.out 
@�
$ gcc k.c -O3
$ ./a.out 
�

你的方法对此很不可靠,因为你会一直打印到字符串中的第一个 0。下面是不会输出任何内容的代码,它可以让您误以为数据已被擦除。这就是我在上面的循环中使用 putchar 的原因。

char str[] = "Hello, World";
str[0] = '[=13=]';
printf("%s", str); // Will print nothing, but only first character is wiped