2

因此,假设我可以访问程序的寄存器。我可以访问 esp、ebp 和 eip。eip 指向下一条需要执行的指令, ebp 指向另一个帧指针, esp 指向栈顶。我理解这一点,但是我不理解堆栈的其余部分或如何解析它。

例如,如果我想获得一个框架的局部变量,我应该只减去ebp - esp(知道这ebp是一个比 更大的地址esp)然后遍历这些地址并取消引用它们吗?这是从该特定框架获取局部变量的正确方法吗?

另一个问题,找出与每一帧相关的功能的最佳方法是什么?如果我将地址减 1 ebp,然后取消引用该值,我应该得到返回地址“0x804 ...”吗?这个地址和函数有什么关系?例如,如果 Foo() 的高 pc 地址0x8045555和低 pc 地址0x8045550,我将要获得的返回地址会在这些地址之间吗?

非常感谢,如果我不够清楚,请告诉我..

注意:如果有人有更好的标题建议,我没有找到更好的。

4

2 回答 2

1

这方面的细节取决于您的 CPU 指令集架构(您显然使用的是 32 位 x86)和您的编译器工具链(我猜不出来)。通常,您不想自己重新编写代码来遍历堆栈帧,因为它复杂而脆弱,并且取决于您的编译器的优化和调试设置。

如果您正在尝试调试程序,您应该首先让您平台的调试器尝试整理您的堆栈。例如,使用gdb,您可以运行bt以获取“回溯”。

如果您尝试从相关程序内部执行此操作,并且您使用的是 GNU C 库,那么您可以使用该backtrace(3)函数。

如果您只是想了解事情的真正运作方式,这里有一篇有用的博客文章:http ://eli.thegreenplace.net/2011/02/04/where-the-top-of-the-stack-is-on-x86 /

要更深入地了解,请尝试 Wikipedia 的x86 Calling Conventions文章。再深入一点,如果您使用的是基于 ELF 的架构,例如 Linux,请参阅ELF ABI 规范

于 2012-11-08T02:00:47.670 回答
1

寄存器和堆栈中的数据只是一系列字节。数据由将字节放入系列中的各种指令和应用程序结构化,但是有关字节结构的信息不是字节系列的一部分,而是可能或可能不可用的附加信息。

例如,当编译源代码并由编译器生成二进制代码时,二进制代码中将包含额外的描述信息。附加描述信息的数量将取决于选择的编译器选项以及所用工具链中各种其他工具的功能。

例如,如果选项是使用附加信息(如函数名称)创建调试版本,并且工具链支持使用附加信息显示二进制代码,那么您可以很好地查看变量、函数名称和步进通过源代码级别的代码。

另一方面,如果选项是在没有附加信息的情况下创建优化的构建,那么即使工具链可能支持使用附加信息显示二进制代码,以便以更易读的视图呈现二进制代码,因为信息是不在那里,工具链无法在源代码级别显示二进制代码,显示源代码行、变量、函数名称等。

因此,为了完成您想要做的事情,您需要拥有附加信息,以便您可以将二进制代码与附加信息结合起来。如果没有额外的信息,你所拥有的只是内存中的一堆字节。您可以以各种方式显示这些字节,例如将它们解释为汇编代码或将它们解释为文本字符串。然而,如果没有额外的信息,你只是在猜测,无论你有多么受过教育。

不同平台和操作系统上的不同编译器和工具链会生成不同类型的附加信息。因此,您将需要了解您的工具链提供的特定信息。

于 2012-11-08T02:16:56.920 回答