[调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)

0x00 基础知识们

之前写完操作系统,再来看ret2dir果然一片明朗,相比于之前对于映射机制方面的欠缺,这里明显更加得心应手

1.Linux内存管理

首先Linux的内存管理大致会分为Buddy System 和 Slub算法两种,这里由于我寻找的是一篇古早的分析,所以可能会有些许欠缺,但是目前学习该同样很早的漏洞利用手法已经足够,比较详细且时间较近的分析我推荐arttnba3师傅的个人博客

【OS.0x02】Linux 内核内存管理I - 页、区、节点 - arttnba3's blog

1.Buddy System(伙伴系统)

分配单元为页,且该系统的初衷就是为了缓解内存碎片化的问题,这里由于还没涉及到很多,所以仅仅知道他是分配页框的即可

2.Slub算法

上面我们知道Buddy System是以页面为单位来分配的,因此当我们程序需要分配几十字节大小该怎么办呢?总不能不管什么都分配几个页框吧,因此就有了该slub算法的由来

[调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第1张图片


这里介绍一下大致结构,首先就是一个统筹全局的数组kmalloc_caches[12], 他是一个kmem_cache元素类型的数组,里面包含了12个kmem_cache类型,而这个kmem_cache里面包含着的就是一些待分配的内存块信息,这里我们kmalloc_caches[12]里面的不同的kmem_cahce自身所担当的职能是不同的,例如该数组中的一个kmem_cache类型只会包揽一种大小内存块的信息,因此我们的Slub算法总共是可以分配12种大小不同的内存块,分别是:

2^3, 2^4, 2^5, 2^6, ... , 2^11, 96, 192字节

然后每个kmem_cache里面又保存着当前分配器的类型以及分配情况,其中比较重要的是nodecpu_slab,他俩的类型分别是kmem_cache_nodekmem_cache_cpu,这两个的作用也有所不同,首先我们了解一个知识点,那就是分配器会首先申请一整页的内存,然后该内存就是一个slab,当分配器获取该slab后再从中切割小块分给用户

[调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第2张图片


然后剩下的信息我们到具体分配过程中了解

  • 首先是最初创建slub的时候:
    系统中不存在任何slab供我们使用,所以此时我们会向buddy system中申请一个页框来存放我们的初试slab,然后我们根据需求的块大小找到kmalloc_cahces中适配的分配器,将我们获取到的slab提供给他,注意这里我们会将该slab分配给kmem_cache_cpu下,且该字段下也只会指向一个slab这点得注意,下面就是过程图:

    [调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第3张图片


    这里可以看到有一个页框是链接到kmem_cache_cpu下的page字段,该字段指向的就是分配slab的页框地址,然后我们也会将第一个块标记为已分配并且返还回去,然后剩下的空间我们会将其分割成一个个空闲块组成的链表,该链表的地址也会分配给kmem_cache_cpu下的free_list字段,然后如果我们该页框上面有空闲块的话,那么我们每次就将其标记为已分配然后unlink空闲链表返还分配地址就可以了

  • 然后如果我们的kmem_cache_cpu分配满了并且kmem_cache_node中有包含空闲块的slab的时候,就会将其替换,然后把塞满的块接入到kmem_cache_node中的full双链表下

    [调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第4张图片


    然后再切换之后返还一个空白块就可以了

  • 如果我们此时kmem_cache_cpu指向的页也满了,然后kmem_cache_node也没有空闲的块,那么此时我们就会重新向buddy system申请一个新的页加入kmem_cache_cpu当中,然后将满的块移入kmem_cache_node,如下:

    [调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第5张图片


    然后就是重新获取

  • 上面讲完了分配的过程,下面我们来讲述释放的过程,这里我们的操作就主要是在kmem_cache_node中了,第一种情况那就是我们要释放的块在kmem_cache_node中的full双链表下:

    [调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第6张图片


    此时我们直接将该块标记为空闲,然后将其加入partial双链表当中

  • 而如果我们释放的块是在kmem_cache_node中的partial双链表当中或者说是在kmem_cache_cpupage当中,我们直接释放然后加入空闲队列就行了,这里我直接按照上图的结果来展示释放后的状态

    [调试逆向] Linux内核PWN-ret2dir(附赠基础slub算法!)_第7张图片


  • 最后一种情况就是我们所释放的该块是该slab中最后一个块,那么此时我们释放该块后然后再归还整个slabbuddy system即可,这里就不画图辣。

2.ret2dir原理

ret2dir的存在是为了解决SMAP/SMEP保护模式的一种手法,该保护模式是阻止了内核程序执行用户程序,第一次被提出是在14年的一篇论文,这里页给出链接
ret2dir原论文
首先我们得知道一下Linux内存中的基本布局,链接如下,有兴趣的同学可以自行观看
Linux 内存布局

我们可以看到有以下一个区域

 复制代码 隐藏代码
========================================================================================================================
    Start addr    |   Offset   |     End addr     |  Size   | VM area description
========================================================================================================================
                  |            |                  |         |
 0000000000000000 |    0       | 00007fffffffffff |  128 TB | user-space virtual memory, different per mm
__________________|____________|__________________|_________|___________________________________________________________
                  |            |                  |         |
 0000800000000000 | +128    TB | ffff7fffffffffff | ~16M TB | ... huge, almost 64 bits wide hole of non-

你可能感兴趣的:(linux,算法,网络)