使用保存的EBP值识别堆栈中的堆栈帧

我想通过查看堆栈中的原始数据来将堆栈分成堆栈。 我想通过找到保存的EBP指针的“链接列表”来实现。

  • 我能否假设一个(标准和常用的)C编译器(例如gcc)会在函数序言中的函数调用中始终更新并保存EBP?

    pushl%ebp
    movl%esp,%ebp

    或者有些情况下,有些编译器可能会跳过那部分函数,​​它们没有得到任何参数,也没有局部变量?

    x86调用约定和Wiki文章中对函数序言的帮助不大。

  • 有没有更好的方法通过查看原始数据来将栈分割为栈帧?

  • 谢谢!


    即使优化了帧指针,通过查看堆栈内存来获取保存的返回地址,栈帧通常也是可区分的。 请记住,x86中的函数调用序列总是由以下部分组成:

        call someFunc             ; pushes return address (instr. following `call`)
        ...
    someFunc:
        push EBP                  ; if framepointer is used
        mov EBP, ESP              ; if framepointer is used
        push <nonvolatile regs>
        ...
    

    所以你的堆栈将永远 - 即使帧指针丢失 - 在那里有返回地址。

    你如何识别返回地址?

  • 首先,在x86上,指令具有不同的长度。 这意味着返回地址 - 不像其他指针(!) - 往往是不对齐的值。 统计3/4的结果不是四的倍数。
    任何未对齐的指针都是返回地址的合适候选者。
  • 那么请记住x86上的call指令具有特定的操作码格式; 在返回地址之前读取几个字节,并检查是否在那里找到一个call操作码(大部分时间为99%,直接调用返回5个字节,通过寄存器返回3个字节​​)。 如果是这样,你找到了一个返回地址。
    这也是一种区分C ++ vtables和返回地址的方法 - 你可以在栈中找到vtable入口点,但是从那些你没有找到call指令的地址回头看看。
  • 使用这种方法,即使没有符号,框架化调试信息或任何东西,也可以从堆栈中获取呼叫序列的候选。

    如何从这些候选人中将实际的呼叫序列组合在一起的细节并不那么简单,但您需要反汇编程序和一些启发式方法来追踪从最低找到的返回地址一直到最后已知的程序位置的潜在呼叫流程。 也许有一天我会写博客的;-)虽然在这一点上,我宁愿说,一个计算器发布的边距太小,不能包含这个......


    某些版本的gcc有一个-fomit-frame-pointer优化选项。 如果内存服务,甚至可以使用参数/局部变量(它们直接从ESP而不是使用EBP索引)。 除非我错误地认为,MS VC ++可以做大致相同的事情。

    不客气,我不确定这种方式是否接近普遍适用。 如果你有调试信息的代码,通常很容易 - 否则......

    链接地址: http://www.djcxy.com/p/80337.html

    上一篇: Recognizing stack frames in a stack using saved EBP values

    下一篇: What does subl do here?