近来在读《0day安全软件漏洞分析技术》,此书句句让人有划线标重点的冲动,讲得实在太tm好了=。=
==============下面是我凌乱的摘抄===============
进程使用的内存按照功能大致分为4个部分:
1. 代码区:存储着被装入执行的二进制机器代码,处理器会到这个区域取指令执行
2. 数据区:存储全局变量等
3. 堆区:进程可以在堆区动态地请求一定大小的内存,并在用完之后归还给堆区
4. 栈区:动态地存储函数之间的调用关系,保证被调用函数在返回时恢复到母函数中继续执行
一图以蔽之:
可以有此类比:
CPU是只辛劳的小蜜蜂;
数据区、堆区、栈区等用来存放原料、半成品、成品等;
存在代码区的指令告诉CPU要做啥,怎么做,到哪里领原材料,用什么工具做,做完了把成品放到哪里去;
栈除了能放原料等,还能有备忘的功能,让CPU接着来时的方向继续工作
程序中所使用的缓冲区可以是堆区、栈区和存放静态变量的数据区。缓冲区溢出的利用方法和缓冲区到底属于上面哪个内存区域密不可分。
为了配合栈工作,系统为其配备了两个寄存器:
1. ESP:存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶
2. EBP:存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈底
一个很重要的寄存器:
EIP:存放着一个指针,该指针永远指向下一条等待执行的指令地址
函数调用约定描述了函数传递参数方式和栈协同工作的技术细节,不同操作系统、编译器、语言在此实现上有所差别:
函数调用步骤:
参数入栈
返回地址入栈
代码区跳转
栈帧调整
1. 保存当前栈帧状态值,以备后面恢复栈帧时使用(EBP入栈)
2. 将当前栈帧切换到新栈帧(将ESP值装入EBP,更新栈帧底部)
3. 给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈顶)
push param3
push param2
push param1
call fun
fun:
push ebp
mov ebp,esp
sub esp,xxx
函数返回步骤:
保存返回值
通常将函数的返回值保存在寄存器EAX中
弹出当前栈帧,恢复上一个栈帧
1. 给ESP加上栈帧的大小,降低栈顶
2. 将当前栈帧底部(也是顶部)保存的前栈帧EBP值弹入EBP寄存器,恢复上一个栈帧 <== 一个pop ebp,既改变了EBP,也改变了ESP,好厉害的说~
3. 将函数返回地址弹給EIP寄存器
4. 跳转至函数返回地址
add esp,xxx
pop ebp
retn ;该指令弹出栈顶元素至EIP寄存器并跳转