好友
阅读权限 35
听众
最后登录 1970-1-1
本帖最后由 peiwithhao 于 2022-9-30 14:42 编辑
Largebin Attack
今天来整理以下我所刚学完的largebin attack的理解,以及堆分配过程的基础认识。首先我们在学习largebin attack 之前,先来回顾以下堆分配以及释放的过程。
bins 结构
我们先来看看main_arena中,各个bin的分布情况
不得不说这图画的真好,上面这布局是基于64位的,32位的布局也类似,我们就拿他来类比着看,
首先可以看到fast bin是共有7个链条,每条链上的堆大小相等,分别对应0x20、0x30、、直到0x80。其中需要注意的是fastbin的inuse位始终为1,所以他不会被合并,FILO 然后就是唯一的top chunk,这个topchunk就是再我们找不到对应可以分配的chunk时将会从topchunk切割, 之后我们可以看到有last_remainder 和unsortedbin fd 以及 unsortedbin bk ,这三项可以拿在一起讲。在用户malloc请求分配内存之后,ptmalloc找到的chunk与目标申请的大小不一致时,会进行切割,剩余部分称为last remainder chunk,由 last_remainder 管理,存储在unsortedbin中。而这个unsortedbin在所有bin中就一条链,而且这个链条是可以链接不同大小块的,这里由于unsorted bin是双链条,所有main_arena上面保存着fd与bk指针,在unsorted bin只有一个块的时候,他这个fd与bk均指向那个唯一的unsoted bin中的额chunk,FIFO 再然后就是small bins,其所属范围为0x20 ~ 0x3f0,且在结构体中军保存了0x20 fd,0x20 bk具体原因可以参考unsortedbin,且上述方差为0x10,至于这里为什么与fastbin的大小有重合我们等会儿讲,FIFO
下标 SIZE_SZ = 4(32位) SIZE_SZ = 8(64位) 2 0x10 0x20 3 0x18 0x30 4 0x20 0x40 5 0x28 0x50 x 0x8*x 0x10*x 63 0x1f8 0x3f0
smallbins之后就是largebins,也就是今天的知识点,首先他与其他bins相比都十分特殊,所以我们接下来再来细讲,这里我们先概述以下,largebin的范围从0x400~0x80000,其每个链条上的chunk大小可以不尽相同 组 数量 公差 1 0x20 64B 2 0x10 512B 3 0x08 4096B 4 0x04 32768B 5 0x02 262144B 6 0x01 不限制
好了在结构体上的知识就说到这里,现在进入正题,开始回忆堆分配以及释放
堆分配过程
这里由于我们的重点在于bins,所以对于一些具体函数的实现就不过多赘述,这里只讨论bins哈
(注意这里我们讨论的是不包含tcache的情况)
首先若我们申请的堆大小位于fastbin的范围内,那自然我们只需从中取得即可,若不属于其范围或fastbin为空,紧接着向下执行; 若发现其处于smallbin中,与上述一样,若不属于其范围或smallbins为空,接着向下执行; 当 fast bin、small bin 中的 chunk 都不能满足用户请求 chunk 大小时,就会考虑是不是 large bin。但是,其实在 large bin 中并没有直接去扫描对应 bin 中的 chunk,而是先利用 malloc_consolIDA te(参见 malloc_state 相关函数) 函数处理 fast bin 中的 chunk,将有可能能够合并的 chunk 先进行合并后放到 unsorted bin 中,不能够合并的就直接放到 unsorted bin 中,然后再在下面的大循环中进行相应的处理。 为什么不直接从相应的 bin 中取出 large chunk 呢?这是 ptmalloc 的机制,它会在分配 large chunk 之前对堆中碎片 chunk 进行合并,以便减少堆中的碎片 ; 接下来执行大循环遍历 unsortedbin,若程序运行到此,则说明fast以及small 中没有相匹配的堆块,此时我们进行如下操作:首先对unsortedbin进行遍历,从链尾遍历到链表,若其大小符合所需堆块,则进行分配,否则放入相应的bin中,此时注意若unsortedbin未分配成功,我们会从fastbin中取出所有堆块尝试合并,未合并成功则会放入smallbin,遍历完后尝试用largebin进行分配; 如果请求的 chunk 在 large chunk 范围内,就在对应的 bin 中从小到大进行扫描,找到第一个合适的,若仍旧未找到合适大小的bin,则进行下一步; 如果走到了这里,那说明对于用户所需的 chunk,不能直接从其对应的合适的 bin 中获取 chunk,所以我们需要来查找比当前 bin 更大的 fast bin , small bin 或者 large bin然后进行切割,其中的剩余部分将插入到usortedbin当中,如果这招也不行的话,那只能进行最后一步; 从top chunk进行切割,注意这里切割的剩余部分不会放入unsortedbin,如果这一步也不行、、、再再再进行最后一步,那就是使用sysmalloc进行系统分配(哈哈哈这里就不多说了,感兴趣的可以去ctf-wiki上面看看,这里我们主要是了解堆块与bins间的分配过程)
堆释放过程
相比于分配,释放过程就简单多了,也就是说如果你释放的堆块处于fastbin范围,则将其正常插入,除此之外会将其放入unsorted bin当中。这里要注意unlink的时机(毛遂自荐以下,这里可以看看我之前写的unlink文章哈,我觉得还行 ,
2014_hitcon_stkof学习(以及unlink的个人理解)
https://www.52pojie.cn/thread-1681720-1-1.html
(出处: 吾爱破解 论坛)
),也就是除了释放的是fastbin范围的堆块,其他的相邻堆块都会进行unlink合并,这也是防止内存中过多小碎块的存在,如果你所释放的堆块与topchunk相邻,则会并入topchunk中。
以上真的建议大家在自己的脑海里面仔细审查一遍,这个理清楚了做题才不会模模糊糊。
接下来我们来了解large bin的知识
largebin基础知识
首先咱们已经知道了largebin与其他的bin十分不同,什么?你已经知道了?(不就是一条链上的大小不一样么,还有什么特别的么),那当然也有,他比较特别的就是多了两个指针,fd_nextsize , bk_nextsize.这里我先贴出来ctfwiki上的解释
[C] 纯文本查看 复制代码
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
而以上的注释部分也说得很清楚,这个fd_nextsize与bk.nextsize只有在largebin中才会享有,他的具体含义我这里来语言描述以下:
fd_nextsize:指向前一个与当前chunk_size不小同的块(注意是同意链条上)
bk_nextsize:指向后一个与当前chunk_size大小不同的块(同上)
其中还有几个注意点那就是在相同大小的chunk中,只有首chunk的fd_nextsize和bk_nextsize才有具体值,之后的全按0处理,还有就是largebin的链条分配是按从大到小排序的,如果size相同则按free时间前后来排序,这里有个漏洞点那就是在放入largebin过程中若释放的堆块比当前链条尾的短,则程序会直接放入链尾而不进行过多的检查(这里有点模糊,以后我来修改)。
说到这里相信也有很多刚接触的师傅不太明白,因为这文字实在是过于抽象,所以我这里来画个图给大家看看
这张图大致就可以讲出largebin中的链接关系了,请大伙务必仔细看,千万别嫌麻烦,这里弄懂了其他的就自然通了,这里也可以看出来fd,bk指针与普通堆块是一样链接的,所以为了让大家更方便观看,我这里就再去除掉fd、bk指针
从这张图我来简单带大家看看,这里不同大小堆块中的首块我都运用了不同颜色来进行标识,可以看到只有颜色标记的堆块采用到了fd_nextsize与bk_nextsize,并且这两个指针也分别指向了下一个带颜色的堆块,所以说其实我们在一个largebin链条中除了fd、bk链条之外还有额外的链条,这样也是为了方便我们寻找堆块,大家可以稍微思考思考,如果没有fd_nextsize与bk_nextsize的话,由于这条链上块的大小并不都是相同,难道我们要依次一个一个遍历搜寻堆块嘛,那就太笨了一点,所以说添加这两个链接可以加快我们搜寻不同堆块的速度,并且始终记住我们这里是双循环链表,相信这样大家看这个图就会理解的更加顺畅。
how2heap中的large_bin_attack练习
由于没找到合适的题目,但是看有的师说说 2e4zu这题好像挺好,我先mark以下以后写, 这里我使用的本机2.34的环境,因为我考虑着这个实验用不到tcache就没管。 先给出主函数给大家看看
[C] 纯文本查看 复制代码
int main(){
/*Disable IO buffering to prevent stream from interfering with heap*/
setvbuf(stdin,NULL,_IONBF,0);
setvbuf(stdout,NULL,_IONBF,0);
setvbuf(stderr,NULL,_IONBF,0);
size_t target = 0;
printf("Here is the target we want to overwrite (%p) : %lu\n\n",&target,target);
size_t *p1 = malloc(0x428);
printf("First, we allocate a large chunk [p1] (%p)\n",p1-2);
size_t *g1 = malloc(0x18);
printf("And another chunk to prevent consolidate\n");
printf("\n");
size_t *p2 = malloc(0x418);
size_t *g2 = malloc(0x18);
free(p1);
size_t *g3 = malloc(0x438);
free(p2);
p1[3] = (size_t)((&target)-4);
size_t *g4 = malloc(0x438);
return 0;
}
程序不复杂,这里删除了多余的注释部分,这个程序是先分配了一个chunk_p1(0x430),然后申请一个0x20的块来进行防合并,之后以同样的方法分配了一个chunk_p2(0x420),这里我们就先不说完过程,免得大家看着一股脑赛这么多东西过来不好理解。程序执行到这里我们首先来看看堆栈的内存布局
然后我们继续执行程序,由于我们已经掌握堆的分配机制(确信)(如果忘记的话建议去上面的部分再多思考思考)所以我们此时首先free掉p1,此时由于其不在fastbin范围内,所以放入unsorted bin,从调试中也可以证实
然后我们再分配一个0x440,为什么要如此分配呢,那是因为我们想要进行依次大循环遍历,借此将0x430也就是p1这块放入largebin中
这里看到我们成功放入了,然后就是free掉p2,此时放入到unsorted bin中,之后p1的bk_nextsize位修改为target-0x20,这样就使得程序以为我们的头链表还不是p1,而是我们的target了。
此时我们再重操旧业,将chunk_p2也放入到largebin当中且只检查了链尾的p1并发现比他小,所以就将p2直接变为链尾,所以此时我们的p2的fd_nextsize指针就会指向我们的target(与unsorted bin很像有没有)
实验结束!!!!!!
怎么感觉十分的水,不过前期的分配以及largebin讲解感觉还行,今天的实验变成配菜了感觉,之后找到了合适的题目会第一时间分享给大家。
免费评分
查看全部评分
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。