Architecture 调用堆栈内存结构

Architecture 调用堆栈内存结构,architecture,memory,multithreading,stack,Architecture,Memory,Multithreading,Stack,接下来,我试图学习更多关于调用堆栈架构的知识。在线搜索并没有得到我想要的答案,这可能是因为我不知道该使用哪些关键词。不管怎样,我相信这里有人能帮我 首先,让我们从维基百科的条目摘录开始: 在软件中,当程序写入预定数据结构之外的程序调用堆栈上的内存地址时,堆栈缓冲区溢出发生;通常是一个固定长度的缓冲区 一位同事告诉我,他记得在Linux上,堆栈位于进程“虚拟内存”的最末端,并根据需要向后增长,因此它不是“固定长度缓冲区”。然而,我无法证实这一点。因此,我的问题是: 在Windows和Linux上是

接下来,我试图学习更多关于调用堆栈架构的知识。在线搜索并没有得到我想要的答案,这可能是因为我不知道该使用哪些关键词。不管怎样,我相信这里有人能帮我

首先,让我们从维基百科的条目摘录开始:

在软件中,当程序写入预定数据结构之外的程序调用堆栈上的内存地址时,堆栈缓冲区溢出发生;通常是一个固定长度的缓冲区

一位同事告诉我,他记得在Linux上,堆栈位于进程“虚拟内存”的最末端,并根据需要向后增长,因此它不是“固定长度缓冲区”。然而,我无法证实这一点。因此,我的问题是:

  • 在Windows和Linux上是调用 堆栈是否总是固定大小的缓冲区?如果 不是,它是如何生长的?怎么样 管理虚拟内存的共享 用那堆
  • 是吗 堆栈的体系结构取决于 所使用的编译语言?上 操作系统?在硬件方面
  • 尺寸是多少 在确定的堆栈的 编译时,还是可以更改 后验
  • 你是如何和在哪里 单个线程的调用堆栈 分配
  • 堆栈缓冲区溢出 堆栈缓冲区溢出是指程序意外或恶意写入堆栈上特定数据项(如c字符串)的范围之外。这会影响修改堆栈(而不是堆)上附近控件或数据结构的值,这可能会导致不希望的程序行为,如崩溃、错误或更改控制流

    这通常不是指在堆栈本身的范围之外进行写入,堆栈本身通常受到保护,以防止意外的过度或不足运行

    | start of stack |
    | data           |
    | parameters     |
    | return address |
    | data           |
    | parameters     |
    | return address |
    | parameters     |
    | return address | <- might overflow into this region or above
    | string data    | <- writes to this region ... (look up)
     stack head
      |
      V direction of growth for pushes
     ...
    | end of stack   |
    | guard page     | <- writes to this region cause a segfault
     ...
    | heap           |
    
    如果您创建自己的线程,您可以负责创建它们的堆栈(请参见
    man pthread\u attr
    ),您可以使用系统建议的大小或设置自己的大小。

    对于Windows:

  • 对于用户模式应用程序,默认情况下,堆栈的内存最初保留为1 MB。保留意味着地址范围不能用于任何其他用途,但内存实际上没有分配。这允许堆栈在内存中是连续的,但不要求在默认情况下分配所有堆栈(即使大多数堆栈未使用)。在实际提交的堆栈的末尾有一个保护页-每当访问该页时,Windows都会为堆栈分配更多内存。如果试图使用超出为堆栈保留的空间,则会出现堆栈溢出异常。的MSDN页面更详细地介绍了保留与提交

  • x86对堆栈提出了一些强烈的要求(例如必须向下增长)。其他架构更灵活。几乎所有基于x86的操作系统都使用类似的堆栈。可以使用不同的堆栈体系结构。您不能使用x86的任何堆栈支持,您必须自己手动操作,但在调用任何OS API时,您必须转换为传统堆栈

  • 该信息存储在.exe中。你可以用一只手来调整它。或者,API允许您更改堆栈大小

  • 线程的堆栈是在线程创建时创建的,可以使用.exe中的默认值,也可以使用CreateThread调用中指定的值

  • 当您溢出存储在堆栈上的固定长度缓冲区并覆盖堆栈上的其他控制数据(如返回地址)时,会发生堆栈缓冲区溢出

    例如,假设您调用函数,它有一个大小为16字节的本地缓冲区。调用堆栈可能如下所示(为清楚起见,省略了其他细节:

    0x1000 - Return address
    0x990 - Buffer
    

    如果您的代码有错误并在0x990处溢出缓冲区,您将覆盖返回地址。如果攻击者可以导致缓冲区溢出,他们可以将一些代码放入缓冲区,并覆盖返回地址以指向注入的代码。当您的函数返回时,它将跳转到攻击者的代码。

    因此,“堆栈缓冲区溢出”与我填充堆栈时在Windows中遇到的“堆栈溢出”错误的概念不同,即使用永无止境的递归调用?(Windows调用“堆栈溢出”,而不是“segfault”)。正确。保护页中的segfault被捕获并变为堆栈溢出。
    0x1000 - Return address
    0x990 - Buffer