【发布时间】:2009-09-28 19:21:19
【问题描述】:
我对 C 和指针和内存管理等低级语言的了解越多,我就越想知道现代操作系统和内存保护的最新技术。例如,有哪些检查可以防止某些流氓程序随机尝试读取尽可能多的地址空间而无视操作系统设置的规则?
一般来说,这些内存保护方案是如何工作的?他们的优势和劣势是什么?换句话说,在现代操作系统中运行已编译的程序时,即使您拥有 C 语言并且您拥有自己的编译器并进行了任何您想要的调整,是否有一些事情根本无法完成?
【问题讨论】:
我对 C 和指针和内存管理等低级语言的了解越多,我就越想知道现代操作系统和内存保护的最新技术。例如,有哪些检查可以防止某些流氓程序随机尝试读取尽可能多的地址空间而无视操作系统设置的规则?
一般来说,这些内存保护方案是如何工作的?他们的优势和劣势是什么?换句话说,在现代操作系统中运行已编译的程序时,即使您拥有 C 语言并且您拥有自己的编译器并进行了任何您想要的调整,是否有一些事情根本无法完成?
【问题讨论】:
保护由硬件(即 CPU)强制执行。应用程序只能将地址表示为虚拟地址,CPU 使用lookaside buffers 解析虚拟地址到物理地址的映射。每当 CPU 需要解析未知地址时,它都会产生“页面错误”,中断当前正在运行的应用程序并将控制权切换到操作系统。操作系统负责查找其内部结构(page tables)并找到应用程序所触及的虚拟地址与实际物理地址之间的映射。一旦找到映射,CPU 就可以恢复应用程序。
加载物理地址和虚拟地址之间的映射所需的 CPU 指令受到保护,因此只能由受保护的组件(即操作系统内核)执行。
总体而言,该方案有效,因为:
如果恶意模块被加载到内核中,该方案会失败,因为在该保护级别它可以读取和写入任何物理地址。
应用程序可以读取和写入其他进程的内存,但只能通过请求内核为它们执行此操作(例如在 Win32 中ReadProcessMemory),并且此类 API 受访问控制保护(调用者需要某些特权)。
【讨论】:
内存保护在硬件中实施,通常以 KB 为单位的最小粒度。
来自关于memory protection的维基百科文章:
在分页中,内存地址空间是 分成相等的小块, 称为页面。使用虚拟内存 机制,每一页都可以做成 居住在物理的任何位置 记忆,或被标记为 受保护。虚拟内存让它 可能有一个线性虚拟 内存地址空间并使用它来 访问块在物理上碎片化 内存地址空间。
大多数计算机体系结构都基于 页面,尤其是 x86 架构, 也可以使用页面来保护内存。
页表用于映射 虚拟内存到物理内存。这 页表通常对 过程。页表更容易 分配新内存,作为每个新页面 可以从任何地方分配 物理内存。
通过这样的设计,不可能 访问页面的应用程序 尚未明确分配给 它,仅仅因为任何内存地址, 即使是一个完全随机的,那 应用程序可能决定使用 指向分配的页面,或 生成页面错误 (PF) 错误。 未分配的页面根本没有 应用程序中的任何地址 观点。
【讨论】:
您应该向 Google 询问分段错误、内存违规错误和一般保护失败。这些是各种操作系统返回的错误,以响应程序试图访问它不应该访问的内存地址。
Windows Vista(或 7)具有随机附加 dll 的例程,这意味着每次发生缓冲区溢出时都会将您带到不同的地址。这也使得缓冲区溢出攻击的可重复性降低了一点。
【讨论】:
因此,将与您的问题一起发布的答案链接在一起。程序尝试读取任何未映射到其地址空间中的内存地址,将导致处理器发出页面错误异常,将执行控制权转移到操作系统代码(受信任代码),然后内核将检查哪个是错误的地址,如果当前进程地址空间中没有映射,它将向通常杀死进程的进程发送 SIGSEV(分段错误)信号(这里讨论的是 Linux/Unix),在 Windows 上你会得到相同的东西.
注意:您可以查看 Linux 和 POSIX 操作系统中的 mprotect(),它允许您显式保护内存页面,诸如 malloc() 之类的函数返回具有默认保护的页面上的内存,然后您可以对其进行修改,通过这种方式,您可以将内存区域保护为只读(但仅以页面大小的块为单位,通常约为 4KB)。
【讨论】: