深切明白Glibc堆的完成(上) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

深切明白Glibc堆的完成(上)

申博_新闻事件 申博 116次浏览 已收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

在浏览本文之前,发起人人先读一下这篇文章,个中议论了一个过期的但很重要的内存损坏破绽,我将之称为“客栈缓冲区溢出”。除此之外,我还假定若是我作为进击者怎样应用这些破绽来掌握长途递次并使其运转歹意shellcode。

在测试中,我发明若是要运用一种称为“stack canaries”的破绽减缓步伐,则进击者则难以应用这些客栈缓冲区溢出破绽,除非,他们运用分外的破绽能力应用它们。

Stack Canaries是已知的安排在缓冲器和掌握数据之间的一个随机值,当缓冲器溢出时,最早被损坏的就是这个值。

当开发人员运用种种基于客栈的破绽减缓机制时,进击者通常会运用与堆相干的破绽(如Use-after-free破绽、double free破绽和堆溢出破绽)来构建破绽。这些基于堆的破绽比基于客栈的破绽更难明白,因为针对基于堆的破绽的进击手艺能够异常依靠于堆分派器的内部完成的现实事情体式格局。

 Use-after-free破绽(简称UaF破绽)是以后最盛行的高危内存损坏破绽。如今针对UaF破绽的检测事情实在不完美,缘由是UaF破绽发作的特性是分派内存、开释内存、运用已开释的内存并按递次涌现,而这3种事宜能够涌如今递次的任何地位,须要跟踪较长的实行序列并搜刮潜伏的风险事宜序列能力检测到该破绽,这很大程度上提高了检测的难度。

Double Free实在就是同一个指针free两次。虽然一样平常把它叫做double free。实在只如果free一个指向堆内存的指针都有能够发作能够应用的破绽。double free的道理实在和堆溢出的道理差不多,都是经由过程unlink这个双向链表删除的宏来应用的。只是double free须要由本身来捏造全部chunk而且诳骗操纵体系。

因而,在议论应用基于堆的破绽之前,我将先议论堆的事情体式格局。起首,我会引见一些观点,并议论怎样竖立新的堆块。其次,我将深入探讨怎样开释和收受接管这些块。

堆的事情体式格局会依据完成的平台分歧,而存在分歧的堆完成。比方,Google Chrome的PartitionAlloc与FreeBSD中运用的jemalloc堆分派器异常分歧。Linux中默许的glibc堆完成与堆在Windows中的事情体式格局也有很大分歧。因而,我将重要存眷glibc堆分派器,即在Linux装备上默许运转的C/ c++递次中,堆分派是怎样事情的。这个堆派生自ptmalloc堆完成,而它现实上又是从更老的dlmalloc(Doug Lea malloc)内存分派器派生而来的。

堆是甚么,它的用处是甚么?

堆被C和c++递次员用来在递次实行时期手动分派新的历程内存地区,递次员要求堆治理器经由过程挪用堆函数(如malloc)来分派这些内存地区。然后,递次员能够运用、修正或援用这些分派的内存地区,直到递次员不再须要它,并经由过程挪用free将分派返回给堆治理器为止。

下面是一个C递次怎样在堆上分派、运用和开释组织的示例:

typedef struct 
{
    int field1;
    char* field2;
} SomeStruct;
int main()
{
    SomeStruct* myObject = (SomeStruct*)malloc(sizeof(SomeStruct));
    if(myObject != NULL)
    {
        myObject->field1 = 1234;
        myObject->field2 = “Hello World!”;
        do_stuff(myObject);
free(myObject);
    }
    return 0;
}

只需递次员遵照一些简朴的划定规矩,堆治理器就能够确保每一个运动分派不会互相堆叠,这也是大多数C和c++递次依靠堆的一个缘由。

下图列出了递次员在运用堆时必需遵照的一些基础划定规矩,和递次员违背这些划定规矩会发作的一些破绽种别。在背面的文章中,我将更细致地议论一切这些与堆相干的破绽。

深切明白Glibc堆的完成(上)

固然,malloc和free并非C和c++递次员与堆交互的独一体式格局。c++开发人员经常经由过程c++操纵符new和new[]来分派内存。这些分派必需运用响应的c++操纵符delete和delete[]来开释,而不是运用free。递次员还能够经由过程与malloc兼容的堆函数(如calloc、realloc和memalign)分派内存,这些堆函数和malloc一样,终究经由过程free开释。

为了简朴起见,我将起首议论malloc和free。一旦明白了这两个函数,其他大多数堆函数就会变得异常轻易明白。

下面是一个c++递次怎样在堆上分派、运用和开释组织的示例:

class SomeClass
{
public:
    int field1;
    char* field2;
};
int main()
{
    SomeClass* myObject = new SomeClass();
    myObject->field1 = 1234;
    myObject->field2 = “Hello World!”;
    do_stuff(myObject);
    delete myObject;
    return 0;
}

内存块和块分派战略

假定递次员经由过程malloc要求10个字节的内存,为了知足这个要求,堆治理器须要做的不只仅是找到递次员能够写入的随机的10字节地区,它还须要存储关于分派的元数据,此元数据会与递次员能够运用的10字节地区一同被存储。

堆治理器还须要确保分派在32位体系上是8字节对齐的,或许在64位体系上是16字节对齐的。若是递次员只想存储文本字符串或字节数组之类的数据,那末对齐就不重要了。然则若是递次员盘算运用分派来存储更庞杂的数据组织,那末对齐会对递次的正确性和机能发作很大的影响。因为malloc没法晓得递次员将在分派中存储甚么,因而堆治理器必需默许确保一切分派都是对齐的。

深切明白Glibc堆的完成(上)

此分派元数据会和对齐添补字节与malloc将返回给递次员的内存地区一同被存储,出于这个缘由,堆治理器在内部分派的内存“块”比递次员最后要求的略大。当递次员要求10个字节的内存时,堆治理器会发明或竖立一个新的内存块,该内存块足以存储10个字节的空间和元数据和对齐添补字节。然后,堆治理器将此块符号为“已分派”,并返回一个指向块内对齐的10字节“用户数据”地区的指针。此时,递次员将该地区视为malloc挪用的返回值。

块分派的基础策

那末堆治理器怎样在内部分派这些块呢?

起首,让我们看看分派小bin内存的战略,分派小bin内存是堆治理器的重要事情。我将在下面更细致的诠释这些步调,一旦我们完成了这些步调,就能够检察大规模分派的状况。

分派小bin内存的战略是如许的:若是存在先前开释的内存块,而且该块大到足认为要求供应效劳,则堆治理器将运用该开释的块举行新分派。不然,若是堆顶部有可用空间,堆治理器将从可用空间中分派一个新块并运用它。另有一种状况就是,堆治理器将要求内核向堆的末端增添新内存,然后从这个新分派的空间分派一个新块。若是一切这些战略都失利,分派将没法获得效劳,malloc将返回NULL。

从余暇块分派

深切明白Glibc堆的完成(上)

从观点上讲,分派先前开释的块异常简朴。当内存被发送回余暇块时,堆治理器会在一系列称为“bin”的分歧链表中跟踪这些已开释的块。当发出分派要求时,堆治理器会搜刮那些bin,以寻觅充足大的余暇块来知足要求。若是找到一个块,它能够从bin中删除该块,将其符号为“已分派”,然后将指向该块的“用户数据”地区的指针作为malloc的返回值返回给递次员。

出于机能缘由,有几种分歧范例的bin,即疾速bin、未排序bin、小bin、大bin和每线程tcache机制。

从堆的顶部分派

深切明白Glibc堆的完成(上)

若是没有可用的余暇块能够为分派要求供应效劳,则堆治理器必需从头开始组织新的块。为此,堆治理器起首检察堆末端的余暇空间(偶然称为“顶部块”或“盈余块”),以检察是不是有充足的空间。若是存在,则堆治理器将从该可用空间中天生一个新的块。

OceanLotus隐写术剖析申报

简介 OceanLotus APT,也称为APT32和APT-C-00,是从2012年开始活跃的间谍组织。BlackBerry Cylance研究人员近期发现该组指的一个新的payload加载器,其中使用隐写术来读取隐藏在.png图像文件中的加密payload。隐写算法使用了最低有效位(

在堆的顶部要求内核供应更多内存

深切明白Glibc堆的完成(上)

一旦堆顶部的可用空间用完,堆治理器将不得不要求内核向堆的末端增添更多内存。

在初始堆上,堆治理器要求内核经由过程挪用sbrk在堆的末端分派更多内存。在大多数基于Linux的体系上,这个函数在内部运用一个名为“brk”的体系挪用。这个称号最后的意义是“变动递次中缀地位”,不外说法太庞杂,即它在递次加载到内存以后向该地区增添了更多内存。因为这是堆治理器竖立初始堆的地位,以是这个体系挪用的作用是在递次初始堆的末端分派更多内存。

终究,运用sbrk扩大堆会失利。因为堆终究会变得异常大,以至于进一步扩大会致使它与历程地点空间中的其他内容发作争执,好比内存映照、同享库或线程的客栈地区。一旦发作这类状况,堆治理器将运用对mmap的挪用将新的非一连内存附加到初始递次堆。

若是mmap也失利,那末历程就不能分派更多内存,malloc返回NULL。

经由过程MMAP举行堆外(OFF-HEAP)分派

异常大的分派要求会在堆治理器中获得特别处置惩罚。这些大块运用对mmap的直接挪用在堆外分派,而且运用块元数据中的符号来符号该现实。当这些伟大的分派稍后经由过程对free的挪用返回给堆治理器时,堆治理器经由过程munmap将全部mmaped地区开释回体系。

默许状况下,这个阈值在32位体系上最小为128KB,最大为512KB,在64位体系上是32MB,若是堆治理器检测到这些大型分派正在被暂时运用,则此阈值也会动态增添。

arena

arena的引进是为了处理多线程内存分派合作的题目,在多线程应用递次中,堆治理器须要珍爱内部堆数据组织免受能够致使递次瓦解的合作前提的影响。在ptmalloc2之前,堆治理器经由过程在每一个堆操纵之前简朴地运用一个全局互斥锁来完成这一点,以确保在任何给定时候只要一个线程能够与堆交互。

只管此战略有用,但堆分派器的运用率和机能都异常高,因而会致使运用大批线程的应用递次涌现严峻的机能题目。为此,ptmalloc2堆分派器引入了“arena”的观点。每一个arena本质上是一个完整分歧的堆,它完整独登时治理本身的块分派和余暇bin。每一个arena依然运用互斥锁序列化对其内部数据组织举行接见,然则只需线程与分歧的arena交互,就能够安全地实行堆操纵,而不会相互争执。

递次的初始(“main”)arena只包罗我们已看到的堆,关于单线程应用递次,这是堆治理器将运用的独一arena。然则,当新线程到场历程时,堆治理器会分派并将辅佐arena附加到每一个新线程,以削减当线程试图实行malloc和free之类的堆操纵时,举行不必要的守候。

关于每一个到场历程的新线程,堆治理器都邑实验找到一个对应的余暇的arena,并将其附加到该线程。一旦一切可用的arena都被其他线程运用,堆治理器就会竖立一个新的arena, 32位历程中的arena数目最多为2x cpu-cores,64位历程中的arena数目最多为8x cpu-core。一旦到达这个限定,堆治理器就会摒弃,因为多个线程不得同享一个arena,且实行堆操纵时须要个中一个线程守候另外一个线程。

子堆

深切明白Glibc堆的完成(上)

虽然子堆的事情体式格局与初始递次堆基础雷同,但照样有两个重要区分。追念一下,初始堆位于递次加载到内存以后的地位,并由sbrk动态扩大。相反,每一个子堆都运用mmap定位到内存中,堆治理器运用mprotect手动模仿子堆的增进。

当堆治理器想要竖立子堆时,它起首要求内核生存一个内存地区,这个内存地区能够经由过程挪用mmap增进为子堆。生存此地区不会直接将内存分派到子堆中,它只是要求内核不要在这个地区内分派线程客栈、mmap地区和其他分派。

默许状况下,子堆的最大巨细在32位历程上是1MB,在64位体系上是64MB。

这是经由过程向mmap要求符号为PROT_NONE的页面来完成的,该页面充任内核的提醒,它只须要为该地区生存地点局限便可,还不须要内核为它附加内存。

深切明白Glibc堆的完成(上)

运用sbrk增添初始堆的地位时,堆治理器会经由过程手动挪用mprotect将地区中的页面从PROT_NONE变动为PROT_READ | PROT_WRITE,模仿将子堆“增进”到这个生存地点局限。这会致使内核将物理内存附加到这些地点,这现实上会致使子堆迟缓增进,直到全部mmap地区被填满。一旦全部子空间耗尽,arena就会分派另外一个子堆。这就许可辅佐arena险些无穷地增进,只要当内核耗尽内存或历程耗尽地点空间时才会终究住手。

注重:初始(“main”)arena只包罗主堆,主堆位于递次二进制加载到内存的地位以后,并运用sbrk举行扩大。这是用于单线程应用递次的独一arena,在多线程应用递次中,新线程被分派到辅佐arena。运用arenas能够下降线程在实行堆操纵之前须要守候互斥工具的能够性,从而加速递次的速率。与主arena分歧,这些辅佐arena从一个或多个子堆分派块,其在内存中的地位起首运用mmap竖立,并经由过程运用mprotect增进。

块元数据

如今我已引见了分派块的一切要领,块不只包罗作为malloc返回值供应给递次员的“用户数据”地区,还包罗元数据。然则这个块元数据记录了甚么,它又位于那边?

因为堆治理器源代码将一个块末端的元数据与下一个块开首的元数据相结合,而且存在或运用了几个元数据字段。以是,块元数据的地位详细取决于关于大块的种种特性。

本文,我们只看运动分派,它有一个size_t标头, size_t是透露表现长度(尺寸)的范例,这个范例是由 typedef unsigned int size_t; 界说的,一样平常用于生存一些长度信息,好比数组的长度、字符串的长度等。它位于给递次员的“用户数据”地区背面。这个字段(源代码挪用mchunk_size)是在malloc时期编写的,以后由free决议怎样处置惩罚分派的开释。

 size_t值在32位体系上是4字节的整数,在64位体系上是8字节的整数。

深切明白Glibc堆的完成(上)

mchunk_size存储了4条信息:块巨细,和称为“A”、“M”和“P”的三个位。这些都能够存储在雷同的size_t字段中,因为块巨细老是8字节对齐的或许在64位上是16字节对齐的,因而块巨细的最低三个位老是零。

 “A”标记用于通知堆治理器该块是属于辅佐arena,照样主arena。在余暇时期,堆治理器仅被给予指向递次员想要开释的分派的指针,而且堆治理器须要肯定指针地点的谁人域。若是在块的元数据中设置了A标记,堆治理器必需搜刮每一个arena,并检察指针是不是位于该arena的任何子堆中。若是未设置该标记,则堆治理器能够会中缀搜刮,因为它晓得块就来自初始arena。

“M”标记用于指导块是经由过程mmap在堆外分派的伟大分派,当这个分派终究被传回free时,堆治理器会马上经由过程munmap将全部块返回给操纵体系,而不是试图收受接管它。出于这个缘由,开释的块永久不会设置此标记。

“P”标记令人困惑,因为它现实上属于前一个块。它透露表现前面的块是一个余暇块。这意味着,当这个块被开释时,它能够安全地连接到前一个块,从而竖立一个更大的余暇块。

深切明白Glibc堆的完成(上)

接下来,我将更细致地议论怎样将这些余暇块“兼并”在一同,并议论怎样运用分歧范例的“bin”分派和收受接管这些块。在这以后,我们还会研讨一些分歧种别的堆破绽,和进击者怎样应用这些破绽长途掌握易受进击的递次。

OceanLotus隐写术剖析申报

简介 OceanLotus APT,也称为APT32和APT-C-00,是从2012年开始活跃的间谍组织。BlackBerry Cylance研究人员近期发现该组指的一个新的payload加载器,其中使用隐写术来读取隐藏在.png图像文件中的加密payload。隐写算法使用了最低有效位(


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明深切明白Glibc堆的完成(上)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址