zoukankan      html  css  js  c++  java
  • 栈溢出原理笔记(一)

    系统栈的工作原理

    1.内存的不同用途
            简单来说,缓冲区溢出就是在大缓冲区的数据复制到小缓冲区中,由于没注意小缓冲区的边界,”撑爆“了小缓冲区。从而冲掉了小缓冲区相邻内存区域的数据。
    根据不同的操作系统,一个进程可能被分配到不同内存区域中去执行,但是不管什么样的系统,什么计算机架构,进程使用的内存都可以按照功能分为4部分:
            代码区:可执行指令
            数据区:用于存储全局变量
            堆区:  进程可以在堆区动态的请求一定大小的内存,并在用完之后归还给堆区。动态分配和回收是堆区的特点
            栈区:  用于动态的存储函数之间的调用关系,以保证被调用函数返回时恢复到母函数中继续执行
            这只是简单的内存划分,如果想了解关于内存更详细的论述,请参考《深入理解计算机系统》,windows下,PE文件代码段中包含的二进制机器代码会被装入内存的代码区,处理器将到这里一条一条的取出指令和操作数,送入算术逻辑单元运算,如果代码请求开辟动态内存,则会在内存的堆区分配一块区域返回给代码区的代码使用,当函数调用发生时,函数的调用关系等信息会动态的保存在栈区。
    程序中所使用的缓冲区可以是在堆区、栈区、数据区,不同地方的缓冲区利用方法不同。
    2.栈与系统栈
            栈是一种数据结构,是一种先进后出的数据表,用于标识栈的属性两个:栈顶、栈底 。内存中的栈区指的就是系统栈,由系统自动维护。
    3.函数调用时发生了什么
            请看如下C代码:
     

           int B(int b1,int b2){
                    int var_b1=b1+b2;
                    int var_b2=b1-b2;
                    return var_b1*var_b2;
            }
            int A(int a1,int a2){
                    int var_a1;
                    var_a=B(a1,a2)+a1;
                    return a1;               
            }
            int main(int argc,char **argv,char **envp){
                    int var_main;
                    var_main=A(4,3);
                    return var_main;
            }

            根据操作系统的不同、编译器和编译选项的不同,同一文件不同函数的代码在内存代码区中的分布可能相邻,也可能不相邻,可能有先后顺序,也可能没有,但他们都是在代码所映射的节里
    函数调用时,伴随的系统栈中的操作如下:
    在main函数调用A的时候,首先在自己的栈帧中压入函数返回地址,然后位A创建新栈帧并压入系统栈 ,
    在函数A调用B的时候,同样先在自己的栈帧中压入返回地址,然后为B创建新栈帧并压入系统栈
    B返回时,B的栈帧被弹出系统栈,A栈帧的返回地址被露在栈顶,处理器跳到返回地址处执行
    在A返回时,A的栈帧被弹出系统栈,main函数栈帧中的返回地址被露在栈顶。处理器跳到返回地址执行
    4.寄存器与函数栈帧
            每一个函数独占自己的栈帧空间,当前正在运行的函数总是在栈顶,win32系统提供两个寄存器用于标识位于系统栈顶端的栈帧
             ESP:栈指针寄存器,存放一个指针,该指针永远指向系统栈最上面的栈帧的栈顶
             EBP:基址指针寄存器,该指针永远指向系统栈最上面的栈帧的底部
             函数栈帧:ESP和EBP之间内存空间为当前栈帧
             在函数栈帧中一般包含以下几种信息:、
             局部变量:为函数举报变量开辟的内存空间
             栈帧状态值:保存前栈帧的顶部和底部(实际上只保存前栈帧的底部,前栈帧的顶部可以通过堆栈平衡得到)
             函数返回地址:保存当前函数调用前的“断点”信息,也就是函数调用前的指令位置
             函数栈帧的大小不固定,一般和局部变量的多少有关
    5.函数调用约定与相关指令
            调用约定描述了函数传递参数的方式和栈协同工作的技术细节,下面列出几种调用方式:
                                    C                Syscall                Stdcall                BASIC                FORTRAN                PASCAL
    参数入栈顺序 右→左         右→左                右→左                左→右                 左→右                左→右
    谁恢复栈平衡 母函数                子函数                子函数                子函数                子函数                子函数
            对于Visual C++,支持3种函数调用约定:

            调用约定声明                参数入栈顺序                谁恢复栈平衡
            __cdecl                                右→左                                母函数
            __fastcall                        右→左                                子函数
            __stdcall                        右→左                                子函数

            除了入栈方向和恢复平衡不同之外,参数传递有时也会有所不同。例如,每一个C++类成员函数都有一个this指针,在windows下,这个指针保存在ECX中
    ,但如果用GCC编译,这个指针会作为最后一个参数入栈

            函数调用大致包括以下几个步骤:
             参数入栈
             返回地址入栈
             代码区跳转
             栈帧调整:具体包括
             保存当前栈帧状态值(push ebp)
             将当前栈帧切换到新栈帧(mov ebp,esp)
             给新栈帧分配空间(把ESP减去所需空间大小,抬高栈顶)
             
            函数返回大致包括以下几个步骤:
             保存返回值(通常保存在EAX中)
             弹出当前栈帧,恢复上一个栈帧:具体包括
             在堆栈平衡的基础上给ESP加上栈帧的大小,降低栈顶,回收当前栈帧空间
             将当前栈帧底部保存的前栈帧EBP值弹入EBP,恢复出上一个栈帧
             将函数返回地址弹给EIP

        跳转

    也曾想仗剑走天涯,后来因为Bug多就没去……
  • 相关阅读:
    linux环境下安装nginx步骤
    时间戳—时间互转 java
    redis配置中踩过的坑
    在Windows端安装kafka 提示错误: 找不到或无法加载主类 的解决方案
    Windows平台kafka环境的搭建
    在windows上搭建redis集群(redis-cluster)
    身份证号打码隐藏
    PIL获取图片亮度值的五种方式
    Python文件排序
    PIL
  • 原文地址:https://www.cnblogs.com/daiorz/p/11673557.html
Copyright © 2011-2022 走看看