【问题标题】:How does assembly know where to return?组装如何知道返回到哪里?
【发布时间】:2014-02-16 22:49:33
【问题描述】:

我目前正在尝试了解 IA32 架构中的汇编。我一直以为我理解了堆栈结构,直到我运行了一些缓冲区溢出漏洞利用代码,该代码将返回地址推到堆栈顶部然后返回。然后,令我惊讶的是,程序返回到我压入堆栈的那个地址。我最初认为基指针将用于返回,因为它位于返回地址旁边。但是,我没有触摸基本指针。那么,Assembly 是如何知道返回哪里的呢?

谢谢。

【问题讨论】:

  • 程序集是一种语言。它什么都不“知道”。
  • CALL 将 EIP 寄存器值压入堆栈。 RET 恢复它。如果你的书没有解释这一点,请找另一本书,非常基本的东西。
  • 返回时不使用 EBP 基指针。返回地址是堆栈顶部的任何值。 EBP 只是一个“助手”,子程序可以使用它来更轻松地查找参数和本地堆栈变量

标签: assembly x86


【解决方案1】:

ret 指令只是弹出堆栈顶部的任何内容并将其用作返回地址。没有什么花哨的事情发生。如果您不跟踪堆栈大小并且碰巧在错误的时间执行ret(在堆栈顶部不是正确返回地址的上下文中),那么您就有了错误。

【讨论】:

    【解决方案2】:

    x86 有几种返回方法。

    最简单的是RET指令。这会从堆栈中获取当前地址,将其放入 EIP 中,然后瞧。

    因为 x86 架构是 CISC,所以它也有扩展功能,所以你有一个带有大小参数的 RET。这个是用来替换基本的 RET 指令在运行 ADD 到堆栈 AFTER THE RETURN 之后:

    RET 16
    -- becomes
    RET
    ADD 16, esp
    

    这允许您在返回的同时编写一条指令来取消您的参数变量。调用约定在这种情况下非常重要,因为调用者负责将参数变量添加到堆栈中,而被调用者将它们从堆栈中移除!

    因为 x86 处理器带有所谓的段,所以有一个 RET 指令可以从堆栈中检索一个段以及一个 IP 地址。您现在每天都不会在常规程序中使用此指令,因为内存模型足够大,可以避免它,但它是可用的。这称为 FAR RET。

    此外,英特尔处理器支持 4 级保护,因此可以保护内核(用户程序无法访问),驱动程序也可以驻留在单独的保护级别中,以避免内核损坏驱动程序。 RET 指令也可用于在这些级别之间切换。再次在堆栈上找到信息。

    最后,对于中断处理程序也有一种特殊情况。在标准程序中,您永远不会看到那些仅在内核中使用的程序。每当处理器产生中断时,就会调用一个特殊的处理程序。该处理程序必须使用特殊的 IRET 指令(中断返回)返回。它也从堆栈中获取数据,但它保存了更多信息,例如寄存器和状态标志。

    ESP -> +-------+
           |  ...  |  <-- local variables
    EBP -> |  ...  |  <-- previous frame pointer (EBP)
           |  ...  |  <-- registers that cannot be modified are saved here
           |  EIP  |  <-- IP address saved by CALL instruction
           |  ...  |
           |  ...  |  <-- parameters to your function
           +-------+
           |  ...  |  <-- local variables of caller
    EBP -> |  ...  |  <-- previous frame pointer (EBP)
           .........
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-14
      • 2020-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多