如何在不初始化变量的情况下读取整个内存堆栈?

时间:2012-10-14 01:16:24

标签: c windows memory-management

每个进程获得4 GB的虚拟地址空间,而在4 GB中,用户空间获得2 GB。在这2 GB中,我想读取操作系统分配给进程的整个内存堆栈。

假设我在堆栈上声明了一个大小为2000,000(char chrArray[2000000];)的本地数组。我想问几个问题。

1)使用gcccygwin,我能够在没有初始化的情况下读取其内容,但其内容大部分为空(我使用%c将数组打印到文件中)或某些内容此数组末尾的垃圾数据。我在垃圾数据中存在一个字符串并在内存转储中查找它(使用DumpIt工具),但此字符串在内存转储中不存在。

我的问题是垃圾数据来自哪里?它位于硬盘中吗?

2)我想在每个子进程中使用fork()来声明char数组,但我想,它会复制父进程的整个地址空间并使用Copy-on-Write技术,因此它似乎没用。我对这种解释是对的吗?

3)我想要的是声明一个大尺寸的数组,每次映射到一些新的物理内存地址。这是可以实现的,如果可以,怎么样?

通过重复这个过程,我想扫描整个内存。

我尝试bash脚本多次运行此程序,但它总是从相同的虚拟基地址开始。

我尝试运行一个for循环来声明(比方说)10个char数组但是通过打印它们的基地址,我看到所有数组都有相同的地址。

我正在使用Windows 7C计划,我认为,我缺乏必要的操作系统知识。

感谢。

P.S。如果有人好奇为什么我这样做,我正在这方面做研究。

2 个答案:

答案 0 :(得分:2)

为避免安全问题,在分配物理页面时,大多数内核都会确保物理页面不包含任何数据(例如,用零填充)。否则,一个进程可以用敏感信息(例如密码)填充页面,然后释放它,另一个进程可以分配该页面并访问敏感数据。

因此,您在阵列中看到的垃圾是您自己的垃圾。

当一个进程首次启动时,很多事情通常会在main()获得控制权之前发生。这些东西可能包括动态链接和设置系统库,执行构造函数(用于C ++)等。这意味着在代码运行之前,事情会被放到堆栈中(然后从堆栈中删除)。这是你看到的垃圾。

答案 1 :(得分:2)

  

假设我声明了一个大小为2000,000(char chrArray[2000000];

的本地数组

这很危险。如果你的堆积不足,你的程序就会崩溃。

  

此数组末尾的一些垃圾数据。

堆栈向下增长,因此数组的末尾(较高部分)对应于堆栈的旧部分。程序启动时数据归零,程序运行时会将数据写入堆栈。所以“垃圾数据”只是剩下的局部变量和你调用的函数的其他堆栈帧。 (大多数操作系统使用专门的写时复制技术将进程内存归零 - 我说“专用”,因为当你知道整个块是零时,很容易复制一块内存。)

  

它位于硬盘中吗?

来自硬盘的数据仅出现在您的内存空间中。当您的进程重用其他进程的内存时,内存首先归零。

  

我想过使用fork()来声明每个子进程中的char数组,但我想,它会复制父进程的整个地址空间并使用Copy-on-Write技术,所以它似乎没用。我对这种解释是对的吗?

这是标记的Windows,除了使用Cygwin时,它没有fork()。我不知道Cygwin的实现。我不确定你要完成什么,或者你期望什么,但这正是fork()应该表现的方式。 fork()调用会复制整个过程,包括内存。

  

我想要的是声明一个大尺寸的数组,每次映射到一些新的物理内存地址。这是可以实现的吗?如果可以,那该怎么办?

用户空间程序不适用于物理内存地址,仅适用于虚拟内存地址。不,这是不可能的。

更糟糕的是,假设您在虚拟地址1000处分配了一个数组char arr[10000],并假设虚拟地址1000对应于物理地址6000.

  • &arr[0]的地址是虚拟地址1000和物理地址6000 ......除非你的进程被换出,此时它的没有物理地址。所以物理地址并不总是存在。

  • 当您的流程重新进入时,&arr[0]仍然是虚拟地址1000,但它可能是物理地址6000或8000或999000 ......谁知道?

  • &arr[1000]的地址始终是虚拟地址2000,但它可能是物理地址10000或85000或完全不同的东西......谁知道?

进一步阅读:您可能希望了解操作系统的“虚拟内存”子系统。