一、进程使用的内存都可以按照功能大致分为以下4个部分:
代码区、数据区、堆区、栈区
______________________________________________________________________
二、栈溢出
ESP: 系统栈最上面一个栈帧的栈顶
EBP:系统栈最上面一个栈帧的底部
EIP: 指向下一条等待执行的指令地址
- 在函数栈帧中,一般包含以下几类重要信息:
局部变量:为函数局部变量开辟的内存空间
栈帧状态值:保存前栈帧的顶部和底部(实际只保存底部,顶部通过堆栈平衡得到)
函数返回地址:保存当前函数调用前的“断点”
- 函数调用步骤:
参数入栈:从左到右
返回地址入栈:将代码区调用指令的下一条指令地址压入栈中
代码区跳转:从当前代码区跳转到被调用函数的入口
栈帧调整:保存当前栈帧状态,以备后面恢复本栈帧时使用(EBP入栈)-> 把当前栈帧切换到新栈帧(ESP装入EBP)-> 给新栈帧分配空间(ESP减去所需空间大小,抬高栈顶)
push arg3; push arg2; push arg1; call 函数地址;(1.保存返回地址<push address;> 2.jmp 入口地址;) push ebp; mov ebp esp; sub esp xxx; |
- 函数返回步骤:
保存返回值:通常将函数的返回值保存在EAX中
弹出当前栈,恢复上一栈帧。具体包括:在堆栈平衡的基础上,给ESP加上栈帧的大小,降低栈顶,回收当前栈帧的空间;将当前栈帧底部保存的前栈帧EBP值弹入EBP寄存器,恢复上一个栈帧;将函数的返回地址弹给EIP寄存器;
pop ebp;将上一栈帧底部恢复到EBP; retn;(1.弹出栈顶,即返回地址<栈帧恢复完成>; 2.处理器跳转到返回地址) |
- 修改邻接变量的原理
函数的局部变量在栈中一个挨着一个排列,如果这些局部变量中有数组之类的缓冲区,并且程序中存在数组越界的缺陷,那么越界的数组元素就有可能破坏栈中相邻变量的值,甚至破坏栈中所保存的EBP、返回地址等重要数据。(注意:数组地址增长方式:头在低地址,尾在高地址)
“内存数据”中的DWORD和我们逻辑上使用的“数值数据”是按照字节逆序过的