C|函数调用的栈帧机制与数组越界、缓冲区溢出( 三 )


0040D820 push ebp // ebp赋值前先压栈保存先前状态 esp = 0x0012feec, *esp = ebp0012FEEC 48 FF 12 00 DF D4 40 00 05 00 00 00 H...咴@.....
0040D821 mov ebp,esp // ebp = esp = 0x0012feec栈区:
3 函数栈帧空间分配0040D823 sub esp,1D0h // 1D0h = 464 = 400+64 , esp = 0x0012fd1c此时栈顶指针附近的内存随机值:
0012FD10 FE FF FF FF FE 60 75 77 76 A3 71 77 ....uwvw
0012FD1C 00 00 1F 00 63 01 00 50 D3 5D 6E 77 ....c..P覿nw
0012FD28 CF 79 14 75 00 00 00 00 00 00 00 00 蟳.u........
3.1 寄存器压栈
寄存器状态保持(压栈)
0040D829 push ebx0040D82A push esi0040D82B push edi // esp = 0012FD10, *esp = edi栈内存:
0012FD10 48 FF 12 00 00 00 00 00 00 F0 FD 7F H........瘕.
此时esp = 0x0012fd10 , 三个寄存器使用的栈内存是464个字节以外的栈内存 。
栈区:
3.2 栈帧分配的空间每个字节全部置为0xCC
0040D82C lea edi,[ebp-1D0h]0040D832 mov ecx,74h0040D837 mov eax,0CCCCCCCCh0040D83C rep stos dword ptr [edi]此时esp和ebp之间的栈空间:
0012FD10 48 FF 12 00 00 00 00 00 00 F0 FD 7F H........瘕.
0012FD1C CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫
0012FEE0 CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫
0012FEEC 48 FF 12 00 DF D4 40 00 05 00 00 00 H...咴@.....
3.3 栈空间为局部变量从ebp处开始偏移进行初始化操作
5: int b = -1; // [ebp-4]0040D83E mov dword ptr [ebp-4],0FFFFFFFFh此时的栈空间:
0012FEE0 CC CC CC CC CC CC CC CC FF FF FF FF 烫烫烫烫....
0012FEEC 48 FF 12 00 DF D4 40 00 05 00 00 00 H...咴@.....
汇编代码继续:
6: int arr[98] = {0}; // [ebp-18Ch]0040D845 mov dword ptr [ebp-18Ch],00040D84F mov ecx,61h0040D854 xor eax,eax0040D856 lea edi,[ebp-188h]0040D85C rep stos dword ptr [edi]7: int c = 1; // [ebp-190h] , 栈是逆增长 , 栈顶地址值>栈顶0040D85E mov dword ptr [ebp-190h],1此时的栈空间:
0012FD50 CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫
0012FD5C 01 00 00 00 00 00 00 00 00 00 00 00 ............
0012FD68 00 00 00 00 00 00 00 00 00 00 00 00 ............
8: arr[-1] = c*a; // 数组向栈顶方向越界访问
0040D868 mov eax,dword ptr [ebp-190h]0040D86E imul eax,dword ptr [ebp+8]0040D872 lea ecx,[ebp-18Ch]0040D878 mov dword ptr [ecx-4],eax此时的栈空间:
0012FD50 CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫
0012FD5C 05 00 00 00 00 00 00 00 00 00 00 00 ............
0012FD68 00 00 00 00 00 00 00 00 00 00 00 00 ............
9: arr[98] = b*a; // 数组向栈底方向越界访问
0040D87B mov edx,dword ptr [ebp-4]0040D87E imul edx,dword ptr [ebp+8]0040D882 mov dword ptr [ebp-4],edx此时的栈空间:
0012FEDC 00 00 00 00 00 00 00 00 00 00 00 00 ............
0012FEE8 FB FF FF FF 48 FF 12 00 DF D4 40 00 ....H...咴@.
0012FEF4 05 00 00 00 00 00 00 00 00 00 00 00 ............
栈区:
3.4 值返回
10: //printf("%d %d\n",b,c); // -5 511: return b;0040D885 mov eax,dword ptr [ebp-4] // 返回值保存在寄存器eax中3.5 寄存器状态恢复
此时 esp = 0x0012fd10
0040D888 pop edi //esp += 4, edi = *esp0040D889 pop esi0040D88A pop ebx以上的栈空间是栈帧空间464以外的空间 。
此时 esp = 0x0012fd1c
ebp = 0x0012feec
0040D88B mov esp,ebp// 栈顶指针更新 , 栈空间回收此时ebp对应的栈空间
0012FEEC 48 FF 12 00 DF D4 40 00 05 00 00 00 H...咴@.....
0040D88D pop ebp此时 esp = 0x0012fef0
ebp = 0x0012ff48
0040D88E ret函数返回:
0040D4DF add esp,4 // 4是实参压栈时使用的字节数 。如果压了3个int , 则是ch0040D4E2 mov dword ptr [ebp-4],eax0012FF3C CC CC CC CC CC CC CC CC FB FF FF FF 烫烫烫烫....
0012FF48 88 FF 12 00 F9 11 40 00 01 00 00 00 ......@.....
ebp-4是主调函数局部变量e的栈内存保存位置 。
如果函数的返回值是一个复合类型 , 超过了两个寄存器所能容纳的大小 , 会在主调函数的栈帧开辟空间 , 以供值返回 。
4 数组越界访问向栈底方向越界1个int空间:对应局部变量int b;
向栈底方向越界2个int空间:对应ebp本身;
向栈底方向越界3个int空间:对应函数返回地址[ebp+4];
向栈底方向越界4个int空间:对应函数实参存储位置[ebp=8]:
#include <stdio.h>void arrayBound(int a){ int b = -1; int arr[98] = {0}; int c = 1; arr[98] = a; arr[101] = 555; b=a; printf("%d\n",b); // 555}int main(){ int c = 5; arrayBound(c); printf("%d\n",c); // 5 while(1); return 0;}向栈底方向越界3个int空间:对应函数返回地址:
arr[100] = 0040D4DF; // 数组向栈底方向越界访问
当arr[100] 被赋的值是一个合法的内存地址时 , 正常运行 , 否则 , 运行出错 。

推荐阅读