Skip to main content

重写malloc和free

背景:作为即将步入大二的小老登🤓,CTF出题的压力来到了我们的身上,在Vidar-Team有一个hgame-mini比赛,由于一些改革,需要在hgame-mini涉及一些堆题,但是有些人认为直接上堆题过于困难,于是决定做一些伪堆题,通过重写malloc和free,减轻新生的压力,虽然我水平不太够,但是给新生出点题还是没问题的,于是我就决定写一份自己的malloc(这其实还是有些私心的,重写libc本来也是我希望能完成的一个任务),从malloc的读者到malloc的设计者,感慨万千

说实在的,这还是我第一次真正脱离讲义,脱离框架代码(实际上也有glibc的框架参考),所以我需要自己来分步,分解任务

编译到自己的malloc和free

如何在程序编译时使用自己写的malloc,这是遇到的第一个问题,我一开始想的是用LD_PRELOAD环境变量,但是在我的macos上始终无法做到,于是采用在编译时加入选项-fno-builtin,成功

编写最简单的malloc

首先必须使malloc可以跑起来,不实现free。

那就要确定我的malloc的底层需要哪个syscall

打算使用sbrk,因为感觉比较简单,但是warning: 'sbrk' is deprecated

无所谓了

功能描述如下:第一次调用时mmap一块巨大的内存,这块内存作为一个cache(避免之后频繁的向操作系统请求),这块内存在ptmalloc2中称之为topchunk,之后的所有内存请求均从topchunk切割,所有的free均尝试合并找附近的合并,没有合并成功的内存块放入唯一(暂时)的一个bin(unsorted bin),之后的malloc通过首先在unsorted bin中寻找若找到合适大小的则分配,没有合适大小的则从topchunk切割,free同理

基础设施建设

基础设施包括一些宏的定义,testbench的编写,一些struct的编写

sysmalloc

这个函数需要在做到第一次malloc的时候提供一个巨大的topchunk以及一个所需大小的chunk返回给用户,以及在之后topchunk空间不够时增大topchunk的大小并提供返回一个所需大小的chunk,暂时没有第二个的需求,所以搁置先

malloc/free

malloc首先在unsorted bin中搜索,找到一个申请的大的chunk,如果大的很多就切割,如果大的不多就直接给,这个大的多不多取决于切割后有没有大于minisize(0x20),这种方式就是效率特别慢,但是肯定能work,如果无法在unsorted bin中找到合适的,就从topchunk切割

写之前复习了一下这个bin的结构…..哎,glibc,你真的不是人能看的

就这个bin_at几乎让我困惑了一整天,就问谁能秒看懂这个,从bins中取地址,再取这个地址的地址,现在这个地址在arena中,再将这个地址减两个size_t,谁能理解这样做的目的??

后来吃个饭终于理解了,如果传入i为1,确实获得是bin[0]-2*size_t的地址,此时这个地址是什么并不重要(实际上是arena中top指针的地址),最主要的是通过这个地址来获得他的fd,也就是说他将top指针的位置当成了一个chunk(但是这并不是一个真正的chunk),然后通过->fd的方式来获得相应的指针

意义何在?直接bins[0]不也可以获得地址嘛,太过诡异,但是他似乎返回的是一个虚拟的chunk

如图所示,最里面的chunk的fd和最外面的bk都是指向这个虚拟chunk的头,从而其实就像chunk之间形成了一个不循环链表,但是加上了这个虚拟chunk之后就形成了一个循环链表,循环链表理论没有起始点,但是按照不循环链表的角度来看的话,循环链表其实起始点是这个虚拟chunk(即bin_at获得的chunk),虚拟chunk的fd指向第一个chunk(最外面的chunk),bk指向最里面的chunk

妙啊妙啊,哎,还是感觉很诡异(主要是没见过这种操作)–以0空间开销的方式获得一个循环链表的性能,太无敌了

之后写完之后的测试是最麻烦的,我无法对我的malloc适配pwndbg(或者说很难),因为pwndbg的调试命令都是以glibc malloc作为实现。(但是或许我可以适配一下,让pwndbg能检测出来?毕竟pwndbg也是通过一些二进制文件中的符号来进行堆探测的?)

在测试malloc的时候,发现printf依赖malloc,导致调试有点困难,所以或许需要给我的malloc和free改一个名字,但是其实我不希望printf改变我的堆状态

之后发现使用gdb(with gdb script)其实可以很轻松的进行测试

甚至还可以在gdb脚本中定义函数,做更多的事情,这样其实可以获得pwndbg 50%的威能,后发现在gdbinit中可以对pwndbg进行一些基本配置,比如让某些区域不显示(比如reg,stack等区域对我就没什么用),pwndbg真好☺️☺️

其实差不多完结了,丢一下代码吧

myLibc/src/malloc.c at master · yizishun/myLibc (github.com)