- 搞清楚调用约定
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 ENDPmov 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 ENDPecx存放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的标准返回值定义:正数、零、负数。
调用约定
cdeclC调用约定
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 ; fun2fastcall快速调用约定
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值。