首先,我想说我有足够的汇编背景知识,可以理解成为一名函数式汇编程序员所需了解的大部分知识。不幸的是,我不明白 Windows API 调用在返回地址方面是如何工作的。
下面是使用 MinGW 的 as 作为汇编器和 MinGW 的 ld 作为链接器使用 Windows 的 GAS 程序集编写的一些示例代码...
.extern _ExitProcess@4
.text
.globl _main
_main:
pushl $0
call _ExitProcess@4
此代码在汇编后编译运行...
as program.s -o program.o
并链接它...
ld program.o -o program.exe -lkernel32
据我了解,Windows API 调用通过推送指令获取参数,如上所示。然后在通话期间;
call _ExitProcess@4
函数的返回地址放在栈上。然后,这就是我感到困惑的地方,该函数将所有参数从堆栈中弹出。
我很困惑,因为堆栈是后进先出的,在我看来,在堆栈上弹出参数时,它会先弹出返回地址。参数在前,然后是返回地址,因此从技术上讲,它会先弹出。
我的问题是,在通过压入操作将参数传递给函数调用并将返回地址放在堆栈上之后,堆栈的布局是什么样的?函数在执行时如何将参数和返回地址从堆栈中弹出?最后,返回地址如何从堆栈中弹出,函数调用返回到返回地址中指定的地址?
最佳答案
几乎所有 Windows API 函数都使用 stdcall calling convention .这就像正常的“cdecl”约定一样工作,除了你已经看到被调用的函数负责在返回时删除参数。它使用 RET 指令执行此操作,该指令采用可选的立即数操作数。此操作数是在第一次弹出返回值后从堆栈中弹出的字节数。
在 cdecl 和 stdcall 调用约定中,函数的参数在函数执行时不会从堆栈弹出。它们留在堆栈中并使用 ESP 或 EBP 相对寻址进行访问。因此,当 ExitProcess 需要访问其参数时,它会使用类似 mov 4(%esp), %eax
或 mov 4(%ebp), %eax
的指令。
https://stackoverflow.com/questions/40393158/