function5-栈
懒懒散散
一天就学一会就是玩游戏刷手机~
前言
栈这个东西,百度的也是大概,具体的内存分析还是有待深挖。
在计算机系统中,栈则是一个具有以上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。在i386机器中,栈顶由称为esp的寄存器进行定位。压栈的操作使得栈顶的地址减小,弹出的操作使得栈顶的地址增大。
栈在程序的运行中有着举足轻重的作用。最重要的是栈保存了一个函数调用时所需要的维护信息,这常常称之为堆栈帧或者活动记录。堆栈帧一般包含如下几方面的信息:
- 函数的返回地址和参数
- 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
正文
说人话栈就是为了应对临时变量所产生的一块独立的内存空间,有调用变量和函数时就由计算机自主分配调用,用完就释放。
- 如果所有变量的内存地址都要固定,程序庞大的时候内存消耗过高
- 如果变量都是自主分配,麻烦不说,还要自己清理
- 如果一部分变量系统分配,一部分自主分配,系统分配的系统会自动清理
那么结果肯定是3好。
1 |
|
寄存器在x86反汇编的时候已经看到过ebp,esp,eax,edx之类的了,那么细分的话:
- 32位通用寄存器有:4个数据寄存器
(EAX,EBX,ECX,EDX)
,2个变址寄存器(ESI,EDI)
和2个指针寄存器(ESP,EBP)
- 6个段寄存器
(ES,CS,SS,DS,FS,GS)
- 1个指令指针寄存器
(EIP)
- 1个标志寄存器
(EFLAGS)
打断点到int x = Add(250, 150);
上反汇编查看:
画图是比较直观的操作,所以继续画画看:
十六进制转十进制不会的就用计算器00A41A25 push 96h
00A41A2A push 0FAh
参数从右往左进栈,每push一次esp的地址就等于 esp -= 4
,这不是一次重复说明了,常量也会占用内存,之所以减法是因为内存模型是从高地址到低地址,比如0xfffd5 ~ 0xfffa1,所以esp通过减法使得上升,至于-4可能是跟32位的地址大小相关吧。00A41A2F call 00A411C7
跳转到Add函数位置处,其实也不难看出一点,每一条指令最左边的地址都相差4,例如A25 和 A2A ,A就是表示10,中间隔了[26 27 28 29]
往上找一下add的位置,这里call的地方看不太懂变成了什么。
找到后关闭源代码,发现挺长的。
不过慢慢看,发现这个函数的第一步也还是push ebp,将ebp压入栈中,但是这个时候ebp的值就不确定了。然后mov ebp,esp让esp上来,sub esp,0D8h
esp-216我就不知道为啥偏移这么多了。
即使配合源码从后面的00731849到00731860我倒是没看明白干了什么。
00731865开始给ebp-8的位置传64h,也就是100,也就是int c = 100
;那句源代码。int d = hh(a,b)
; a和b都是我们传递的,所以两个mov都没啥问题,就是把eax和ecx在压入栈跳转到hh函数的位置,同样的这个地址跳转的也不是函数内容,更像是在实现的过程。明明已经禁用优化了,换成release的话也不对头,int x=Add那句好像因为没有调用,甚至都没有反汇编。
就不管call,看add esp,8
,推测是释放形参用的。然后mov eax
的值给ebp-14,ebp-14就是变量d的位置,eax先前说到一般存储可能为返回值。
然后c += d
,表达式就是c = c+d
, 现在已知这俩的位置,先将ebp-8也就是c的值传给eax寄存器,然后eax寄存器在加上ebp-14里的值,也就是d的值。因为结果是相加后重新赋给c,所以最后还有一步,将eax相加之和的结果在传给ebp-8;
return c就很直接了,把ebp-8的值传给eax。
后面的pop弹出栈还看不明白。反正ret最后回到main函数里就对了,因为函数里面pop ebp的时候,esp应该就会+4回到之前的地址了。
但是函数里面怎么画就不知道了,这个反汇编还是有点问题不太直观。可能还得借助其他工具分析。
通过esp和ebp来回移动可以保持栈平衡,虽然esp到达过的地方数据短时间不会被清除,但是按照固定逻辑,正常情况下esp也不会回头访问了,除非下次用到那块地址了又擦写数据让esp顶上。
这样栈溢出会好理解一点,就比如数组,他是一片连续的内存空间,如果输入的时候不控制,那么后面溢出的入栈之后造成的问题就会五花八门,最常见就是改变程序运行位置,本来连续空间正常执行,一个溢出,导致后面执行位置或者返回位置被修改了。程序未能如期运行可以说是。
这种可控的情况下,vs就搞了那套函数后面带_s的安全函数,只要提前限定长度就能减少溢出的问题,当然不可能完全解决的。
结语
- 栈的空间提前分配好了。局部变量通常就入栈,栈通过esp实现局部变量的创建和释放
- 栈平衡破坏了,函数在某一段可能不能返回到预期的位置,利用这个原理,可以来实现对目标操作系统的控制权限。
- sub esp,x 这个x有可能就能知道有多少个局部变量
- 目前常见寄存器:
- ebp 栈底
- esp 栈顶,栈顶以下的值或是局部变量之类的
- eax 函数返回值通常由eax传递
- eip 属于cpu执行的位置
- ecx,edx 也是存放临时变量的
不过说实在还是挺失败的~纯纯的理论者,真到了实操怎么去控制一个栈溢出都不知道。。。惭愧哦