chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk,in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk,in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
通常我们应该能够在gnu调试器(gdb)中看到这个结构.所以我写了以下程序.该程序分配6个大小为64字节的内存块.每个块都填充了memset,因此我们可以在以后轻松查看gdb中的相应块.因为块1,3和6被释放,所以它们应该具有上述结构.由于中间存在已分配的块,因此释放的块不能合并,因此它们通过每个块中的指针组织在双重链表中.
#include <stdio.h> #include <stdlib.h> #include <string.h> void to_jump(); int main(int argc,char **argv){ char *b1,*b2,*b3,*b4,*b5,*b6; //allocate 6 chunks of memory b1 = malloc(64); b2 = malloc(64); b3 = malloc(64); b4 = malloc(64); b5 = malloc(64); b6 = malloc(64); memset(b1,'B',64); memset(b2,'C',64); memset(b3,'D',64); memset(b5,'E',64); memset(b6,'F',64); //free chunks 1,3 and 6 free(b1); free(b3); free(b6); strcpy(b4,argv[1]); // <-- set breakpoint here //exploit this line free(b4); free(b5); free(b2); } void to_jump(){ printf("Exploited"); }
当我在gdb中启动程序并在strcpy行中设置断点时(b4,argv [1]);我们应该能够看到释放的块被组织在一个双链表中.但是gdb输出如下:
gdb-peda$p b1 $11 = 0x602010 "" gdb-peda$x/62xg 0x602000 0x602000: 0x0000000000000000 0x0000000000000051 0x602010: 0x0000000000000000 0x4242424242424242 | 0x602020: 0x4242424242424242 0x4242424242424242 | b1 (freed) 0x602030: 0x4242424242424242 0x4242424242424242 | 0x602040: 0x4242424242424242 0x4242424242424242 | 0x602050: 0x0000000000000000 0x0000000000000051 0x602060: 0x4343434343434343 0x4343434343434343 | 0x602070: 0x4343434343434343 0x4343434343434343 | b2 (allocated) 0x602080: 0x4343434343434343 0x4343434343434343 | 0x602090: 0x4343434343434343 0x4343434343434343 | 0x6020a0: 0x0000000000000000 0x0000000000000051 0x6020b0: 0x0000000000602000 0x4444444444444444 | 0x602000 is pointing to b1 (previous freed block) 0x6020c0: 0x4444444444444444 0x4444444444444444 | b3 (freed) 0x6020d0: 0x4444444444444444 0x4444444444444444 | 0x6020e0: 0x4444444444444444 0x4444444444444444 | 0x6020f0: 0x0000000000000000 0x0000000000000051 0x602100: 0x0000000000000000 0x0000000000000000 | 0x602110: 0x0000000000000000 0x0000000000000000 | b4 (will be filled trough strcpy(b4,argv[1]); 0x602120: 0x0000000000000000 0x0000000000000000 | 0x602130: 0x0000000000000000 0x0000000000000000 | 0x602140: 0x0000000000000000 0x0000000000000051 0x602150: 0x4545454545454545 0x4545454545454545 | 0x602160: 0x4545454545454545 0x4545454545454545 | b5 (allocated) 0x602170: 0x4545454545454545 0x4545454545454545 | 0x602180: 0x4545454545454545 0x4545454545454545 | 0x602190: 0x0000000000000000 0x0000000000000051 0x6021a0: 0x00000000006020a0 0x4646464646464646 | 0x6020a0 is pointing to b3 (previous freed block) 0x6021b0: 0x4646464646464646 0x4646464646464646 | b6 (freed) 0x6021c0: 0x4646464646464646 0x4646464646464646 | 0x6021d0: 0x4646464646464646 0x4646464646464646 | 0x6021e0: 0x0000000000000000 0x0000000000020e21
在此输出中,我们可以看到释放的块和指向前一个释放块的后向指针(请参阅输出右侧的注释).但前锋指针和前一块的大小在哪里?
根据要释放的块的大小,块被保存在不同类型的块(链表)中:
>未分类的垃圾箱
>小垃圾箱
>大箱子
如果您有兴趣了解这些垃圾箱的维护方式,建议您查找源代码.但是所有这些箱子中常见的一点是这些列表是双重联系的.所以你的假设是正确的,你应该在释放的块中找到前向和后向指针(也可能是之前的大小字段)
然而,还有另一种称为fastbin的特殊箱.非常小的块(通常在16到80个字节之间,但在不同版本中可能略有不同)保存在这些快速箱中.与常规垃圾箱不同,它们是单独连接的.它们根据它们的大小保存在适当的fastbin中(每个bin包含相同大小的块).可以在LIFO订单中添加和删除块,而不是必须遍历列表,从而加快性能.与常规块不同,相邻的fastbin-chunk不会被合并(这当然会导致碎片化,但会更快地释放).这也意味着您不需要前一个块的大小.
程序中的块也可能是其中一个fastbins的一部分.因此,要查看您的期望,请尝试分配和释放更大的内存.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。