在书上看到如下内容
; 这里是Sub1 - C类型
:00401000 55 push ebp
:00401001 8BEC mov ebp, esp
:00401003 8B4508 mov eax, dword ptr [ebp+08]
:00401006 8B5D0C mov ebx, dword ptr [ebp+0C]
:00401009 C9 leave
:0040100A C3 ret
; 这里是Sub2 - PASCAL类型
:0040100B 55 push ebp
:0040100C 8BEC mov ebp, esp
:0040100E 8B450C mov eax, dword ptr [ebp+0C]
:00401011 8B5D08 mov ebx, dword ptr [ebp+08]
:00401014 C9 leave
:00401015 C20800 ret 0008
; 这里是Sub3 — StdCall类型
:00401018 55 push ebp
:00401019 8BEC mov ebp, esp
:0040101B 8B4508 mov eax, dword ptr [ebp+08]
:0040101E 8B5D0C mov ebx, dword ptr [ebp+0C]
:00401021 C9 leave
:00401022 C20800 ret 0008
…
; 这里是invoke Sub1,1,2 — C类型
:00401025 6A02 push 00000002
:00401027 6A01 push 00000001
:00401029 E8D2FFFFFF call 00401000
:0040102E 83C408 add esp, 00000008
; 这里是invoke Sub2,1,2 — PASCAL类型
:00401031 6A01 push 00000001
:00401033 6A02 push 00000002
:00401035 E8D1FFFFFF call 0040100B
; 这里是invoke Sub3,1,2 — StdCall类型
:0040103A 6A02 push 00000002
:0040103C 6A01 push 00000001
:0040103E E8D5FFFFFF call 00401018
可以清楚地看到,在参数入栈顺序上,C类型和StdCall类型是先把右边的参数先压入堆栈,而PASCAL类型是先把左边的参数压入堆栈。在堆栈平衡上,C类型是在调用者在使用call指令完成后,自行用add esp,8指令把8个字节的参数空间清除,而PASCAL和StdCall的调用者则不管这个事情,堆栈平衡的事情是由子程序用ret 8来实现的(ret指令后面加一个操作数表示在ret后把堆栈指针esp加上操作数)。
我有一点疑惑就是,调整堆栈使其平衡不就是为了让子程序能够正常的返回吗?那C类型在call之后才调整堆栈,子程序是如何正常返回到call的地方的呢?
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)