调用 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
下面我说说我的想法,希望大家指正或者赞同。
当我知道答案是什么时,我就是这么想的,所以我不确定我的想法是否正确。
- eax = 0
- 我们入栈return地址,即下一行,即标签L1。
- 我们跳到L3。
- 我们入栈return地址,即下一行,即标签L4。
- 我们跳到L1。
- 我们入栈return地址,也就是下一行,即inc eax.
- 我们跳到L4。
- 我们跳转到 inc eax 所在的行,堆栈现在为空。
- eax = 1.
- 我们到此结束(在标签 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
事情是这样的:
在 foo
我们调用 func
。 foo.returned
的地址被放入栈中(比如,栈槽 -1)。
在 func
我们将 eax
设置为零。
接下来我们调用L3
。 func.returned
= L1
的地址入栈(slot -2)。
在L3
我们调用栈顶的双字。这是 func.returned
。 L3.returned
= L4
的地址被放入栈中(slot -3)。
在L1
我们调用栈顶的双字。这是 L3.returned
。 L1.returned
的地址被放入栈中(slot -4)。
在 L4
我们 return。这会将 L1.returned
(从插槽 -4)弹出到 eip
.
在 L1.returned
我们做 inc eax
,将 eax
设置为 1。
然后在 L2
我们 return。这会将 L3.returned
(从插槽 -3)弹出到 eip
.
在 L4
我们 return。这会将 func.returned
(从插槽 -2)弹出到 eip
。
在L1
我们调用栈顶的双字。这是 foo.returned
。 L1.returned
的地址被放入栈中(slot -2)。
在 foo.returned
处,我们执行我标记为 X
的任何内容。假设函数 returns 使用 retn
最终然后...
...我们return。这会将 L1.returned
(从插槽 -2)弹出到 eip
.
在 L1.returned
我们做 inc eax
。假设 X
没有改变 eax
那么我们现在有 eax
= 2.
然后在 L2
我们 return。这会将 foo.returned
(从插槽 -1)弹出到 eip
.
如果我的假设是正确的,那么 eax
最后就是 2。
注意,在栈顶调用return地址确实很奇怪。我无法想象它的实际用途。
另请注意,如果在调试器中,您 proceed-step 在 foo
中调用 func
,则在第 11 步,调试器可能 return 将控制权交给用户, 其中 eax
等于 1。但是此时堆栈不平衡。
我对esp指针有一些误解
以下是以前考试中显示的代码。 returned 值为 1。
func: xor eax,eax
call L3
L1: call dword[esp]
inc eax
L2: ret
L3: call dword[esp]
L4: ret
下面我说说我的想法,希望大家指正或者赞同。 当我知道答案是什么时,我就是这么想的,所以我不确定我的想法是否正确。
- eax = 0
- 我们入栈return地址,即下一行,即标签L1。
- 我们跳到L3。
- 我们入栈return地址,即下一行,即标签L4。
- 我们跳到L1。
- 我们入栈return地址,也就是下一行,即inc eax.
- 我们跳到L4。
- 我们跳转到 inc eax 所在的行,堆栈现在为空。
- eax = 1.
- 我们到此结束(在标签 L2)和 return 1.
我认为 eax
= 2 并且 func
的调用者被 L1
处的指令调用了一次。在下文中,我将跟踪执行以向您展示我的意思。
我 re-arranged 你的例子有点可读性。这是 NASM 来源。我认为这应该等同于原始版本(假设设置了
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
事情是这样的:
在
foo
我们调用func
。foo.returned
的地址被放入栈中(比如,栈槽 -1)。在
func
我们将eax
设置为零。接下来我们调用
L3
。func.returned
=L1
的地址入栈(slot -2)。在
L3
我们调用栈顶的双字。这是func.returned
。L3.returned
=L4
的地址被放入栈中(slot -3)。在
L1
我们调用栈顶的双字。这是L3.returned
。L1.returned
的地址被放入栈中(slot -4)。在
L4
我们 return。这会将L1.returned
(从插槽 -4)弹出到eip
.在
L1.returned
我们做inc eax
,将eax
设置为 1。然后在
L2
我们 return。这会将L3.returned
(从插槽 -3)弹出到eip
.在
L4
我们 return。这会将func.returned
(从插槽 -2)弹出到eip
。在
L1
我们调用栈顶的双字。这是foo.returned
。L1.returned
的地址被放入栈中(slot -2)。在
foo.returned
处,我们执行我标记为X
的任何内容。假设函数 returns 使用retn
最终然后......我们return。这会将
L1.returned
(从插槽 -2)弹出到eip
.在
L1.returned
我们做inc eax
。假设X
没有改变eax
那么我们现在有eax
= 2.然后在
L2
我们 return。这会将foo.returned
(从插槽 -1)弹出到eip
.
如果我的假设是正确的,那么 eax
最后就是 2。
注意,在栈顶调用return地址确实很奇怪。我无法想象它的实际用途。
另请注意,如果在调试器中,您 proceed-step 在 foo
中调用 func
,则在第 11 步,调试器可能 return 将控制权交给用户, 其中 eax
等于 1。但是此时堆栈不平衡。