调用 func 将返回哪个值

Which value will be returned by calling func

我对esp指针有一些误解

以下是以前考试中显示的代码。 returned 值为 1。

func:   xor eax,eax
        call L3
        L1: call dword[esp]
        inc eax
        L2: ret
        L3: call dword[esp]
        L4: ret

下面我说说我的想法,希望大家指正或者赞同。 当我知道答案是什么时,我就是这么想的,所以我不确定我的想法是否正确。

  1. eax = 0
  2. 我们入栈return地址,即下一行,即标签L1。
  3. 我们跳到L3。
  4. 我们入栈return地址,即下一行,即标签L4。
  5. 我们跳到L1。
  6. 我们入栈return地址,也就是下一行,即inc eax.
  7. 我们跳到L4。
  8. 我们跳转到 inc eax 所在的行,堆栈现在为空。
  9. eax = 1.
  10. 我们到此结束(在标签 L2)和 return 1.

我认为 eax = 2 并且 func 的调用者被 L1 处的指令调用了一次。在下文中,我将跟踪执行以向您展示我的意思。

我 re-arranged 你的例子有点可读性。这是 NASM 来源。我认为这应该等同于原始版本(假设设置了 ,即您在正常 32 位模式下 运行)。

        bits 32
func:
        xor eax, eax
        call L3
.returned:
L1:
        call near [esp]
.returned:
        inc eax
L2:
        retn
L3:
        call near [esp]
.returned:
L4:
        retn

现在假设我们从执行此操作的某个函数开始:

foo:
        call func
.returned:
        X
        retn

事情是这样的:

  1. foo 我们调用 funcfoo.returned 的地址被放入栈中(比如,栈槽 -1)。

  2. func 我们将 eax 设置为零。

  3. 接下来我们调用L3func.returned = L1 的地址入栈(slot -2)。

  4. L3我们调用栈顶的双字。这是 func.returnedL3.returned = L4 的地址被放入栈中(slot -3)。

  5. L1我们调用栈顶的双字。这是 L3.returnedL1.returned的地址被放入栈中(slot -4)。

  6. L4 我们 return。这会将 L1.returned(从插槽 -4)弹出到 eip.

  7. L1.returned 我们做 inc eax,将 eax 设置为 1。

  8. 然后在 L2 我们 return。这会将 L3.returned(从插槽 -3)弹出到 eip.

  9. L4 我们 return。这会将 func.returned(从插槽 -2)弹出到 eip

  10. L1我们调用栈顶的双字。这是 foo.returnedL1.returned的地址被放入栈中(slot -2)。

  11. foo.returned 处,我们执行我标记为 X 的任何内容。假设函数 returns 使用 retn 最终然后...

  12. ...我们return。这会将 L1.returned(从插槽 -2)弹出到 eip.

  13. L1.returned 我们做 inc eax。假设 X 没有改变 eax 那么我们现在有 eax = 2.

  14. 然后在 L2 我们 return。这会将 foo.returned(从插槽 -1)弹出到 eip.

如果我的假设是正确的,那么 eax 最后就是 2。


注意,在栈顶调用return地址确实很奇怪。我无法想象它的实际用途。

另请注意,如果在调试器中,您 proceed-step 在 foo 中调用 func,则在第 11 步,调试器可能 return 将控制权交给用户, 其中 eax 等于 1。但是此时堆栈不平衡。