C&汇编基础-函数
9unk Lv5

C 函数的格式

函数名、参数名和变量名的命名规则:只包含字母、数字和_(下划线)且不能以数字开头。

函数名、参数名和变量名不能使用 C 的关键词,如下所示:

1
auto break case char const continue default do double else enum extern float for 

无参数,无返回值的函数格式

1
2
3
4
5
void 函数名()
{
//代码(每行以 ; 结尾)
}

有参数,无返回值

1
2
3
4
void 函数名(参数类型 参数名,参数类型 参数名)
{
//代码(每行以 ; 结尾)
}

无参数,有返回值

1
2
3
4
返回值类型 函数名()
{
//代码(每行以 ; 结尾)
}

有参数,有返回值

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
//call 指令调用之后,会使用 jmp 指令跳转到 Function 函数入口
00401023 jmp Function (0040eb30)


// Function 函数反汇编
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. 保存栈底
1
0040EB30   push        ebp
  1. 提升堆栈
1
2
0040EB31   mov         ebp,esp
0040EB33 sub esp,40h
  1. 保护现场
1
2
3
0040EB36   push        ebx
0040EB37 push esi
0040EB38 push edi
  1. 填充缓冲区
1
2
3
4
0040EB39   lea         edi,[ebp-40h]
0040EB3C mov ecx,10h
0040EB41 mov eax,0CCCCCCCCh
0040EB46 rep stos dword ptr [edi]
  1. 恢复现场
1
2
3
0040EB48   pop         edi
0040EB49 pop esi
0040EB4A pop ebx
  1. 降低堆栈
1
2
0040EB4B   mov         esp,ebp
0040EB4D pop ebp
  1. 函数返回
1
0040EB4E   ret

函数的反汇编分析-简单功能(无返回值)

源码

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
//jmp 跳转到函数入口
00401028 jmp Function (0040eb30)

//Function 函数反汇编
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. 保存栈底
1
0040EB30   push        ebp
  1. 提升堆栈
1
2
0040EB31   mov         ebp,esp
0040EB33 sub esp,40h
  1. 保护现场
1
2
3
0040EB36   push        ebx
0040EB37 push esi
0040EB38 push edi
  1. 填充缓冲区
1
2
3
4
0040EB39   lea         edi,[ebp-40h]
0040EB3C mov ecx,10h
0040EB41 mov eax,0CCCCCCCCh
0040EB46 rep stos dword ptr [edi]
  1. 函数功能
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. 恢复现场
1
2
3
0040EB51   pop         edi
0040EB52 pop esi
0040EB53 pop ebx
  1. 降低堆栈
1
2
0040EB54   mov         esp,ebp
0040EB56 pop ebp
  1. 函数返回
1
0040EB57   ret

从 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. 保存栈底
1
0040EB30   push        ebp
  1. 提升堆栈
1
2
0040EB31   mov         ebp,esp
0040EB33 sub esp,40h
  1. 保护现场
1
2
3
0040EB36   push        ebx
0040EB37 push esi
0040EB38 push edi
  1. 填充缓冲区
1
2
3
4
0040EB39   lea         edi,[ebp-40h]
0040EB3C mov ecx,10h
0040EB41 mov eax,0CCCCCCCCh
0040EB46 rep stos dword ptr [edi]
  1. 输出返回值
1
2
0040EB48   mov         eax,dword ptr [ebp+8]
0040EB4B add eax,dword ptr [ebp+0Ch]
  1. 恢复现场
1
2
3
0040EB4E   pop         edi
0040EB4F pop esi
0040EB50 pop ebx
  1. 降低堆栈
1
2
0040EB51   mov         esp,ebp
0040EB53 pop ebp
  1. 函数返回
1
0040EB54   ret

从上面的反汇编中红可以看到,返回值是存储到 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. 保存栈底
1
00401150   push        ebp
  1. 提升堆栈
1
2
00401151   mov         ebp,esp
00401153 sub esp,48h
  1. 保护现场
1
2
3
00401156   push        ebx
00401157 push esi
00401158 push edi
  1. 填充缓冲区
1
2
3
4
00401159   lea         edi,[ebp-48h]
0040115C mov ecx,12h
00401161 mov eax,0CCCCCCCCh
00401166 rep stos dword ptr [edi]
  1. 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 //给边阿玲 t 赋值
  1. 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 //给变量 r 赋值
  1. 传递返回值
1
0040118E   mov         eax,dword ptr [ebp-8]
  1. 恢复现场
1
2
3
00401191   pop         edi
00401192 pop esi
00401193 pop ebx
  1. 降低栈顶
1
00401194   add         esp,48h
  1. 验证堆栈平衡
1
2
3
00401197   cmp         ebp,esp
00401199 call __chkesp (00401330)
0040119E mov esp,ebp
  1. 降低栈底
1
004011A0   pop         ebp
  1. 函数返回
1
004011A1   ret

从嵌套函数的反汇编中发现,程序中多了一步验证堆栈平衡的步骤。当然这只有在 VC6 的 debug 模式下才会有的代码。

  • 本文标题:C&汇编基础-函数
  • 本文作者:9unk
  • 创建时间:2021-04-01 14:18:28
  • 本文链接:https://9unkk.github.io/2021/04/01/c-hui-bian-ji-chu-han-shu/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!