【问题标题】:If I am using the Win32 entry point, should I increase the esp value to remove the variables from the stack?如果我使用 Win32 入口点,我应该增加 esp 值以从堆栈中删除变量吗?
【发布时间】:2018-03-01 18:03:26
【问题描述】:

如果我使用的是 Win32 入口点并且我有以下代码(在 NASM 中):

extern _ExitProcess@4

global _start

section .text
_start:
    mov ebp, esp

    ; Reserve space onto the stack for two 4 bytes variables
    sub esp, 4
    sub esp, 4

    ; ExitProcess(0)
    push 0
    call _ExitProcess@4

现在在退出进程之前,我是否应该像使用任何“正常”函数一样增加 esp 值以从堆栈中删除两个变量?

【问题讨论】:

  • 这不是和你上一个问题基本一样
  • 如我所见,如果你理解了最后一个问题的答案,那么你可以在这里应用这些知识,不是吗?
  • 写两次sub esp, 4 总是比sub esp, 8 差。在汇编语言中,没有编译器可以为您优化冗余。
  • 您正在调用_Noreturn 函数,没有返回。因此,与任何其他函数调用一样,不,您无需在进行另一次调用之前执行任何操作。
  • Johan 的回答是为了回答有关从应用程序的 main() 函数返回的问题,或者至少 Johan 是这样解释的。与您的情况完全不同,您使用的是 Win32 入口点并且无论如何都不会从它返回。

标签: winapi assembly x86 nasm


【解决方案1】:

ExitProcess api 可以从任何地方调用。在任何功能和子功能中。和堆栈指针当然可以是任何。您不需要将任何寄存器(包括堆栈指针)设置为某些(以及哪些?)值。所以回答 - 你不需要增加 esp


如@HarryJohnston 所述,当然堆栈必须有效且对齐。在 any api 调用之前和之前。 ExiProcess 是常用的 api。并且可以作为任何其他 api 调用。和任何其他 api 一样,它只需要有效的堆栈,而不需要具体的堆栈指针值。非易失性寄存器只需要恢复我们返回给调用者。但ExiProcess 不返回给调用者。它永远不会回来

所以规则非常简单 - 如果你从任何函数返回(入口点或绝对任何 - 没关系) - 我们需要恢复非易失性寄存器(堆栈指针 esprsp 基于调用约定)并返回。如果我们不返回调用者 - 我们不需要恢复/保留任何寄存器。如果我们从线程或进程入口点返回,尽管有很好的做法,但也恢复所有寄存器 - 在当前的 Windows 实现中 - 即使我们不这样做,任何方式都可以工作,因为 kernel32 shell 调用者只需调用 ExitThread 之后我们返回。它在这里不使用任何非易失性寄存器或局部变量。所以即使没有从入口点恢复它,代码也可以工作,但无论如何恢复它会好得多

【讨论】:

  • +1。不过,为了明确起见,当您调用 ExitProcess 时,堆栈指针必须是有效,也就是说,它必须正确对齐(如果适用)并指向堆栈分配。例如,如果它被设置为零,程序将崩溃而不是干净地退出。但是,您在堆栈中的 何处 并不重要。从哪个线程调用它甚至都没有关系!
  • @HarryJohnston - 当然堆栈必须有效且对齐。我只是假设它是默认的。我试着对 OP 说 - ExiProcess 是通常的 api。并且可以作为任何其他 api 调用。和任何其他 api 一样,它只需要有效的堆栈,而不需要具体的值。非易失性寄存器只需要恢复我们返回给调用者。但ExiProcess 不返回给调用者。它永远不会回来
  • @Harry Johnston 如果你不介意的话,我有两个问题:1) “堆栈指针必须是有效的,也就是说,它必须正确对齐” “正确对齐”是什么意思,你的意思是esp必须对齐4个字节,当我只调用ExitProcess()或调用时它必须对齐吗?有什么功能吗? 2)“并指向堆栈分配中的某个位置” “堆栈分配”是什么意思,你的意思是esp可以有除@987654331之外的任何值@?
  • @user8426277 - 1) 在 x86 esp=4*n 中的任何 api 调用之前必须是。在 amd64 rsp=16*n 必须是。在自己的函数内部,您正式可以具有任何堆栈指针值(如果这是没有中断的用户模式),但几乎总是我们在堆栈指针始终有效时编写代码。 2)“堆栈分配” - 这不清楚吗?一些内存块。在 Windows 中,每个线程都有自己的堆栈。它的边界写在NT_TIB。堆栈必须在 [StackLimit, StackBase) 范围内。如果您交换堆栈 - 您也必须更新 NT_TIB 中的数据(例如查看 SwitchToFiber
  • @user8426277,请注意,这些只是调用 any API 函数的相同要求。您调用 ExitProcess 而不是 CreateFile 的事实没有任何区别。当然,如果堆栈指针指向未提交的内存(包括但不限于零),那么 any call 将失败,因为call 命令所做的第一件事就是推送返回地址,所以你会遇到内存访问冲突。
猜你喜欢
  • 1970-01-01
  • 2017-06-10
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-29
  • 2012-03-31
  • 1970-01-01
相关资源
最近更新 更多