Visual c++ 使用push/lea offset/sub而不是push/mov/sub制作堆栈帧? 我分析了用VC++编译的C++函数(可能是VS10),以前从未见过这种序言模式。

Visual c++ 使用push/lea offset/sub而不是push/mov/sub制作堆栈帧? 我分析了用VC++编译的C++函数(可能是VS10),以前从未见过这种序言模式。,visual-c++,assembly,x86,reverse-engineering,Visual C++,Assembly,X86,Reverse Engineering,这似乎是一个stdcall,但开场白有点不同: stdcall通常使用以下开场白模式启动函数: push ebp mov ebp, esp sub esp, const 然而,我正在分析的这个函数的序言如下: push ebp lea ebp, [esp - 0x4C] sub esp, 0x80 分析使用相同序言/尾声的同一PE中的其他函数,RETN似乎总是在离开指令之后出现,这是我在常规的cdecl函数中从未见过的另一件事 我想知道为什么编译器会这样做。它

这似乎是一个stdcall,但开场白有点不同:

stdcall通常使用以下开场白模式启动函数:

push   ebp
mov    ebp, esp
sub    esp, const
然而,我正在分析的这个函数的序言如下:

push   ebp
lea    ebp, [esp - 0x4C]
sub    esp, 0x80
分析使用相同序言/尾声的同一PE中的其他函数,RETN似乎总是在离开指令之后出现,这是我在常规的cdecl函数中从未见过的另一件事

我想知道为什么编译器会这样做。它似乎在ESP上打开了空间(通过
子ESP,const
),那么为什么它通过
lea ebp,[ESP-const]
打开另一个堆栈块呢

有人知道编译器为什么这样做吗?这与stdcall不同吗

我在网上做了一些研究,也研究了这个特定的汇编代码来找出答案,但没有发现它的必要性

提前谢谢

使用序言/尾声的屏幕进行编辑:

开场白

结语

对函数的调用

由于评论中没有人给出答案,我们现在开始

“通常的”stdcall与我在本主题中讨论的stdcall之间在序言/尾声中存在差异的原因是编译器对代码密度的优化


偏移
EBP
在序言中,编译器能够缩短函数中访问某些堆栈变量的指令。它现在可以使用单字节位移访问更大的堆栈内存块(取决于序言偏移量
EBP
),使用
EBP+N
EBP-M
访问局部变量(其中
N
M
是一个介于-128和+127之间的常数)。当然,访问EBP偏移量以外的变量的指令将使用4字节的位移,但使用此优化技巧,该函数的整体代码将更短。

lea
不保留任何内容,它只是偏移帧指针。您可以使用它来处理带有
sbyte
偏移量的堆栈帧的更多部分。使用带有参数的
ret
是因为调用约定是
stdcall
我真的不明白关于
leave
的问题。帧指针偏移只是代码密度的一个技巧,避免了必须对32位位移进行编码。除了harold所说的(关于偏移帧指针以获得代码密度),我在截图中很好奇是否还有更多关于尾声代码的内容。你会看到在尾声中做了三件事。const被添加回EBP,
leave
,然后
ret
。当然,正如Harold试图说的,这里的整个想法是,如果在序言中偏移EBP,并且可以获得大多数堆栈访问,以适应有符号字节(+127到-128)然后,可以使用1字节位移而不是4字节位移来缩短访问堆栈变量的指令。