ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • glibc - ptmalloc (1)
    Heap exploitation/malloc 2020. 3. 1. 16:15

    malloc()은 사용자가 프로그램 실행시 동적으로 메모리를 할당받을 수 있게 해주는 함수이다. 이는 내부적으로 sbrk()를 사용해 커널로부터 힙 영역을 할당받는데, sbrk()는 커널모드로 들어가기 때문에 단순히 요청한 크기만큼 sbrk()를 해주면 큰 부하가 걸린다. 이를 해결하기 위해 glibc는 ptmalloc을 사용하여 힙 영역을 관리한다. 

     

    Arena


    arena란 힙 영역을 관리하는 구조체로, ptmalloc에서는 쓰레드별로 관리하여 main arena, thread arena가 존재한다. 단일 쓰레딩 환경에서는 main_arena만 존재한다. arena의 구조체는 아래와 같다. 

     

    struct malloc_state
    {
      /* Serialize access.  */
      mutex_t mutex;
     
      /* Flags (formerly in max_fast).  */
      int flags;
     
      /* Fastbins */
      mfastbinptr fastbinsY[NFASTBINS];
     
      /* Base of the topmost chunk -- not otherwise kept in a bin */
      mchunkptr top;
     
      /* The remainder from the most recent split of a small request */
      mchunkptr last_remainder;
     
      /* Normal bins packed as described above */
      mchunkptr bins[NBINS * 2 - 2];
     
      /* Bitmap of bins */
      unsigned int binmap[BINMAPSIZE];
     
      /* Linked list */
      struct malloc_state *next;
     
      /* Linked list for free arenas.  Access to this field is serialized
         by free_list_lock in arena.c.  */
      struct malloc_state *next_free;
     
      /* Number of threads attached to this arena.  0 if the arena is on
         the free list.  Access to this field is serialized by
         free_list_lock in arena.c.  */
      INTERNAL_SIZE_T attached_threads;
     
      /* Memory allocated from the system in this arena.  */
      INTERNAL_SIZE_T system_mem;
      INTERNAL_SIZE_T max_system_mem;
    };

    힙을 관리하기 위한 여러 정보들을 담고 있으며 각 arena끼리 linked-list로 관리된다. 가장 먼저 main_arena는 아래와 같이 libc 내부에서 선언된 변수이다.

    static struct malloc_state main_arena =
    {
      .mutex = _LIBC_LOCK_INITIALIZER,
      .next = &main_arena,
      .attached_threads = 1
    };

    main_arena

    heap이 생성되지 않았을 때의 main_arena 값이다. 

     

    Chunk


    Chunk란 힙을 관리하는 하나의 단위이며 힙은 chunk들로 구성된다. chunk에는 Allocate chunk, Free chunk, Top chunk가 존재한다.

     

    Allocate Chunk


    Allocate Chunk는 현재 할당되어 사용하고 있는 청크를 말하며 위와 같은 구조로 되어있다. Data의 크기는 요청한 사이즈에 따라 달라질 수 있지만 prev_size, size의 크기는 8바이트씩으로 고정되어있다.

     

    • prev_size: 이전 청크의 사이즈를 나타낸다. 이전 청크가 free 되어 현재 청크의 prev_inuse 비트가 해제되었을 때만 사용된다.
    • size: 현재 청크의 사이즈를 나타낸다. 청크의 사이즈는 0x10 바이트 단위로 커지므로 하위 3비트는 사용되지 않는다는것을 이용해 하위 3비트는 플래그로 사용된다. 그중 최하위 1비트는 prev_inuse이다.
    • prev_inuse(p): 사이즈 8바이트 내의 최하위 1비트이며 이 비트가 세트되어있으면 이전 청크가 현재 사용중이라는 것을 나타낸다.

    이것을 실제 gdb에서 확인해보면 아래와 같다.

    • prev_size : 0
    • size: 0x60 | 1 (p)

    이 청크의 실제 사이즈는 0x60(헤더 포함)이며 0x61로 표현되는 이유는 이전 청크가 사용중이라는 의미이다. 

     

    Free Chunk


    Free Chunk는 free()를 이용해 해제된 청크를 말하며 Allocated Chunk에서 fd, bk를 더 사용한다. fd, bk가 쓰이는 위치는 원래 Data 부분인데, 해제된 청크의 데이터는 의미가 없다고 여겨 그 위에 덮어 쓰인다. 

     

    실제 gdb에서 확인하면 아래와 같다.

    fd, bk에 어떠한 값이 쓰여졌다. 

     

    Top Chunk


    Top Chunk는 말 그대로 모든 청크 위에 있는 청크를 말한다. 앞서 malloc이 sbrk를 이용해 커널로부터 힙영역을 할당받는다고 했는데, 초기에 힙 영역을 0x21000 바이트만큼 받아온다. 이때 이를 통채로 top chunk로 보고 이 top chunk에서 일정 부분을 잘라서 Allocate Chunk를 만들어 할당해준다.

     

    사진을 보면 파란색 힙 영역이 생성된 것을 확인할 수 있으며 크기가 0x21000인 것 또한 확인할 수 있다.

     

    위의 프로그램에서 malloc(0x10)을 실행한 상태인데, malloc에서 처음 sbrk로 0x21000만큼 할당을 받고 다시 0x20(heap header 0x10 byte. + data 0x10 byte)를 잘라서 반환한다. 이렇게 되면 top chunk는 (0x21000 - 0x20) | 1 (prev_inuse) 가 되어 0x20fe1이 될 것이다.

    현재 0x602020에 위치한 청크가 top chunk이다. 이는 main_arena에서도 확인할 수 있다.

     

    main_arena.top이 현재 top chunk의 위치를 가리키고 있다.

    댓글

Designed by Tistory.