一、_lib_malloc函数介绍
- 当我们在应用层调用malloc申请堆的时候,在glibc中实际上调用的是_lib_malloc函数,但是_lib_malloc函数只是用来简单的封装_int_malloc函数的,_int_malloc函数才是申请堆的核心函数。
二、__malloc_hook全局变量
- 函数介绍:_lib_malloc首先通过__malloc_hook全局变量获取一个函数指针,然后判断这个函数是否为空,这个函数代表用户自定义的堆分配函数,主要为了方便用户快速修改该函数并进行测试。
- _malloc_hook漏洞:如果__malloc_hook被修改,那么就会执行被修改后的函数(one_gadget),以后文章介绍。
- 从下面的源码可以看到,先读取__malloc_hook全局变量,然后判断是否有用户自定义的堆分配函数,如果有就执行,不再进行系统的堆分配了(_int_malloc)。
三、寻找arena并执行_int_malloc函数函数
- 解析_lib_malloc函数回去寻找一个arena来试图分配内存(arena_get),然后调用_int_malloc函数取申请内存。
- 如果分配失败(注:_int_malloc函数中会再次判断是否有arena可用),ptmalloc会尝试再去寻找一个可用的arena,并再次调用_int_malloc函数取申请内存。
- 如果申请到了arena,那么在使用完arena之后,函数退出之前还需要解锁(_lib_lock_lock)。
- _int_malloc函数:在下面介绍。
四、_lib_malloc函数总结
- _lib_malloc函数就是做以下事情:
- 要么没有申请到内存。
- 要么通过mmap/brk申请内存。
- 要么在其arena中分配内存。
- (或者出错退出)。
- 最终返回内存(return)。
- 核心:调用_int_malloc函数。
五、_int_malloc函数介绍
- 当我们在应用层调用malloc申请堆的时候,在glibc中实际上调用的是_lib_malloc函数,但是_lib_malloc函数只是用来简单的封装_int_malloc函数的,_int_malloc函数才是申请堆的核心函数。
- _int_malloc会根据应用层用户申请的内存块大小,从而分配相应的chunk给用户使用。
函数的分配堆内存的主要执行流程
- ①请求大小在fastbin的范围内:在fastbins中找是否有对应的chunk可以使用。
- ②请求大小在smallbin的范围内:在smallbin中找是否有对应的chunk可以使用。
- ③请求大小在largebin的范围内:先调用malloc_consolidate对fastbins进行整理。然后在unsortedbin中查看是否有满足要求的chunk可以使用。
- ④在largebin中寻找可用的chunk来使用。
- ⑤寻找较大的bin链中是否有可用的chunk来使用。
- ⑥切割topchunk来使用。
- ⑦topchunk也不够了,再次调用malloc_consolidate整理fastbins。
- ⑧topchunk不够用,再次malloc_consolidate之后还没有可以用的,最终调用sysmalloc(系统调用)申请内存。
- 下面根据_int_malloc函数的执行流程,一步一步介绍函数是怎么执行的。
六、定义相关变量
- _int_malloc函数进入之前的第一步是定义相关的变量。
七、检测arena
- 如果没有可用的arena,或者arena的内存不足,那么就通过系统调用mmap去申请一块内存,并返回。
八、检查fastbins
- _int_malloc函数先去检查应用层所分配的堆大小是否属于fastbins的大小范围内,如果属于就去fastbins中查找是否有可用的freechunk可用。
①判断大小范围
- 判断malloc的大小是否属于fastbins范围内。
②寻找可用freechunk
- 根据malloc的大小,查找相对应大小的fastbin链是否有可用的freechunk。如果有就获取(REMOVE_FB),如果没有就直接退出。
③系统检测
- 取走对应的freechunk之前,还需要进行系统检测,检测见文章:https://blog.csdn.net/qq_41453285/article/details/97753705。
④alloc_perturb
- 如果设置了perturb_type,则将获取到的chunk初始化为perturb_type ^ 0xff。
九、检查smallbins
- 如果malloc的大小属于fastbin的大小范围,_int_malloc函数接着判断malloc的大小是否属于smallbins的大小范围内,如果属于就去smallbins中查找是否有可用的freechunk可用。
①判断大小范围
- 判断malloc的大小是否属于smallbins范围内。
②寻找可用freechunk
- 根据malloc的大小,查找相对应大小的smallbin链是否有可用的freechunk。如果有获取。
- last宏:因为smallbin是双向链表结构,采取FIFO(先进先出)存取chunk,所以使用last返回双向链表中的最先free进去的节点。if中判断如果双链表中的最后一个不是头,那么说明链表不为空。
③系统检测
- 取走对应的freechunk之前,还需要进行系统检测,检测见文章:https://blog.csdn.net/qq_41453285/article/details/97753705。
④set_inuse_bit_at_offset
- 如果当前的chunk被malloc所使用之后,那么当前chunk的后面2个chunk的prev_inuse位都要设置为1,详情见文章:https://blog.csdn.net/qq_41453285/article/details/99053867。
十、malloc_consolidate
- 如果所申请的大小既不属于fastbins、也不属于smallbins,那么此时就要执行到malloc_consolidate了。
- malloc_consolidate只是将碎片进行整理,还没进行freechunk的切割使用,切割使用是在下面的大for循环中。
- malloc_consolidate详情见文章:https://blog.csdn.net/qq_41453285/article/details/97627411。
十一、for大循环
- 进过了上面的代码执行,说明在fastbins、smallbins中没有找到可用的freechunk可以使用,并且还触发了malloc_consolidate。
- 那么之后就进入for大循环了,for循环会做以下事情:
- 第一步:尝试从unsortedbin中分配用户所需的内存,并进行consolidate(如果成功,可能会产生last remainder)。
- 第二步:如果第一步没有满足需求。尝试从largebins中分配用户所需的内存。
- 第三步:如果第二步没有满足需求。尝试寻找更大的chunk来使用(与第一步一样,也会产生last remainder)。
- 第四步:如果第三步没有满足需求。尝试从top chunk中分配用户所需的内存。
①unsortedbin的while循环
- for循环的第一步是对unsortedbin进行操作,这个while会一直进行consolidate对chunk进行整理,边整理变查看是否有可以满足需求的chunk可以给malloc使用了,如果有了就return退出了,如果没有,此while循环10000次就退出了(见下面的⑥)。
②对于unsortedbin的检测
- 内存损坏机制:检查chunk是否对齐(64位16字节对齐,32位8字节对齐)。
③重新申请smallchunk
- 如果申请的chunk大小为smallbin的大小范围,并且在进入for循环之前在所有的smallbins中没有寻找可用的freechunk。那么进入for循环之后又会再一次申请smallchunk。
- 申请的过程:如果unsortedbin中有last remainder,并且该last remainder可以切割一块给smallchunk可使用,那么就切割last remainder给malloc的smallchunk使用。
- set_head、set_foot:如果成功切割某个last remainder给malloc使用,由于last remainder被改变了,所以需要使用者两个宏定义重定设置last remainder的标志位。
④取走unsortedbin中对应的freechunk
- 如果unsortedbin中有一个freechunk跟我们的malloc的请求大小刚好一样,那么直接取走这个freechunk。
- 因为unsortedbin是双链表结构,所以取走freechunk的操作遵循双链表的方法。
⑤consolidate
- 从unsortedbin中取合适的freechunk给malloc使用之后,接下来就是consolidate了,就是将unsortedbin中的freechunk移动到smallbins或largebins中了。
- 如果某一个freechunk属于smallbin的大小范围,就执行if放入smallbins中。否则该freechunk就属于largechunk,则执行else放入largebins中。
⑥MAX_ITERS
- 上面几个步骤是对unsortedbin的一直整理和切割获取。
- 经历了10000此之后,如果还没有找到合适的chunk给malloc使用,那么就不对unsortedbin进行操作了。但是经过这10000之后,unsortedbin中相应的bin都已经放入到对应的bin链中了(smallbins/largebins)。
⑦largebin的判断获取
- 经过前面一系列的执行,如果fastbins、smallbins、unsortedbin中的chunk都不符合malloc的要求,接下来就是判断malloc的chunk是否为largebin,并在largebins中找chunk来使用了。
- 如果largebins中有满足要求的freechunk给malloc使用,那么就执行里面的if语句,切割largebin中的chunk给malloc使用。
- largebin的MINISIZE操作:与unsortedbin不同,如果在unsortedbin中切割freechunk产生last remainder,如果last remainder大小小于MINISIZE,那么last remainder还会留在unsortedbin上。但是largebin不同,如果对largechunk切割产生last remainder,如果last remainder大小小于MINISIZE,那么剩余的last remainder就也给malloc使用了。(例如malloc需要0x100的chunk,某一个largehunk为0x110,那么切割之后last remainder为0x10,由于0x10<MINISIZE,那么这0x10也一起给malloc了,那么malloc最后实际上是申请了0x110的堆空间)。所以if会预先判断剩余的大小是否会小于MINISIZE。
- 之后就开始切割largechunk给malloc使用,并将切割last remainder放入到unsortedbin中了。同时设置一些标志位,这些操作在else中完成。
⑧寻找更大的chunk
- glibc设计ptmalloc的主要目的是为了减少程序与内核的交互,提高效率。同时为了减少“野内存”的出现太多,所以当所有的bins链都没有合适的chunk可使用之后,并且再考虑使用topchunk之前,会再次去寻找一个较大的chunk来使用(寻找比当前的bin更大的fastbin、smallbin、largebin来使用)。
- 例如,在0x70的fastbin中有一个freeechunk,malloc一个0x60的chunk,最终程序会选择这个0x70的freechunk来使用,而不去找topchunk了。
- 与largebin一样,如果更大的chunk被切割使用之后的last remainder大小小于MINISIZE,那么剩下的last remainder也给malloc使用了。同时也会更新一些标志位。
⑨使用topchunk:
- 如果上面的所有都不能使用,那么就要使用topchunk了。
⑩再次调用malloc_consolidate
- 当topchunk也不够使用的时候,此时不会直接去调用sysmalloc申请内存。而是在else if里面再次调用mslloc_consolidate对fastbins中的chunk进行整理,然后进入下一次循环,在后面的循环中再次判断是否有可用的chunk可以使用,如果还没有,最后就要执行sysmalloc函数进行申请内存了。
十二、__lib_free函数简介
- 在应用层调用free函数,在glibc中其实调用的就是__lib_free函数。
- 与_lib_malloc函数一样,_lib_free函数会调用_int_free函数,_int_free函数此时释放堆内存的核心函数。
函数的主要内容包括
- ①检查是否有__free_hook。
- ②如果堆块为NULL,则什么都不做。
- ③如果是mmap的堆块,调用munmap进行释放。
- ④最后调用_int_free函数释放堆块。
free函数的执行内容
- ①判断要释放的chunk是否在fastbin的范围内,如果在就放入fastbin中;否则就放入unsortedbin中。
- ②当放入unsortedbin中时,要检查unlink(前向合并、后向合并)。
十三、__free_hook
- 函数介绍:与__malloc_hook一样,如果用户自动了释放函数,则调用该函数,并且直接执行返回了。
- __free_hook漏洞:如果将__free_hook变为一个system的地址,那么就可以执行这个system的地址。
- 从下面的源码可以看到,先读取__free_hook全局,然后判断是否有用户自定义的函数,如果有就执行,不再进行系统的堆释放了(_int_free)。
- 我是小董,V公众点击"笔记白嫖"解锁更多【堆漏洞挖掘】资料内容。