【问题标题】:Why would an assembly programmer want to subtract from ebp in this location instead of esp?为什么汇编程序员要在这个位置减去 ebp 而不是 esp?
【发布时间】:2018-02-22 05:13:57
【问题描述】:

我对 ebp 和 esp 的使用与在 x86 汇编语言中设置堆栈帧的关系有些困惑。在以下代码中:

section '.code' code readable executable        ; define the code section of the file
main:                ;main label is where execution begins
push ebp
mov ebp,esp          ;set up the base ptr
sub ebp,4            ;subtract 4 from ebp
mov dword [esp],msg
call [printf]
mov dword [esp],p   ; pass pause>nul cmd to system to hold the box open
call [system]
mov dword [esp],0              ;pass NULL to exit
call [exit]   

程序员从 ebp 中减去了 4,但我不知道为什么。通常,我在这里看到的是 ESP 的减法,而不是 EBP。这里从 EBP 中减去的目的是什么?

【问题讨论】:

  • 这段代码还有更多内容吗?或者这是整个sn-p?就这里具体发生的事情而言......他们使用堆栈指针将参数传递给他们调用的函数(而不是push'ing他们到堆栈他们只是将数据直接移动到堆栈)。
  • @SimonWhitehead 不,先生,这是整个程序没有 .data 和 .idata 部分,其中声明了使用的变量和导入
  • 我想我明白你在说什么......所以基本上,他们将 msg 的内存地址(因此 ptr 为 4 个字节)放入从 ebp 中减去的 4 个字节中?
  • SO 替代方案是sub esp, 4,然后是push msg
  • 我认为它没有任何实际用途。这个sn-p挺奇怪的。

标签: assembly x86 fasm stack-frame


【解决方案1】:

绝对是一个错误

push ebp              ; 1
mov ebp,esp           ; 2
sub ebp,4             ; 3
mov dword [esp],msg   ; 4

因为指令2和3只修改ebp寄存器(而不是esp)指令4会覆盖指令1中压入的值。

我怀疑程序员是否有意这样做。

【讨论】:

  • 此外,程序员从不使用ebp 寄存器。如果他们真的想这样设置ebp,它会被写成lea ebp, [esp-4]
  • @Martin 我明白了,这是因为 push ebp 将基 ptr 的地址放入堆栈指针,但我们随后将 msg 的地址放入堆栈指针的同一位置因为我们在推送之后从未移动堆栈指针?
  • @the_endian 是的,push x 与“sub esp,sizeof xmov [esp],x”几乎相同(它有点复杂,但在简化意义上......)......还有另一个@ 987654330@ 将覆盖最后一个push 的值。所以被剪断的可以只做push msg(可能是push offset msg,取决于使用的汇编程序)而不是那4条指令(并保持ebp为原始值)。
  • 如果“程序员的意图”是 push msg,他的 4 条指令是(一种非常奇怪但有效的)实现方式。他不清理堆栈,因此空间也被重新用于“系统”和“退出”调用。看起来很奇怪,根本不需要破坏 ebp,但对我来说看起来更像是一个奇怪的黑客而不是一个错误。但是 2) 和 3) 绝对不需要
【解决方案2】:

您的代码似乎来自FASM tutorial,完整代码如下所示:

format PE console
entry main

include 'macro/import32.inc'

section '.data' data readable writeable
msg db "hello world!",0
p db "pause>nul",0

section '.code' code readable executable
main:
push ebp
mov ebp,esp
sub ebp,4
mov dword [esp],msg
call [printf]
mov dword [esp],p
call [system]
mov dword [esp],0
call [exit]

section '.idata' import data readable
library msvcrt,'msvcrt.dll'
import msvcrt,\
printf,'printf',\
system,'system',\
exit,'exit'

作者在代码描述中这样写:

从我们的入口点开始 标签 main,我设置了一个堆栈帧并在堆栈上分配 4 个字节 esp 的值中减去 4。现在在那个 4 字节范围内,我将 msg 的地址放在那里并调用 printf,

这让我相信作者的实际指示是:

sub esp, 4

代码实际上有一个错字。描述正确,代码错误。

【讨论】:

  • "mov ebp, esp" 和 "sub ebp,4" 都无关紧要,因为此后不再需要 ebp。 “在堆栈上保留 4 个字节”(以便能够将参数提供给调用的函数)已经完成(通过推送 ebp - 顺便说一句永远不会弹出);之后,3 个调用中的每一个的参数都在堆栈上重复使用该空间 3 次,而不是(重复)推送调用的值,并在调用后清理堆栈。堆栈框架设置从未使用过,分配(最终)通过推送 ebp 完成,因此它正在工作。即使不是这样打算的
  • @Tommylee2k :您误读了代码。 sub esp, 4(假设我们修复了错误)并非无关紧要。作者使用 EBP 作为典型的栈帧。当他使用mov dword [esp],msg 时,他正在使用堆栈上分配的空间,而不是push msg。教程中的作者实际上解释了这一点,他展示了他使用push msg 方法的替代版本。
  • 不是你的。他的(代码的作者)。他的堆栈帧从未使用过,因此将 esp 存储在 ebp 中并从中减去 -4 都是无关紧要的。这里仍然不需要“sub eSp,4”。调用参数所需的空间已经存在(通过按下 ebp )。在 HIS 代码中,没有“add esp,4”,但他推送了 ebp,它被调用的参数覆盖。可以说“偶然工作”
  • @Tommylee2k :他的堆栈帧从未正确使用,因为他在 C 运行时中调用了exit 函数,将其短路。然而,他可能通常有一个完整的堆栈帧。为了完整起见,他可能应该将堆栈序言放在最后。当然作者没有使用它,但这只是一个例子。带有sub esp, 4 的代码版本没有错误,只是恰好有一个不需要的序言(堆栈帧)。
  • 完全正确。从功能上讲,他的代码正在运行(甚至很幸运),但他(显然)完全搞砸了
猜你喜欢
  • 2013-11-13
  • 2013-01-29
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
  • 2011-07-25
  • 2013-02-07
  • 2017-04-03
相关资源
最近更新 更多