C 函数的格式
函数名、参数名和变量名的命名规则:只包含字母、数字和_(下划线)且不能以数字开头。
函数名、参数名和变量名不能使用 C 的关键词,如下所示:
1
| auto break case char const continue default do double else enum extern float for
|
无参数,无返回值的函数格式
有参数,无返回值
1 2 3 4
| void 函数名(参数类型 参数名,参数类型 参数名) { }
|
无参数,有返回值
有参数,有返回值
1 2 3 4 5
| 返回值类型 函数名(参数类型 参数名,参数类型 参数名....) { return 具体的值,必须与返回值类型匹配 }
|
C 函数的调用
1 2 3 4 5 6 7 8 9 10 11 12
| #include "stdafx.h"
void Function() { }
int main(int argc, char* argv[]) { Function(); return 0; }
|
函数的反汇编分析-空函数
源码
1 2 3 4 5 6 7 8 9 10 11 12
| #include "stdafx.h"
void Function() { }
int main(int argc, char* argv[]) { Function(); return 0; }
|
反汇编分析
整体 main 函数的反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 11: int main(int argc, char* argv[]) 12: { 004010C0 push ebp 004010C1 mov ebp,esp 004010C3 sub esp,40h 004010C6 push ebx 004010C7 push esi 004010C8 push edi 004010C9 lea edi,[ebp-40h] 004010CC mov ecx,10h 004010D1 mov eax,0CCCCCCCCh 004010D6 rep stos dword ptr [edi] 13: Function(); 004010D8 call @ILT+30(Function) (00401023) 14: return 0; 004010DD xor eax,eax 15: } 004010DF pop edi 004010E0 pop esi 004010E1 pop ebx 004010E2 add esp,40h 004010E5 cmp ebp,esp 004010E7 call __chkesp (00401330) 004010EC mov esp,ebp 004010EE pop ebp 004010EF ret
|
从 main 的一个整体反汇编,我们可以看到函数 Function()
在反汇编中只是 call 00401023
。其中 call 是调用,00401023 才是真正的 Function() 函数
空函数 Function 的反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 00401023 jmp Function (0040eb30)
6: void Function() 7: { 0040EB30 push ebp 0040EB31 mov ebp,esp 0040EB33 sub esp,40h 0040EB36 push ebx 0040EB37 push esi 0040EB38 push edi 0040EB39 lea edi,[ebp-40h] 0040EB3C mov ecx,10h 0040EB41 mov eax,0CCCCCCCCh 0040EB46 rep stos dword ptr [edi] 8: 9: } 0040EB48 pop edi 0040EB49 pop esi 0040EB4A pop ebx 0040EB4B mov esp,ebp 0040EB4D pop ebp 0040EB4E ret
|
空函数,无参数的反汇编分为如下几部分:
- 保存栈底
- 提升堆栈
1 2
| 0040EB31 mov ebp,esp 0040EB33 sub esp,40h
|
- 保护现场
1 2 3
| 0040EB36 push ebx 0040EB37 push esi 0040EB38 push edi
|
- 填充缓冲区
1 2 3 4
| 0040EB39 lea edi,[ebp-40h] 0040EB3C mov ecx,10h 0040EB41 mov eax,0CCCCCCCCh 0040EB46 rep stos dword ptr [edi]
|
- 恢复现场
1 2 3
| 0040EB48 pop edi 0040EB49 pop esi 0040EB4A pop ebx
|
- 降低堆栈
1 2
| 0040EB4B mov esp,ebp 0040EB4D pop ebp
|
- 函数返回
函数的反汇编分析-简单功能(无返回值)
源码
1 2 3 4 5 6 7 8 9 10 11 12
| #include "stdafx.h"
void Function(int x,int y) { x=x+y; }
int main(int argc, char* argv[]) { Function(1,2); return 0; }
|
反汇编分析
函数的参数调用
1 2 3 4 5
| 13: Function(1,2); 004010D8 push 2 004010DA push 1 004010DC call @ILT+35(Function) (00401028) 004010E1 add esp,8
|
函数的反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 00401028 jmp Function (0040eb30)
6: void Function(int x,int y) 7: { 0040EB30 push ebp 0040EB31 mov ebp,esp 0040EB33 sub esp,40h 0040EB36 push ebx 0040EB37 push esi 0040EB38 push edi 0040EB39 lea edi,[ebp-40h] 0040EB3C mov ecx,10h 0040EB41 mov eax,0CCCCCCCCh 0040EB46 rep stos dword ptr [edi] 8: x=x+y; 0040EB48 mov eax,dword ptr [ebp+8] 0040EB4B add eax,dword ptr [ebp+0Ch] 0040EB4E mov dword ptr [ebp+8],eax 9: } 0040EB51 pop edi 0040EB52 pop esi 0040EB53 pop ebx 0040EB54 mov esp,ebp 0040EB56 pop ebp 0040EB57 ret
|
有参数,无返回值的函数反汇编分如下几部分
- 保存栈底
- 提升堆栈
1 2
| 0040EB31 mov ebp,esp 0040EB33 sub esp,40h
|
- 保护现场
1 2 3
| 0040EB36 push ebx 0040EB37 push esi 0040EB38 push edi
|
- 填充缓冲区
1 2 3 4
| 0040EB39 lea edi,[ebp-40h] 0040EB3C mov ecx,10h 0040EB41 mov eax,0CCCCCCCCh 0040EB46 rep stos dword ptr [edi]
|
- 函数功能
1 2 3
| 0040EB48 mov eax,dword ptr [ebp+8] 0040EB4B add eax,dword ptr [ebp+0Ch] 0040EB4E mov dword ptr [ebp+8],eax
|
- 恢复现场
1 2 3
| 0040EB51 pop edi 0040EB52 pop esi 0040EB53 pop ebx
|
- 降低堆栈
1 2
| 0040EB54 mov esp,ebp 0040EB56 pop ebp
|
- 函数返回
从 Function 函数的反汇编中可以看出:调用参数是使用 push 指令将参数压入堆栈中。还有参数存储在 [ebp+xxx] 中
函数的反汇编分析-简单功能(有返回值)
源码
1 2 3 4 5 6 7 8 9 10 11 12
| #include "stdafx.h"
int Function(int x,int y) { return x+y; }
int main(int argc, char* argv[]) { Function(1,2); return 0; }
|
反汇编分析
参数调用
1 2 3 4 5
| 13: Function(1,2); 004010D8 push 2 004010DA push 1 004010DC call @ILT+40(Function) (0040102d) 004010E1 add esp,8
|
函数的反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 6: int Function(int x,int y) 7: { 0040EB30 push ebp 0040EB31 mov ebp,esp 0040EB33 sub esp,40h 0040EB36 push ebx 0040EB37 push esi 0040EB38 push edi 0040EB39 lea edi,[ebp-40h] 0040EB3C mov ecx,10h 0040EB41 mov eax,0CCCCCCCCh 0040EB46 rep stos dword ptr [edi] 8: return x+y; 0040EB48 mov eax,dword ptr [ebp+8] 0040EB4B add eax,dword ptr [ebp+0Ch] 9: } 0040EB4E pop edi 0040EB4F pop esi 0040EB50 pop ebx 0040EB51 mov esp,ebp 0040EB53 pop ebp 0040EB54 ret
|
有参数的函数,有返回值的函数反汇编分如下几部分
- 保存栈底
- 提升堆栈
1 2
| 0040EB31 mov ebp,esp 0040EB33 sub esp,40h
|
- 保护现场
1 2 3
| 0040EB36 push ebx 0040EB37 push esi 0040EB38 push edi
|
- 填充缓冲区
1 2 3 4
| 0040EB39 lea edi,[ebp-40h] 0040EB3C mov ecx,10h 0040EB41 mov eax,0CCCCCCCCh 0040EB46 rep stos dword ptr [edi]
|
- 输出返回值
1 2
| 0040EB48 mov eax,dword ptr [ebp+8] 0040EB4B add eax,dword ptr [ebp+0Ch]
|
- 恢复现场
1 2 3
| 0040EB4E pop edi 0040EB4F pop esi 0040EB50 pop ebx
|
- 降低堆栈
1 2
| 0040EB51 mov esp,ebp 0040EB53 pop ebp
|
- 函数返回
从上面的反汇编中红可以看到,返回值是存储到 eax 中的。
函数的反汇编分析-嵌套调用
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include "stdafx.h"
int Plus1(int x,int y) { return x+y; }
int Plus2(int x,int y,int z) { int t; int r; t = Plus1(x,y); r = Plus1(t,z);
return r; } int main(int argc, char* argv[]) { Plus2(1,2,3); return 0; }
|
反汇编
参数调用
1 2 3 4 5
| 00401128 push 3 0040112A push 2 0040112C push 1 0040112E call @ILT+50(Plus2) (00401037) 00401133 add esp,0Ch
|
函数反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 11: int Plus2(int x,int y,int z) 12: { 00401150 push ebp 00401151 mov ebp,esp 00401153 sub esp,48h 00401156 push ebx 00401157 push esi 00401158 push edi 00401159 lea edi,[ebp-48h] 0040115C mov ecx,12h 00401161 mov eax,0CCCCCCCCh 00401166 rep stos dword ptr [edi] 13: int t; 14: int r; 15: t = Plus1(x,y); 00401168 mov eax,dword ptr [ebp+0Ch] 0040116B push eax 0040116C mov ecx,dword ptr [ebp+8] 0040116F push ecx 00401170 call @ILT+45(Function) (00401032) 00401175 add esp,8 00401178 mov dword ptr [ebp-4],eax 16: r = Plus1(t,z); 0040117B mov edx,dword ptr [ebp+10h] 0040117E push edx 0040117F mov eax,dword ptr [ebp-4] 00401182 push eax 00401183 call @ILT+45(Function) (00401032) 00401188 add esp,8 0040118B mov dword ptr [ebp-8],eax 17: 18: return r; 0040118E mov eax,dword ptr [ebp-8] 19: } 00401191 pop edi 00401192 pop esi 00401193 pop ebx 00401194 add esp,48h 00401197 cmp ebp,esp 00401199 call __chkesp (00401330) 0040119E mov esp,ebp 004011A0 pop ebp 004011A1 ret
|
函数嵌套调用反汇编分如下几部分:
- 保存栈底
- 提升堆栈
1 2
| 00401151 mov ebp,esp 00401153 sub esp,48h
|
- 保护现场
1 2 3
| 00401156 push ebx 00401157 push esi 00401158 push edi
|
- 填充缓冲区
1 2 3 4
| 00401159 lea edi,[ebp-48h] 0040115C mov ecx,12h 00401161 mov eax,0CCCCCCCCh 00401166 rep stos dword ptr [edi]
|
- t=Plus1(x,y)的函数调用
1 2 3 4 5 6 7
| 00401168 mov eax,dword ptr [ebp+0Ch] 0040116B push eax 0040116C mov ecx,dword ptr [ebp+8] 0040116F push ecx 00401170 call @ILT+45(Function) (00401032) 00401175 add esp,8 00401178 mov dword ptr [ebp-4],eax
|
- r = Plus1(t,z)
1 2 3 4 5 6 7
| 0040117B mov edx,dword ptr [ebp+10h] 0040117E push edx 0040117F mov eax,dword ptr [ebp-4] 00401182 push eax 00401183 call @ILT+45(Function) (00401032) 00401188 add esp,8 0040118B mov dword ptr [ebp-8],eax
|
- 传递返回值
1
| 0040118E mov eax,dword ptr [ebp-8]
|
- 恢复现场
1 2 3
| 00401191 pop edi 00401192 pop esi 00401193 pop ebx
|
- 降低栈顶
- 验证堆栈平衡
1 2 3
| 00401197 cmp ebp,esp 00401199 call __chkesp (00401330) 0040119E mov esp,ebp
|
- 降低栈底
- 函数返回
从嵌套函数的反汇编中发现,程序中多了一步验证堆栈平衡的步骤。当然这只有在 VC6 的 debug 模式下才会有的代码。