在 Linux 中,当满足以下任何条件时,进程能够读取另一个进程内存:
- 该进程具有 root 权限,或者它可以读取
/proc/$PID/mem
or /dev/mem
,默认情况下/proc/$PID/mem
只能/dev/mem
由 root 访问
- 父进程可以
fork()
/clone()
以允许它读取其子进程的部分或全部内存的方式
- 父进程可以分叉子进程以允许子进程读取父进程的部分或全部内存
- 一个进程可以设置共享内存区域
除非进程以提升的特权运行,否则进程无法读取或写入任意不相关进程的内存。在所有其他情况下,您需要成为父进程或目标进程必须故意设置共享内存区域。
父进程可以访问子进程内存是大多数 Unix 系统的定义特征。在 Unix 系统(包括 Linux)中,新进程是使用fork()
系统调用创建的。fork()
通过在 OS 进程表中创建新条目来创建现有进程的副本,并将新进程虚拟内存设置为父虚拟内存的写入时副本。这确实意味着新进程可以读取父进程的内存,但此时新进程仍在执行父进程的代码,所以这不是安全问题。然后,新进程可能会调用其中一个exec*()
系统调用将一个新的可执行文件重新映射到它自己的内存中并跳转到该可执行文件的开始符号。重新映射意味着现在新进程虚拟内存上的分页表中唯一的条目是新的可执行文件。当新进程试图读/写重新映射的区域时,会导致页面错误,操作系统会将被编辑的可执行文件的相应部分分页exec*()
到内存中。如果新进程试图读取/写入未映射的内存区域,这将导致分段错误。在 fork 和 exec 的更高级用法中,进程可以 fork 然后映射子进程内存,这样子进程的全部或部分内存将在exec*()
.
在 Linux 中,当一个进程从操作系统分配内存(例如使用 malloc)时,该进程调用mmap()
分配匿名映射。匿名映射由 RAM 或交换提供。匿名 mmap 由内核填充为零,除非进程请求MAP_UNINITIALIZED
,这仅在非常受限制的嵌入式系统上出于性能原因而受到尊重,内核必须被编译以允许这样做。
此外,对于高安全性场景,Linux 允许进程通过使用 mlock 和/或 MAP_LOCKED 请求其全部或部分内存不可交换。锁定内存始终由 RAM 提供,通常用于防止加密密钥和用于解密的内存被交换到持久存储。