堆漏洞挖掘——__lib_malloc函数、_int_malloc函数、__lib_free函数源码详解

一、_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),如果没有就直接退出。

③系统检测

④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中判断如果双链表中的最后一个不是头,那么说明链表不为空。

③系统检测

④set_inuse_bit_at_offset

十、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公众点击"笔记白嫖"解锁更多【堆漏洞挖掘】资料内容。

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是将yuv420sp格式的图像转换为jpeg格式的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <jpeglib.h> void yuv420sp_to_jpg(unsigned char *yuv, int width, int height, char *filename) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; JSAMPROW row_pointer[1]; int row_stride; FILE *outfile = fopen(filename, "wb"); if (outfile == NULL) { fprintf(stderr, "Cannot open output file %s\n", filename); return; } cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, outfile); cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_YCbCr; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, 90, TRUE); jpeg_start_compress(&cinfo, TRUE); row_stride = width * 3; JSAMPLE *rgb = (JSAMPLE *)malloc(row_stride); while (cinfo.next_scanline < cinfo.image_height) { int y, u, v; unsigned char *py, *pu, *pv; py = yuv + cinfo.next_scanline * width; pu = yuv + width * height + (cinfo.next_scanline >> 1) * width; pv = yuv + width * height * 5 / 4 + (cinfo.next_scanline >> 1) * width; for (int i = 0, j = 0; i < width; i += 2, j += 6) { y = py[i]; u = pu[j] - 128; v = pv[j] - 128; rgb[i * 3] = (unsigned char)(y + 1.4075 * v); rgb[i * 3 + 1] = (unsigned char)(y - 0.3455 * u - 0.7169 * v); rgb[i * 3 + 2] = (unsigned char)(y + 1.779 * u); y = py[i + 1]; rgb[(i + 1) * 3] = (unsigned char)(y + 1.4075 * v); rgb[(i + 1) * 3 + 1] = (unsigned char)(y - 0.3455 * u - 0.7169 * v); rgb[(i + 1) * 3 + 2] = (unsigned char)(y + 1.779 * u); } row_pointer[0] = rgb; jpeg_write_scanlines(&cinfo, row_pointer, 1); } free(rgb); jpeg_finish_compress(&cinfo); fclose(outfile); jpeg_destroy_compress(&cinfo); } ``` 其中,yuv是yuv420sp格式的图像数据,width和height分别是图像的宽和高,filename是要保存的jpeg文件名。该函数使用libjpeg库将yuv420sp格式的图像数据转换为jpeg格式的图像数据,并保存到指定的文件中。具体的转换过程是将yuv数据转换为rgb数据,然后使用libjpeg库进行压缩编码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值