如何在Windows函数调用中设置堆栈?
首先,我想说我在汇编方面有足够的背景知道大部分需要知道的功能汇编程序员。 不幸的是,我不明白Windows API调用的返回地址是如何工作的。
下面是使用MinGW's作为汇编器和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
函数的返回地址放在堆栈上。 然后,这就是我困惑的地方,函数将所有参数从堆栈中弹出。
我很困惑,因为,由于堆栈先进先出,在我脑海中弹出堆栈上的参数时,它会首先弹出返回地址。 争论是第一位的,接下来的回复地址是这样的,所以它在技术上首先被取消。
我的问题是,在通过push操作将参数传递给函数调用和放置在堆栈上的返回地址之后,堆栈的布局是什么样的? 当函数执行时,参数和返回地址如何从堆栈中弹出? 最后,返回地址如何从堆栈中弹出,函数调用返回到返回地址中指定的地址?
几乎所有的Windows API函数都使用stdcall调用约定。 这与普通的“cdecl”约定一样,除非你看到被调用函数负责在返回时去除参数。 它使用RET指令执行此操作,该指令采用可选的立即操作数。 此操作数是首次弹出返回值后弹出堆栈的字节数。
在cdecl和stdcall调用约定中,函数的参数在函数执行时不会从堆栈弹出。 它们留在堆栈上并使用ESP或EBP相对寻址进行访问。 所以当ExitProcess需要访问它的参数时,它使用一个像mov 4(%esp), %eax
或mov 4(%ebp), %eax
这样的指令。