文档详情

函数调用--函数栈.doc

发布:2017-07-25约3.06千字共7页下载文档
文本预览下载声明
函数调用大家都不陌生,调用者向被调用者传递一些参数,然后执行被调用者的代码,最后被调用者向调用者返回结果,还有大家比较熟悉的一句话,就是函数调用是在栈上发生的,那么在计算机内部到底是如何实现的呢? ? 对于程序,编译器会对其分配一段内存,在逻辑上可以分为代码段,数据段,堆,栈 代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写 数据段:保存初始化的全局变量和静态变量,可读可写不可执行 BSS:未初始化的全局变量和静态变量 堆(Heap):动态分配内存,向地址增大的方向增长,可读可写可执行 栈(Stack):存放局部变量,函数参数,当前状态,函数调用信息等,向地址减小的方向增长,非常非常重要,可读可写可执行 如图所示 寄存器 EAX:累加(Accumulator)寄存器,常用于函数返回值 EBX:基址(Base)寄存器,以它为基址访问内存 ECX:计数器(Counter)寄存器,常用作字符串和循环操作中的计数器 EDX:数据(Data)寄存器,常用于乘除法和I/O指针 ESI:源变址寄存器 DSI:目的变址寄存器 ESP:堆栈(Stack)指针寄存器,指向堆栈顶部 EBP:基址指针寄存器,指向当前堆栈底部 EIP:指令寄存器,指向下一条指令的地址 源代码 int print_out(int begin, int end) { printf(%d , begin++); int *p; p = (int*)(int(begin) - 4); if(begin = end) *p -= 5; return 1; } int add(int a, int b) { return a+b; } int pass(int a, int b, int c) { char buffer[4] = {0}; int sum = 0; int *ret; ret = (int*)(buffer+28); //(*ret) += 0xA; sum = a + b + c; return sum; } int main() { print_out(0, 2); printf(\n); int a = 1; int b = 2; int c; c = add(a, b); pass(a, b, c); int __sum; __asm { mov __sum, eax } printf(%d\n, __sum); system(pause); } ? 函数初始化 28: int main() 29: { 011C1540 push ebp //压栈,保存ebp,注意push操作隐含esp-4 011C1541 mov ebp,esp //把esp的值传递给ebp,设置当前ebp 011C1543 sub esp,0F0h //给函数开辟空间,范围是(ebp, ebp-0xF0) 011C1549 push ebx 011C154A push esi 011C154B push edi 011C154C lea edi,[ebp-0F0h] //把edi赋值为ebp-0xF0 011C1552 mov ecx,3Ch //函数空间的dword数目,0xF02 = 0x3C 011C1557 mov eax,0CCCCCCCCh 011C155C rep stos dword ptr es:[edi] //rep指令的目的是重复其上面的指令.ECX的值是重复的次数. //STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址,然后EDI+4 ? 一般所用函数的开头都会有这段命令,完成了状态寄存器的保存,堆栈寄存器的保存,函数内存空间的初始化 函数调用 30: print_out(0, 2); 013D155E push 2 //第二个实参压栈 013D1560 push 0 //第一个实参压栈 013D1562 call print_out (13D10FAh)//返回地址压栈,本例中是013D1567,然后调用print_out函数 013D1567 add esp,8 //两个实参出栈 //注意在call命令中,隐含操作是把下一条指令的地址压栈,也就是所谓的返回地址 ? ? 除了VS可能增加一些安全性检查外,print_out的初始化与main函数的初始化完全相同 ? 被调用函数返回 013D141C mov eax,1 //返回值传入eax中 013D1421 pop edi 013D1422 pop esi 013D1423 pop ebx //寄存器出栈 013D1424 add esp,0D0h //以下3条命令是调
显示全部
相似文档