- 知识背景
- 栈溢出及防护
- 堆溢出及防护
一、知识背景
关于栈溢出的知识背景
1. 典型操作系统的内存布局:
典型操作系统的内存布局分段的意义:
缓存命中率高、访问控制
2. 进程的内存布局:
进程的内存布局3. 堆栈的区别:
地址增长方向
heap - 堆 地址从小到大
stack - 栈 地址从大到小
支持及作用
栈时具有硬件直接支持的,栈存储数据和控制流信息。
堆是由操作系统的库函数予以支持的,堆存储数据。
4. ESP与EIP:
esp - 栈指针寄存器
esp指向栈顶(存储栈顶的地址)
push则esp减
pop则esp增
eip - 指令指针寄存器
eip指向.test中存放的代码指令,指向的指令为即将被执行的指令。
5. 栈的工作原理:
栈的作用:为函数的调用和执行维护存储结构。
函数调用时:存储数据
函数返回时:恢复数据
栈帧:调用函数将在栈上创建一个结构化空间,称为栈帧。
锚点:函数被调用时的栈指针位置。可用于对函数的局部数据寻址、返回到调用者。
call - return的实现:
1)存储参数
push arg2
push arg1
2)存储函数f返回后需要执行的指令地址RA(push RA)
call f
3)存储返回位置
把ebp原来存储的值压栈,将esp此时的位置X存入ebp。
push %ebp
mov %esp %ebp
4)执行函数f的指令
X可帮助函数f的局部变量寻址。
5)返回
将ebp存储的值(即返回位置)告诉esp,并回复ebp原来的数据。此外,通过此时栈顶的数据RA,定位到下一条需要执行的指令。
mov %ebp %esp
pop %ebp
retn
6. 缓冲区:
copy-on-write:
资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享。
很多时候,函数执行中需要局部分配较大的线性内存空间。
函数执行分配空间
增长方向
栈增值方向为从高到低,缓冲区的增长从低到高。
要求
缓冲区应该足够大,写入数据之前应该先检查是否仍有剩余空间可用。
关于堆溢出的知识背景
1. 堆的创建和维护:
通过malloc实现
malloc
2. 堆的工作原理:
malloc创建的区域首先是堆的场地(Arena),进程申请堆空间时,malloc从场地中划分对应大小的区域供其使用。同一场地内可能存在多个堆段。
注意:
Main Arena没有heap_info
有多个heap的Thread Arena只有一个malloc_state
场地的数量限制 - 与cpu内核数量有关:
32位系统:#_of_Arena = 2 * #_of_cores + 1
64位系统:#_of_Arena = 8 * #_of_cores + 1
堆块的形态:
N表示此块是否属于main arean; M表示此块是否是mmap()创建的; P表示前块是否正在使用。
堆块形态 链表结构
显式链表结构bin:
bin是记录free chunk的链表数据结构,在glibc中可分为fastbinsY和bins。
fastbin主要用于高效的分配和回收比较小的内存块,采用LIFO形式的单链表结构。
bin结构
顶块top chunk:
不属于任何bin。
当前所有空闲块(无论哪种bin)全都尺寸不合时,由顶块应急。
顶块比请求尺寸大——分割供给使用,剩余部分为新顶块。
顶块比请求尺寸小——全堆无适合块,扩展堆/分配新堆。
二、栈溢出及防护
1. 主要原因:忽略了边界检查
-> 开发者认为被操作对象合法
-> 二进制代码底层细节复杂,容易使人忽略问题的存在
-> 一些重要的遗产代码在最初的设计中存在此类缺陷
2. 现有的栈溢出防护技术
数据执行保护(W⊕X,NX-bit):
机制:
对内存页面增加一个标识比特,使它要么可改写,要么可执行。
缺陷:
只要不试图执行数据区内容,则既不能阻止溢出,又不能发现溢出。
栈帧上的“金丝雀”:
机制:
栈上设置检查点,检查点数值异常,则代表栈溢出发生。
缺陷:
开销大,对遗产代码不起作用。
影子栈:
机制:
构造一个额外的动态数据区域,称为影子站。当call发生时,在返回地址入栈的同时,也将其写入影子堆栈。每当return发生时,在弹出并转向返回地址前,将其与影子堆栈的栈顶比较。
缺陷:
开销大,无法保护遗产代码。
分离的控制/数据栈:
机制:
修改操作系统的内存布局,分配两个栈空间,分别用于控制参数和数据的存储。
缺陷:
无法保护遗产代码,不适用于完全由汇编语言构成的函数。
三、堆溢出及防护
1. 与栈溢出的区别
溢出方向等于堆增长方向。首先破坏(虚拟地址意义上的)下一个堆块的构造。
2. linux上的典型堆溢出利用方式
- 攻击fastbin
- 攻击unlink
攻击fastbin:
基于fastbin块LIFO的特点,我们可以先申请,然后释放,再申请就可以得到原来地址的块。
但是这不能满足我们的需求,我们需要将堆分配在可控地址。
我们可以通过堆溢出更改已经申请块的fd,使其指向我们可控的地址,并且在可控地址上伪造假的fastbin结构。
释放,再申请两次,第2次就可以得到分配在可控地址上的块。(覆盖fd)
攻击fastbin
攻击unlink:
unlink攻击技术就是利用”glibc malloc”的内存回收机制,将second chunk给unlink掉,并且,在unlink的过程中使用shellcode地址覆盖掉free函数(或其他函数也行)的GOT表项。这样当程序后续调用free函数的时候(如上面代码[5]),就转而执行shellcode了。显然,核心就是理解glibc malloc的free机制。
一旦涉及到free内存,那么就意味着有新的chunk由allocated状态变成了free状态,此时glibc malloc就需要进行合并操作——向前以及(或)向后合并。这里所谓向前向后的概念如下:将previous free chunk合并到当前free chunk,叫做向后合并;将后面的free chunk合并到当前free chunk,叫做向前合并。
攻击思路 攻击思路
3. windows堆溢出攻击的主要形式
- 利用向量化异常处理(VEH)
- 利用系统默认异常处理函数(UEF)
- Heap spray
- Bitmap Flipping攻击
- Bitmap XOR攻击
- Heap Cache攻击
4. 堆溢出防御
堆依靠系统库实现其维护,因此,堆保护 = 系统库升级。
针对unlink的保护:
Double Free检测
next size非法检测
双链表冲突检测
网友评论