ssi*_*ice 11 c x86 stack low-level
如果我运行程序,就像
#include <stdio.h>
int main(int argc, char *argv[], char *env[]) {
printf("My references are at %p, %p, %p\n", &argc, &argv, &env);
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到这些区域实际上在堆栈中.但还有什么呢?如果我们在Linux 3.5.3中运行所有值的循环(例如,直到segfault),我们可以看到一些奇怪的数字,以及由一堆零分隔的两个区域,可能是为了防止覆盖环境变量偶然.
无论如何,在第一个区域中必须有很多数字,例如每个函数调用的所有帧.
我们如何区分每个帧的结尾,参数在哪里,如果编译器添加了一个金丝雀,返回地址,CPU状态等等?
如果不了解叠加层,您只能看到位或数字.虽然一些地区受机器细节的限制,但大量的细节都非常标准.
如果你没有在嵌套例程之外移动太远,你可能正在查看内存的调用堆栈部分.对于一些通常被认为是"不安全"的C,您可以编写有趣的函数来访问上面几个"调用"的函数变量,即使这些变量没有"传递"到源代码中编写的函数.
调用堆栈是一个很好的起点,因为第三方库必须可由尚未编写的程序调用.因此,它是相当标准化的.
步进到进程内存边界之外将为您提供可怕的Segmentation违规,因为内存防护将检测到进程访问未授权内存的尝试.在具有内存分段功能的系统上,Malloc不仅仅"返回"指针,它还"标记"该进程可访问的内存,并检查进程分配未被违反的所有内存访问.
如果你一直遵循这条路径,迟早会对内核或对象格式产生兴趣.在源代码可用的Linux上,研究如何完成工作的方法要容易得多.拥有源代码允许您不通过查看其二进制文件来对数据结构进行反向工程.在开始时,困难的部分将学习如何找到正确的标题.稍后它将学习如何四处寻找并可能改变在非修补条件下你可能不应该改变的东西.
PS.您可能会将此内存视为"堆栈",但过了一段时间,您会发现它实际上只是一块可访问内存的大块,其中一部分被视为堆栈...
堆栈的内容基本上是:
操作系统向程序传递什么?典型的 *nix 将传递环境、程序参数、可能的一些辅助信息以及指向它们的指针以传递给main()
.
在 Linux 中,您将看到:
argv[0]
)auxv
,用于将信息从内核传递给程序argc
然后,下面是堆栈帧,其中包含:
你如何知道每个堆栈帧中的哪个是哪个?编译器知道,因此它只是适当地处理其在堆栈帧中的位置。调试器可以以调试信息的形式对每个函数使用注释(如果可用)。否则,如果有帧指针,您可以识别与其相关的事物:局部变量位于帧指针下方,参数位于堆栈指针上方。否则,您必须使用启发式方法,看起来像代码地址的东西可能是代码地址,但有时这会导致不正确且烦人的堆栈跟踪。