(六)羽夏看C语言——函数
从汇编层面聊一聊函数到底是什么,怎么实现的
写在前面
由于此系列是本人一个字一个字码出来的,包括示例和实验截图。本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读(一)羽夏看C语言——简述 ,方便学习本教程。
前篇答疑解惑
答案:不会。
解答:我相信亲手做实验的人都会知道的。
函数
一个程序必须有一个函数,比如C语言中的main函数
,也可以实现别的函数实现功能。本篇文章将从汇编的角度介绍函数,本次实验的代码十分简单。为了防止干扰,请在项目属性中关闭增量链接
和支持仅我的代码调试
(具体作用请搜索科普,它们会生成一些汇编代码来实现相应的功能)。
int main()
{
int a = 5;
return 0;
}
得到的反汇编如下:
通过汇编可以看出,函数生成的汇编采用ebp寻址
的方式。前三句汇编进行用ebp寻址的准备
和提栈准备缓冲区
的操作,提供的缓冲区用来给函数内的局部变量用的。
有些生成的反汇编可能是这样的版本:
push ebp
mov ebp,esp
sub esp,0x40
push ebx
push esi
push edi
lea edi,[ebp-0x40]
mov ecx,0x10
mov eax,0xcccccccc
rep stosd
mov dword ptr [ebp-8],5
xor eax,eax
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
有些人可能看不懂以下代码:
lea edi,[ebp-0x40]
mov ecx,0x10
mov eax,0xcccccccc
rep stosd
上面的代码是将0xCC
填充满整个缓冲区,可帮助检测到堆栈缓冲区溢出,这是一种常见的攻击,威胁程序的安全性。0xCC
即为我们调试的普通断点,汇编为int 3
,即VS
按下F9
下断点(代码行左边的小红点)。
函数调用
int test(int a, int b)
{
return a + b;
}
int main()
{
int a = test(1, 2);
return 0;
}
然后看一下反汇编:
可以看出函数调用时会通过push
传递参数,然后call
一个地址跳转到test函数
,通过eax
作为返回值
。调用完毕后用add esp,8
平栈,这个所谓的一种调用约定
,被称为__cdecl
。当然调用约定不知这一种,你也可以自定义,不过得从汇编上实现。
调用约定
当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和返回值,由谁平衡堆栈,这就是所谓的调用约定。
我们先给出常见的调用约定,其他的调用约定自行科普:
调用约定 | __stdcall | __cdecl | __fastcall | __pascal |
---|---|---|---|---|
参数传递顺序 | 从右到左 | 从左到右 | 从右到左 | 使用寄存器和栈 |
平栈 | 调用者 | 子程序 | 子程序 | 子程序 |
当然,在IDA
中你可能会看到其他调用约定,比如__thiscall
等。由于调用约定并不是我想重点讲述的,具体细节自行科普。C语言默认的调用约定为__cdecl
,所以在 函数调用 部分已有示例。这部分就用__fastcall
做个为调用约定
示例。
int __fastcall test(int a, int b)
{
return a + b;
}
int main()
{
int a = test(1, 2);
return 0;
}
如下是反汇编:
下一篇
(七)羽夏看C语言——模板(C++)