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


作用:为ebp当前存放的地址 , 在栈顶开辟空间存入它 , 用作调用子函数时的现场保护
3、pop——出栈指令
定义:与入栈指令相反 , 它先将栈顶的数据传送到通用寄存器、存储单元或段寄存器中 , 然后ESP增加作为当前栈顶 。
格式:pop src
举例:pop ebp
解释:相当于C语言中 ebp=*esp, esp+=4
作用:调用子函数结束后 , 恢复主函数的ebp
4、add——加法指令
格式:add dest, src
解释:相当于dest+=src
5、sub——减法指令
格式:sub dest, src
解释:相当于dest-=src
6、call——函数调用指令
格式:call 函数名
作用:(1)将程序当前执行的位置IP压入堆栈中;(2)转移到调用的子程序 。
其它:
RET 指令从堆栈把返回地址弹回到指令指针寄存器 。ret 相当于 pop EIP 。
rep stos dword ptr [edi] //rep是重复其上面的指令,ECX是重复的次数 。
ILT是INCREMENTAL LINK TABLE的缩写 , 这个@ILT其实就是一个静态函数跳转的表 , 它记录了一些函数的入口然后跳过去 , 每个跳转jmp占一个字节 , 然后就是一个四字节的内存地址 , 加起为五个字节 。
LEA(Load Effective Address )取有效地址指令 , 取源操作数地址的偏移量 , 并把它传送到目的操作数所在的单元
0.5 函数栈
0.5.1 栈空间增长方式:从高地址向低地址扩展 , 是一片让程序重复利用的数据空间;
栈空间由编译器维护(需要在Debug模式下才可以跟踪);
0.5.2 栈空间对齐方式:X86按4个字节来对齐 , X64按8个字节来对齐;
0.5.3 两个跟踪栈空间的寄存器ESP和EBP
对栈的操作由ESP跟踪 , 用来指示栈顶 。push、call时 , esp -= 4 , pop、ret时 , esp += 4
EBP用来引用函数参数和局部变量 。EBP相当于一个“基准指针” 。从主调函数传递到被调函数的参数以及被调函数本身的局部变量都可以通过这个基准指针为参考 , 加上偏移量找到 。
*ebp(表示栈地址ebp对应的值) = 上一个ebp的值(栈地址);
ebp-4 = 第一个局部变量地址;
ebp+4 = 函数返回地址;
ebp+8 = 函数第一个参数地址;
0.5.4 不同的函数调用约定 , 在参数的入栈顺序 , 堆栈的恢复(由caller还是callee负责)、函数的命名上会有所不同 。
0.5.5 一个完整的函数帧包括:函数参数、返回地址 , 上一个EBP的值 , 局部变量空间 , 3个寄存器 。在函数内部代码执行前 , 一个完整的函数帧已经建立 。
下面通过一个完整实例来理解函数调用的栈帧机制与数组向栈底方向越界的分析:
看以下代码:
#include <stdio.h>int arrayBound(int a){ int b = -1; // [ebp-4] int arr[98] = {0}; // [ebp-18Ch] int c = 1; // [ebp-190h] , 栈是逆增长 , 栈顶地址值>栈顶 arr[-1] = c*a; // 数组向栈顶方向越界访问 , arr[-1]对应int c arr[98] = b*a; // 数组向栈底方向越界访问 , arr[98] 对应int b //printf("%d %d\n",b,c); // -5 5 return b;}int main(){ int e = arrayBound(5); // -5 //printf("%d\n",e); return 0;}主函数main()的栈帧:
主函数main()调用arraybound() , 此时的汇编代码:
15: int e = arrayBound(5); // -50040D4D8 push 50040D4DA call @ILT+10(arrayBound) (0040100f)0040D4DF add esp,40040D4E2 mov dword ptr [ebp-4],eax16: //printf("%d\n",e);17: return 0;0040D4E5 xor eax,eax18: }1 主调函数调用被调函数时函数参数压栈此时的栈指针值:
ebp 0x0012ff48 // ebp是栈底指针
esp 0x0012fef8 // esp是栈底指针 , 栈的push和pop操作会同时改变esp的值(esp移动)
此时的栈顶指针附近的内存映像:
0012FEEC 30 2F 42 00 83 00 00 00 68 20 1F 00 0/B.....h ..
0012FEF8 00 00 00 00 00 00 00 00 00 F0 FD 7F .........瘕.
0012FF04 CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫
【C|函数调用的栈帧机制与数组越界、缓冲区溢出】执行汇编:
0040D4D8 push 5// esp += 4 = 0x0012fef4 , *esp = 5栈帧内存:
0012FEEC 30 2F 42 00 83 00 00 00 05 00 00 00 0/B.........
2 返回地址压栈汇编指令call对应两个操作:push 返回地址和jmp指令 。
0040D4DA call @ILT+10(arrayBound) (0040100f) 0012FEEC 30 2F 42 00 DF D4 40 00 05 00 00 00 0/B.咴@..... // 返回地址是0040D4DF
esp = 0x0012fef0 , *esp = 0040D4DF
0040100F jmp arrayBound (0040d820)代码跳转:
0040D820 push ebp0040D821 mov ebp,esp0040D823 sub esp,1D0h0040D829 push ebx0040D82A push esi0040D82B push edi0040D82C lea edi,[ebp-1D0h]0040D832 mov ecx,74h0040D837 mov eax,0CCCCCCCCh0040D83C rep stos dword ptr [edi]5: int b = -1; // [ebp-4]0040D83E mov dword ptr [ebp-4],0FFFFFFFFhebp值压栈:

推荐阅读