堆溢出与堆的内存布局有关,要搞明白堆溢出,首先要清楚的是malloc()分配的堆内存布局是什么样子,free()操作后又变成什么样子。

解决第一个问题:通过malloc()分配的堆内存,如何布局?

上图就是malloc()分配两块内存的情形。

其中mem指针指向的是malloc()返回的地址,pre_size与size都是4字节数据,size存放当前chunk(内存块,本文均不翻译)大小,pre_size存放上一个chunk大小。

因为malloc实现分配的内存空间是8字节对齐的,所以size的低3位其实没用,就取其中一位,用来标志前一个chunk是否被释放即PREV_INUSE位。当前一chunk释放,PREV_INUSE位置0,否则置1。

当malloc()分配的空间使用完毕后,将其mem指针传给free()进行释放。

解决第二个问题:free()对堆内存布局会产生什么影响?

上图的情形是,当前chunk的上一chunk被free()释放,容易发现,当前chunk的PREV_ISUSE标志位置0,表示前一chunk已经被释放。

被释放的chunk中,原先data的位置的低地址处被填入两个指针,分别是fd和bk,它们是forward和backward单词的缩写,分别表示前一个free chunk和后一个free chunk的地址。这样所有通过free()释放的内存chunk会组成一个双向链表。也因此一个chunk最小长度为16字节:2个size和2个指针。

当一个chunk被释放时,还有一件事情要做,就是检查相邻chunk的是否处于释放状态,如果相邻chunk空闲的话,就会进行chunk合并操作。由于每个chunk中都存放了size信息,所以很容易就找到当前chunk前后chunk的状态。

free()里面会调用一个unlink宏来执行合并操作:

#define unlink(P, BK, FD) {                      \
      FD = P->fd;                                    \
      BK = P->bk;                                    \
      FD->bk = BK;                                   \
      BK->fd = FD;                                   \
}

好了,这个宏就是堆溢出利用的关键。仔细阅读这个宏其实就是在一个双向链表中删除一个结点的操作:

P->fd->bk = P->bk
P->bk->fd = P->fd

其中P代表当前被删除结点。

解决最后一个问题:堆溢出如何利用?

首先构造一段堆溢出漏洞代码:

int main(void)
{
    char *buff1, *buff2;
    buff1 = malloc(40);
    buff2 = malloc(40);
    gets(buff1);
    free(buff1);
    exit(0);
}

给出堆空间布局:

low address
+---------------------+   <--first chunk ptr
|     prev_size       |
+---------------------+
|     size=48         |          
+---------------------+   <--first                  
|                     |
|     allocated       |         
|      chunk          |      
+---------------------+   <--second chunk ptr                
|    prev_size        |         
+---------------------+                     
|    size=48          |         
+---------------------+   <--second                  
|     Allocated       |         
|       chunk         |     
+---------------------+      
high address

现在使用gets函数进行堆溢出,将第2块chunk的prev_size覆盖为任意值,size覆盖为-4即0xfffffffc,fd位置覆盖为exit@got-12,bk位置覆盖为shellcode地址。

覆盖后的堆空间布局情况:

low address
+---------------------+   <--first chunk ptr
|     prev_size       |
+---------------------+
|     size=48         |          
+---------------------+   <--first                  
|                     |
|     allocated       |         
|      chunk          |      
+---------------------+   <--second chunk ptr                
|    XXXXXXXXX        |         
+---------------------+                     
|   size=0xfffffffc   |         
+---------------------+   <--second   
|     exit@got-12     |  
|    shellcode地址    |               
|     Allocated       |         
|       chunk         |     
+---------------------+      
high address

下面看free(buff1)时发生的操作:

1.first空间即buff1被释放掉

2.检查上一chunk是否需要合并(这里否)

3.检查下一chunk是否需要合并,检查的方法是检查下下个chunk的PREV_ISUSE标志位。即当前chunk加上当前size得到下个chunk,下个chunk加上下个size得到下下个chunk,因为我们设置下个chunk大小为-4,则下个chunk的pre_size位置被认为是下下个chunk的开始,下个size位置是0xfffffffc标志未置位,被认为是free的需合并。

那么,这里合并用到unlink宏时出问题了,同样对照上面图来看:

second->fd->bk=second->bk
    /* 1.second->bk是shellcode址
       2.shellcode的地址被写进了second->fd+12的位置
       3.second->fd是exit@got的地址-12
       4.所以second->fd+12的位置就是exit@got-12 + 12 = exit@got即got中存的exit地址
       因此exit()函数地址已经被shellcode地址替换
    */
second->bk->fd=second->fd

“shellcode的地址被写进了second->fd+12的位置” 这句话要好好理解,为什么second->fd->bk是second->fd+12呢? 其实second->fd指向前一chunk头部,加12是跳过pre_size,size和fd即到达bk位置。

最后程序在执行到exit(0)语句时,由于地址被替换,shellcode执行。

Linux 堆溢出原理分析的更多相关文章

  1. Linux堆溢出漏洞利用之unlink

    Linux堆溢出漏洞利用之unlink 作者:走位@阿里聚安全 0 前言 之前我们深入了解了glibc malloc的运行机制(文章链接请看文末▼),下面就让我们开始真正的堆溢出漏洞利用学习吧.说实话 ...

  2. Linux Kbuild工作原理分析(以DVSDK生成PowerVR显卡内核模块为例)

    一.引文 前篇博文<Makefile之Linux内核模块的Makefile写法分析>,介绍了Linux编译生成内核驱动模块的Makefile的写法,但最近在DVSDK下使用Linux2.6 ...

  3. CVE-2012-1876:Internet Exporter MSHTML.DLL CaculateMinMax 堆溢出简单分析

    0x01 2012 Pwn2Own 黑客大赛 Pwn2Own 是世界上最著名的黑客大赛,意在激励白帽黑客们进行顶尖的安全研究.在 2012 年 Pwn2Own 大赛上,来自法国著名的安全团队 Vupe ...

  4. 【linux】helloword原理分析及实战

    目录 前言 linux中hello word原理 hello word 实战 学习参考 前言 hello word 著名演示程序,哈哈 下面在 arm linux 下展示一下hello world,便 ...

  5. linux下编译原理分析

    linux下编译hello.c 程序,使用gcc hello.c,然后./a.out就能够执行:在这个简单的命令后面隐藏了很多复杂的过程,这个过程包含了以下的步骤: ================= ...

  6. Linux ubi子系统原理分析

    本文思维导图总纲: 综述 关于ubi子系统,早已有比较正式的介绍,也提供非常形象的介绍ubi子系统ppt 国内的前辈 alloysystem 不辞辛劳为我们提供了部分正式介绍的中文译文,以及找不到原文 ...

  7. 利用DWORD SHOOT实现堆溢出的利用(先知收录)

    原文链接:https://xz.aliyun.com/t/4009 1.0 DWORD SHOOT是什么捏? DWORD SHOOT指能够向内存任意位置写入任意数据,1个WORD=4个bytes,即可 ...

  8. CVE_2012_1876堆溢出分析

    首先用windbg附加进程ie页面内容进程,!gflag +hpa添加堆尾检查,.childdbg 1允许子进程调试,然后加载POC. POC: <html> <body> & ...

  9. linux下堆溢出unlink的一个简单例子及利用

    最近认真学习了下linux下堆的管理及堆溢出利用,做下笔记:作者作为初学者,如果有什么写的不对的地方而您又碰巧看到,欢迎指正. 本文用到的例子下载链接https://github.com/ctfs/w ...

随机推荐

  1. keepalived + nginx 搭建负载均衡集群

    第一章 keepalived 1.1 keepalived 服务说明 Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现 ...

  2. JavaScript eval

    JavaScript  eval JavaScript中的eval是Python中eval和exec的合集,既可以编译代码也可以获取返回值. // EvalError 执行字符串中的JavaScrip ...

  3. final修饰符与多态

    知识点一.final 最终的可以修饰属性.方法.类1.final修饰的属性,表示常量,初始化以后值不能改变.final修饰引用数据类型的变量,引用地址不能改变.2.final修饰类,不能被继承.比如: ...

  4. UI自动化(五)dom

    获取标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // 直接获取标签   document.getEleme ...

  5. 要想成为前端大神,那些你不得不知晓的web前端命名规范。

    一.Web语义化 1.1 H5的语义化 对于经验资深的前端er,在给web布局时,相信都会很注重标签和命名的规范.尤其是随着html5的普及发展,更是把web前端语义化推向一个新的台阶上.比如html ...

  6. Speak Confident English

      I hope you don'd mind me asking...   I know I shouldn't ask, but.... - Well, before I answer that ...

  7. C# 获取往控件中拖进的文件或文件夹的信息

    C# 获取往控件中拖进的文件或文件夹的信息(原创)       在做C#的WinForm开发的时候,有时需要用户往指定的控件中拖进文件或者文件夹.然后根据用户拖进来的文件或者文件夹获取其信息并进行下一 ...

  8. Web版记账本开发记录(五)

    今天是第五天,不过今天有点事什么都没做,只是看了看一些教学视频, 今天家里有事, 还没来得及实践,等明天再实践,然后再完善完善.

  9. yii中接收微信传过来的json数据

    //控制器<?php namespace frontend\controllers; use frontend\models\User; use yii; use yii\web\Control ...

  10. part4:素数判别

    法一: √n判别 这个的话就是暴力了吧,不过只能判别单个数是不是质数,而且很大的话会爆 //没有代码qwq(不想写 法二:埃式筛法 O(nloglogn)判别 直接代码好不啦: ],n,num; ]; ...