c
只要会
数组 和
指针 就没什么太大问题
一、函数&变量
1. 参数传递与返回值
1.1 c代码
我们来分析下面这一份c代码:
int plus(int x, int y){
return x + y;
}
int main(void){
plus(1, 2); // 调用函数
return 0;
}
2.2 汇编代码
调用函数部分:
plus(1, 2);
(1) push 2 ;保存线程
push 1
(2) call plus()函数地址 ; 跳转
(3) add esp, 8 ; 外平栈顶
函数部分:
int plus(int x, int y){
<1> push ebp ; 提升堆栈
mov ebp, esp
sub esp, 40H
<2> push ebx ; 保存线程
push esi
push edi
<3> lea edi, [ebp - 40H] ; 用CC填充缓冲区
mov ecx, 10H
mov eax, 0CC CC CC CCH
rep stos dword ptr [edi]
return x + y;
<4> mov eax, dword ptr [ebp + 8] ; 代码主体
add eax, dword ptr [ebp + 0CH]
}
<5> pop edi ; 释放线程
pop esi
pop ebx
<6> mov esp, ebp ;还原堆栈
<7> pop ebp ; 释放ebp
<8> ret ; 返回调用处
2.3 堆栈情况
调用函数:
-
步骤1:
(1)
,把参数压入栈(从右往左压) -
步骤2:
(2)
,把调用处下一行代码的地址压入栈。跳转到plus()
处 -
步骤3:
<1>
,提升堆栈,保存esp
的值在ebp
里 -
步骤4:
<2>
,保存线程 -
步骤5:
<3>
,用CC
填充缓冲区
补充:
- 提升堆栈和原始堆栈之间的地方叫做缓冲区
- 为了防止缓冲区溢出 错误,需要让指令不能读到缓冲区,而
CC
的作用就是断点。CC
是中文烫
的ASCII码
-
步骤6:
<4>
,把x
给eax
,再把y
加进去 -
步骤7:
<5>
,释放线程 -
步骤6:
<6>
,还原堆栈 -
步骤7:
<7>
,还原ebp
的值 -
步骤8:
<8>
,返回调用处 -
步骤9:
(3)
,执行此步前,堆栈没有还原,所以手动还原(叫外平栈 )
2. 变量
2.1 变量的本质
- 声明变量:
int a
`` - 给变量赋值:
a = 7
mov dword ptr [a], 1
全局变量:
- 编译时就确认内存地址和宽度,变量名就是内存地址别名(数据标号)
- 如果不重写编译,全局变量的内存地址不变。(找基址 的意思就是找全局变量 )
局部变量:
- 局部变量是函数内部申请的,只有当函数执行时,它才有内存空间
- 局部变量的内存是在堆栈中分配的,我们无法确定局部变量的内存地址
2.2 变量与参数的内存分配
继续回顾一下这张图:
可以发现:
[EBP + 8]
就是第一个参数,再+4
就是第二个参数。。。[EBP + 4]
是返回地址EAX
用来存储计算过程,也用来存储返回值(约定俗成的)
局部变量在缓冲区中: int z = x + y
mov eax, dword ptr [ebp + 8] ; x
add eax, dword ptr [ebp + 0CH] ; x + y
mov dword ptr [ebp - 4], eax ; 往缓冲区写入局部变量
返回值通过eax
传递: return z
mov eax, dword ptr [ebp - 4]
int a = plus(1, 2);
mov dword ptr [ebp - 4], eax ; 【注意】这个ebp是调用者main()的ebp,不是plus()的ebp
总结一下:
2.3 嵌套调用
还记得plus()
调用的堆栈下面是啥嘛?是main()
的堆栈!
一个函数调用另一个函数,新的函数会压 在调用函数的上面
- 问题: 为什么全局变量 可以不赋值,定义完直接使用(有默认值,比如
int
类型,默认值为0),而局部变量 必须要赋值才能使用? - 回答: 一个函数执行完毕后,会在堆栈留下一堆垃圾数据。这时,如果调用了一个新的函数,分配给局部变量的那部分内存,里面是别的函数留下的垃圾,如果不赋值使用,必然出错。
补充:
win32 Debug
版编译器有一个命令call __chkesp
来检查函数执行前后,堆栈是否平衡。不平衡就报错
2.4 printf
printf("%d", r);
mov eax, dword ptr [ebp - 4] ; 把r的值给eax
push eax ; 形参入栈
push offset String "%d" ; 取"%d"的偏移地址
call printf
add esp, 8 ; 外平栈