- 搞清楚调用约定
stdcall
、cdecl
、fastcall
,决定了参数传递、谁平衡栈。 - 不能以
push
的个数来确定参数的个数。 - 如果有参数,看
ret
平栈状态。

优化
ebp
省略优化。(帧优化)- 内联优化
- 全程序优化(高版本限定):
- 改调用约定
- 变量传播
内联优化
使用\Ob0|1|2|3
来设置内联优化级别。
0 /Od 下的默认值。 禁用内联扩展。
1 仅允许对标记为 inline、__inline 或 __forceinline 的函数或是在类声明中定义的 C++ 成员函数中进行扩展。
2 /O1 和 /O2 下的默认值。 允许编译器扩展任何未明确标记为无内联的函数。
3 此选项指定比 /Ob2 更积极的内联,但具有相同的限制。 /Ob3 选项自 Visual Studio 2019 起可用。
库函数容易被内联优化strlen
,且没有逆向恢复价值,release
版会被内联优化。
- 库函数强制内联。
- 可以使用
#pragma function(strcpy, strlen)
在msvc
中强制库函数使用内建函数,而不进行内联。

内联后特征
strlen

int main(int argc, char *argv[]){ printf("strlen: %d", strlen(argv[0])); return 0;}
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage`string' DB 'strlen: %d', 00H ; `string'
_argc$ = 8 ; size = 4_argv$ = 12 ; size = 4_main PROC ; COMDAT mov eax, DWORD PTR _argv$[esp-4] mov eax, DWORD PTR [eax] lea edx, DWORD PTR [eax+1] npad 7$LL3@main: mov cl, BYTE PTR [eax] inc eax test cl, cl jne SHORT $LL3@main sub eax, edx push eax push OFFSET `string' call _printf add esp, 8 xor eax, eax ret 0_main ENDP
mov eax, DWORD PTR _argv$[esp-4]
mov eax, DWORD PTR [eax]
将首地址给到eax
。lea edx, DWORD PTR [eax+1]
,edx
中存放首地址+1的地址值。- 使用循环不断
test
结束符。 - 找到之后与
edx
相减,得到字符串长度。
strcpy

int main(int argc, char *argv[]){ char* buf = (char*)malloc(100); strcpy(buf, argv[1]); return 0;}
_argc$ = 8 ; size = 4_argv$ = 12 ; size = 4_main PROC ; COMDAT push 100 ; 00000064H call _malloc mov ecx, DWORD PTR _argv$[esp] add esp, 4 mov ecx, DWORD PTR [ecx+4]$LL3@main: mov dl, BYTE PTR [ecx] lea ecx, DWORD PTR [ecx+1] mov BYTE PTR [eax], dl lea eax, DWORD PTR [eax+1] test dl, dl jne SHORT $LL3@main xor eax, eax ret 0_main ENDP
ecx
存放argv
的首元素,ecx+4
前往下一个元素(指针4个字节)。mov BYTE PTR [eax], dl
将元素每次拷贝一个字节,传送到buf
中。
strcmp
int my_strcmp(const char* str1, const char* str2) { while (*str1 && (*str1 == *str2)) { str1++; str2++; } return *(unsigned char*)str1 - *(unsigned char*)str2;}
int main(int argc, char *argv[]){ char* buf = (char*)malloc(100); memset(buf, 0xcc, 100); printf("%d", strcmp(buf, argv[2])); return 0;}
# License: MSVC Proprietary# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage`string' DB '%d', 00H ; `string'
_argc$ = 8 ; size = 4_argv$ = 12 ; size = 4_main PROC ; COMDAT push esi push 100 ; 00000064H call _malloc push 100 ; 00000064H mov esi, eax push 204 ; 000000ccH push esi call _memset mov ecx, DWORD PTR _argv$[esp+16] add esp, 16 ; 00000010H mov eax, DWORD PTR [ecx+8]$LL3@main: mov cl, BYTE PTR [esi] cmp cl, BYTE PTR [eax] jne SHORT $LN4@main test cl, cl je SHORT $LN5@main mov cl, BYTE PTR [esi+1] cmp cl, BYTE PTR [eax+1] jne SHORT $LN4@main add esi, 2 add eax, 2 test cl, cl jne SHORT $LL3@main$LN5@main: xor eax, eax push eax push OFFSET `string' call _printf add esp, 8 xor eax, eax pop esi ret 0$LN4@main: sbb eax, eax or eax, 1 push eax push OFFSET `string' call _printf add esp, 8 xor eax, eax pop esi ret 0_main ENDP
$LN4@main: sbb eax, eax ; 根据 ZF 设置 eax 的符号位 or eax, 1 ; eax = -1 或 1,表示不相等 push eax push OFFSET `string' ; 准备打印结果 call _printf add esp, 8
如果发现字符不相等:
- 使用
sbb eax, eax
和or eax, 1
来设置eax
的值为-1
或1
,取决于哪个字符串更大。 - 这种逻辑是
strcmp
的标准返回值定义:正数、零、负数。
调用约定
cdecl
C调用约定
int __cdecl fun1(int n1, int n2){ return n1 + n2;}
- 参数从右往左入栈。
- 调用者平栈。
汇编特征:
ret
不含操作数。

stdcall
标准调用约定
int __stdcall fun2(int n1, int n2){ return n1 + n2;}
- 函数内部平栈。
- 参数从右往左入栈。
; 调用push esipush edicall int fun2(int,int) ; fun2; 不需要外部平栈
; 内部实现_n1$ = 8 ; size = 4_n2$ = 12 ; size = 4int fun2(int,int) PROC ; fun2, COMDAT mov eax, DWORD PTR _n1$[esp-4] add eax, DWORD PTR _n2$[esp-4] ret 8 ; 特征 ret后面带值int fun2(int,int) ENDP ; fun2
fastcall
快速调用约定
int __fastcall fun3(int n1, int n2){ return n1 + n2;}
ecx
、edx
作为前两个参数,多余的参数,从右往左入栈。- 函数内部平栈。
汇编特征:
- 未保存
ecx
、edx
就使用寄存器。
; 实现int fun3(int,int) PROC ; fun3, COMDAT lea eax, DWORD PTR [ecx+edx] ret 0int fun3(int,int) ENDP ; fun3
; 调用mov edx, esimov ecx, edicall int fun3(int,int) ; fun3
函数返回值
x86
机器上返回64
位返回值->edx.eax
值。