代码之家  ›  专栏  ›  技术社区  ›  Philippe Beaudoin

调用堆栈内存结构

  •  2
  • Philippe Beaudoin  · 技术社区  · 16 年前

    跟随 my question yesterday ,我试图了解更多关于调用堆栈的体系结构。网上搜索并没有找到我要找的答案,这可能是因为我不知道确切使用哪些关键字。不管怎样,我相信这里有人能帮我…

    首先,让我们从维基百科词条的摘录开始 stack buffer overflow :

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

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

    1. 在Windows和Linux上 堆栈总是固定大小的缓冲区?如果 不是,它是怎么生长的?它如何 管理共享虚拟内存 用堆?
    2. 做 堆栈的架构取决于 使用的编译语言?上 操作系统?在硬件上?
    3. 是尺寸 确定的堆栈的 编译时,或者可以更改它 后验?
    4. 怎么去哪儿 单个线程的调用堆栈 分配?
    2 回复  |  直到 16 年前
        1
  •  3
  •   Alex Brown    16 年前

    堆栈缓冲区溢出

    堆栈缓冲区溢出是指程序意外或恶意地写入堆栈上特定数据项的范围之外,例如C字符串。这会影响修改堆栈(而不是堆)上附近的控件或数据结构的值,从而导致不希望的程序行为,如崩溃、错误或更改控制流。

    这通常不是指在堆栈本身的范围之外写入,堆栈本身通常受 guard pages 以防止意外的超负荷或欠负荷运行。

    | 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           |
    

    Linux上的调用堆栈

    调用堆栈的大小是固定的,实际堆栈本身根据需要在该限制内增长和收缩。

    堆和堆栈不重叠或共享内存-它们通常在虚拟地址空间的不同区域进行管理。

    进程主堆栈的大小由程序运行时存在的环境决定。有关C函数的设置,请参见 man 3 ulimit 要从bash查看/设置它,请参见, ulimit -s 详情。

    > ulimit -s
    8192
    

    如果创建自己的线程,则可以承担创建它们的堆栈的责任(请参见 man pthread_attr )您可以使用系统推荐的大小或设置自己的大小。

        2
  •  2
  •   Michael    16 年前

    对于Windows:

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

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

    3. 信息存储在.exe中。你可以用 linker flag . 或者, CreateThread API允许您更改堆栈大小。

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

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

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

    0x1000 - Return address
    0x990 - Buffer
    

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