Assembly ret,retn,retf-如何使用它们

Assembly ret,retn,retf-如何使用它们,assembly,x86,Assembly,X86,我有以下asm代码: ; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) _wWinMain@16 proc near var_8= dword ptr -8 var_4= dword ptr -4 hInstance= dword ptr 8 hPrevInstance= dword ptr 0Ch lpCmdLine= dword p

我有以下asm代码:

; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
_wWinMain@16 proc near

var_8= dword ptr -8
var_4= dword ptr -4
hInstance= dword ptr  8
hPrevInstance= dword ptr  0Ch
lpCmdLine= dword ptr  10h
nShowCmd= dword ptr  14h

push    ebp
mov     ebp, esp
sub     esp, 8
mov     [ebp+var_4], 5
mov     eax, [ebp+var_4]
add     eax, 1
mov     [ebp+var_8], eax
xor     eax, eax
mov     esp, ebp
pop     ebp
retn    10h

从我读到的,你有三种返回指令:ret,retn和retf,意思是返回,返回近和返回远。它们允许一个可选参数nBytes,我猜这是从定义的变量中弹出的字节数。什么时候应该使用retn或retf而不是ret?如何计算可选参数nBytes?

实际上只有两种不同的返回,retn(近返回)和retf(远返回)。当您仅使用ret时,汇编器或编译器足够聪明,可以选择需要哪一个。近返回是跳转到现有代码段内,远返回是跳转到不同的代码段。在Windows上,您只有一个代码段,因此ret应该只是retn的助记符。单独的retn和retf指令可以追溯到以前分段内存模型很常见的时代。目前运行的几乎所有32位x86系统都使用一种平坦的、非分段的内存模型


不带参数的Ret从堆栈中弹出返回地址并跳转到它。一些调用约定(如u stdcall)指定被调用函数清理堆栈。在这种情况下,它们调用具有字节数的ret,以将这些参数从堆栈中弹出。16个字节是winmain函数的参数。

实际上有两种类型:
retn
retf
。第三个
ret
由汇编程序编码为前两个中的一个


不同之处在于
retn
(返回附近)将仅弹出指令指针(IP)。当retf(返回far)将弹出指令指针(IP)和代码段(CS)。

在助记符ret N中,N是堆栈上参数的大小。在这种情况下,4个DWORD的值为4*4=16(10h)。

但这仅适用于被调用方负责堆栈清理时的调用约定。在cdecl约定的情况下,ret应该没有任何数字,因为调用方负责堆栈清理。

对不起,我没有得到最后一部分。使用retn时,我应该清除什么?这两个变量+ebp(4字节)?正如你所说,8+4=12,但代码显示为16。剩下的4个字节是什么?对不起,忘记了调用约定是stdcall。我将更新我的答案。如果将函数epilog替换为ret 0ch,则调用函数中的ebp内容将不正确。要添加到此答案:对于64位Windows系统,在执行32位应用程序时,WoW64子系统使用两个代码段-一个用于32位代码(0x23),另一个用于64位代码(0x33)。后者主要由NTDLL在进行系统调用时使用,因为内核只运行64位代码,但恶意软件也可以在其应用程序代码中的这些代码段之间切换,以更好地避免防病毒产品的检测(一种称为天堂之门的技术).Ah所以ret N中的N指的是调用者传递的推送参数的数量,而不是我认为的局部变量。是吗?是的。这是推送参数的数量。最后通过mov esp、ebp命令弹出局部变量