C 函数实现原理
0x00简介
函数存储在栈上,栈从头到尾支持函数调用和传参,每个函数在栈上都有一席之地用来记录他的必要信息总结就是以下主要信息
1.调用约定传参
a 参数的传递方向
b 参数的储存媒介
c 谁负责释放参数空间
d 返回值的处理
调用者(caller)
被调者(callee)
_cdecl: 从右往左传递参数,参数通过栈传递,调用放负责清理参数空间(支持不定参)(c约定)
_stdacll:从右往左传递参数,参数通过栈传递,被调用放负责清理参数空间(不支持不定参)
_fastacall:从右往左传递参数,左数前两个参数通过处理器存放,其余参数通过栈保存,被调用放负责清理参数空间(不支持不定参)
(微软某一个系列编译器约定)
2.在栈顶保存放回地址
3.保存调用方的栈信息
4.更新栈位置被调用方的栈底处
5.在栈内开辟局部变量的空间
5a编译选项有/ZI+/0d 则同从局部变量空间为0xcc
6 保存处理器环境
7.执行函数体
8.恢复处理器环境
9.释放局部变量空间
10.恢复栈信息到调用方
11a __cdecl:取出返回地址,并按此流程更新,抵达新地址后,由调用方清理参数
11b _stdacll,_fastacall 取出返回地址,并清理参数,让后按返回地址作流程更新
0x01原理
1.调用约定
a.参数传递方向
调用方去调用某个函数, 函数传递参数 这些都需要其他约定 不然就有分歧 传递传输是从左往右还是从右往左
这些都是必要约定条件
b.参数的传递方向
提前约定不然就是 传递到栈 传递到寄存器,说白了就是参数的存储媒介问题
c.谁负责释放参数空间
当函数调用时 结束到底是谁来释放空间 是调用方还是被调用方
d.返回值的处理
约定返回值存放位置
默认调用是
c约定 国标约定_cdecl
微软的系统上的约定_stdacll
微软独家某一个系统约定的_fastacall
跨平台首选_cdecl
winapi 都是_stdacll
_cdecl: 从右往左传递参数,参数通过栈传递,调用放负责清理参数空间(支持不定参)(c约定)
_stdacll:从右往左传递参数,参数通过栈传递,被调用放负责清理参数空间(不支持不定参)
_fastacall:从右往左传递参数,左数前两个参数通过处理器存放,其余参数通过栈保存,被调用放负责清理参数空间(不支持不定参)
(微软某一个系列编译器约定)
2.在栈顶保存放回地址
在栈区中可以存放多个栈空间
top是做减法 每调用一个函数 top 减一 压入一个新的栈空间
这是x86体系的方式 释放就是做加法
通过栈低定位函数栈位置
所以保留的栈信息就是栈低的
3.保存调用方的栈信息
4.更新栈位置被调用方的栈底处
5.在栈内开辟局部变量的空间
5a编译选项有/ZI+/0d 则填充局部变量空间为0xcc
6 保存寄存器环境
3个
7.执行函数体
8.恢复处理器环境
9.释放局部变量空间
10.恢复栈信息到调用方
这里来看真实函数执行上面过程
int test2(int x, int y) { int a = x + y; return a; } int test(int x, int y) { int a=test2(x,y); return a; } int main() { int temp = 10; int temp1 = 5; test(10, 5); return 0; }
那个01 00 00 00 标错了不是寄存器 应该是main的形参值
可以看到这个时候变量未初始化
在main函数局部变量空间初始化了2个变量
走进函数 压栈 3个寄存器存储信息 temp1和temp参数传入
在继续走
和上面一样的