在my question yesterday之后,我试图了解一下调用堆栈的架构。在线搜索和搜索搜索没有得到我正在寻找的答案,这可能是因为我不确切知道要使用哪些关键字。无论如何,我相信这里有人可以帮助我......
首先,让我们从维基百科的stack buffer overflow条目摘录开始:
在软件中,当程序写入目标数据结构之外的程序调用堆栈上的内存地址时,会发生堆栈缓冲区溢出。通常是一个固定长度的缓冲区。
一位同事告诉我他记得在Linux上,堆栈处于进程的虚拟内存的最后并且根据需要向后增长 - 因此它不会是“固定长度缓冲区”。但是我无法证实这一点。所以我的问题是:
答案 0 :(得分:3)
堆栈缓冲区溢出是指程序意外或恶意地在堆栈上的特定数据项的范围内写入,例如c-string。这具有修改堆栈(而不是堆)上附近控件或数据结构的值的效果,这可能导致不希望的程序行为,例如崩溃,错误或更改控制流。
这通常不是指在堆栈本身范围之外写入,而堆栈本身通常受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 |
调用堆栈是固定大小的,实际堆栈本身在此限制内按需要增长和缩小。
堆和堆栈不重叠或共享内存 - 它们通常在虚拟地址空间的不同区域中进行管理。
进程主堆栈的大小由程序运行时存在的环境决定。要设置此功能的c函数,请参阅man 3 ulimit
并从bash查看/设置它,有关详细信息,请参阅ulimit -s
。
> ulimit -s
8192
如果您创建自己的线程,您可以负责创建其堆栈(请参阅man pthread_attr
),您可以使用系统建议的大小或设置自己的大小。
答案 1 :(得分:2)
对于Windows:
对于用户模式应用程序,默认情况下,堆栈的内存最初保留为1 MB。保留意味着地址范围不能用于任何其他目的,但实际上并未分配内存。这允许堆栈在内存中是连续的,但是默认情况下不要求所有堆栈(即使大部分都未使用)。在实际提交的堆栈结束时,有一个保护页面 - 每当访问它时,Windows将为堆栈分配更多内存。如果尝试使用超出为堆栈保留的空间,则会出现堆栈溢出异常。 VirtualAlloc的MSDN页面详细介绍了保留与提交。
x86对堆栈提出了一些强烈的要求(例如,必须向下增长)。其他架构更灵活。几乎所有基于x86的操作系统都使用堆栈。可以采用不同的堆栈架构。您无法使用任何x86的堆栈支持,您必须自己手动执行,但是当您调用任何OS API时,您必须转换为传统堆栈。
信息存储在.exe中。您可以使用linker flag进行调整。或者,CreateThread API允许您更改堆栈大小。
线程的堆栈是在线程创建时创建的,可以使用.exe中的默认值,也可以使用CreateThread调用中指定的内容。
当您超出存储在堆栈中的固定长度缓冲区并覆盖堆栈上的其他控制数据(如返回地址)时,会发生堆栈缓冲区溢出。
例如,假设您调用函数,它有一个大小为16字节的本地缓冲区。调用堆栈可能如下所示(为清楚起见,省略了其他详细信息:
0x1000 - Return address
0x990 - Buffer
如果您的代码有错误并且在0x990超出缓冲区,您将覆盖返回地址。如果攻击者可能导致缓冲区溢出,他们可能会将一些代码放入缓冲区,并覆盖返回地址以指回注入的代码。当你的函数返回时,它会跳转到攻击者的代码。