1、内存结构
在Windows内存结构中,进程内存按功能分为四个区域,分别是栈区、堆区、代码区、数据区。
- 代码区:用于存放程序汇编后的机器代码和只读数据,这个内存段的数据一般被标记为只读数据,任何试图修改这个内存段的数据都指令都会引发一个segmentation violation 错误。
- 数据区:用于存储全局变量和静态变量、
- 堆区:由进程利用相关函数或运算符动态申请,用完后解释并归还给堆区。
- 该内存区域由系统自由分配,用于动态存储函数之间的调用关系。
大端序与小端序的概念:
- 大端序:数据的高位字节存放在低地址,低位字节存放在高地址
- 小端序:数据的位高字节存放在高地址,低位字节存放在低地址
2、栈溢出漏洞及利用分析
对应的概念:
栈帧:
- 函数的栈帧:操作系统为每个函数调用划分的一个空间,是一个独立的栈结构,其生长方向是由高地址乡低地址生长。而系统栈则是这些函数调用栈帧的集合。
- 系统栈:由操作系统自动维护,用于实现高级语言中函数的调用。
- 栈帧中的内容:前一个栈帧的栈底位置,该函数的局部变量,该函数调用的参数,用于保存调用前指令的返回地址ret。
函数的调用
(1)调用步骤:参数入栈–>返回地址ret入栈–>代码区跳转–>将当前栈帧调整为被调用函数的栈帧
(2)返回主调函数的步骤:保存返回值–>弹出当前栈帧并将前一个栈帧(ret)恢复为当前栈帧.
缓冲区溢出:
简单的说,缓冲区溢出就是超长的数据向小缓冲区复制,导致数据超出了小缓冲区,导致缓冲区其他的数据遭到破坏,这就是缓冲区溢出。
栈溢出漏洞的基本原理与存在原因:
基本原理:
栈溢出是缓冲区溢出的一种,也是最常见的。在函数的栈帧中,局部变量是顺序排列的,局部变量之下是紧跟着的前栈帧EBP及函数返回地址RET。如果这些局部变量为数组,那么越界的局部变量就会覆盖局部变量甚至覆盖前栈帧EBP和函数返回地址RET,从而造成程序的异常。
栈溢出的情况通常有两种:修改临近变量和修改返回地址。
产生原因:
一方面因为程序员的疏忽,使用了 strcpy、sprintf 等不安全的函数,增加了栈溢出漏洞的可能。另一方面,因为栈上保存了函数的返回地址等信息,因此如果攻击者能任意覆盖栈上的数据,通常情况下就意味着他能修改程序的执行流程,从而造成更大的破坏。这种攻击方法就是栈溢出攻击(stack smashing attacks)。
栈溢出攻击的原因是由于程序中缺少错误检测,另外对缓冲区的潜在操作(比如字符串的复制)都是从内存低址到高址,而函数调用的返回地址往往就在缓冲区的上方(当前栈底),这为我们覆盖返回地址提供了条件。
3、防护方法
/GS保护机制简介:
栈中的守护天使–GS,亦称作Stack Canary / Cookie 。
GS机制分三个步骤:计算随机种子 --> canary写入栈帧 --> GS校验。
实现过程:
- 程序启动时,读取.data的第一个DWORD作为基数,然后和各种元素(时间戳,进程ID,线程ID,计数器等等)进行XOR加密
- 然后将加密后的种子再次写入.data的第一个DWORD
- 函数在执行前,把加密后的种子取出,与当前esp进行异或计算,结果存入EBP的前面
- 函数主体正常执行
- 函数返回前(retn前一点),把cookie取出与esp异或计算后,调用security_check_cookie函数进行检查,与.data节里的种子进行比较,如果校验通过,则返回原函数继续执行;如果校验失败,则程序终止。
查看Windows系统中的DEP是否启用
DEP 的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入 shellcode 时,程序会尝试在数据页面上执行指令,此时 CPU 就会抛出异常,而不是去执行恶意指令。
ASLR保护机制简介
微软在Windows Vista、2008 server、Windows 7、Windows 8等系统的发布中, 开始将ASLR作为内置的系统保护机制运行, 将系统映像的基址设置到1/256的random slot上, 同时将各个线程的堆栈和堆进行随机化。