C -Ubuntu 16.04 -Malloc实施.下一个块的指针在哪里



我试图了解GLIBC中的malloc实现方式。根据malloc的源代码(glibc 2.23中的malloc.c),自由记忆块具有以下结构。

    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个字节的6个内存块。每个块都充满了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

在此输出中,我们可以看到释放的块和向上释放的块的后面指针(请参阅输出的右侧注释)。但是,前向指针和以前的大小在哪里?

从security.stackexchange

交叉播放

取决于要释放的块的大小,块以不同类型的 bins (链接列表)举行:

  • 未分类的垃圾箱
  • 小垃圾箱
  • 大垃圾箱

如果您有兴趣知道如何维护这些垃圾箱,建议您查找源代码。但是,在所有这些垃圾箱中常见的是,列表是双关联的。因此,您的假设是正确的,您应该在释放块中同时找到A forward 和a 向后指针>字段)

但是,还有另一种类型的特殊垃圾箱称为A fastbin 。这些尺寸很小(通常在16到80个字节之间,但版本可能会略有不同)。与您的常规垃圾箱不同,这些是单一连接的。根据它们的尺寸(每个包含相同尺寸的块),它们将其保存在适当的快速曲琴中。可以按LIFO订单添加和删除块,而不必穿越列表,从而加快了性能。同样与常规块不同,相邻的fastbin-chunks也不合并(当然会导致碎片化,但可以更快地自由)。这也意味着您也不需要以前的块的大小。

您程序中的块也可能是这些快速宾斯特之一的一部分。因此,要查看您的期望,请尝试分配和释放更大尺寸的内存。

相关内容

  • 没有找到相关文章

最新更新