2016-12-18 4 views
5

glibc에서 malloc 구현이 어떻게 작동하는지 이해하려고합니다. malloc의 소스 코드 (glibc 2.23의 malloc.c)에 따르면 사용 가능한 메모리 청크는 다음과 같은 구조를 가지고 있습니다.우분투 16.04 - malloc 구현. 다음 청크에 대한 포인터는 어디에 있습니까?

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)에서이 구조를 볼 수 있어야합니다. 그래서 나는 다음과 같은 프로그램을 썼다. 이 프로그램은 64 바이트의 크기로 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 

(출력에서 오른쪽에있는 주석을 참조하십시오). 하지만 앞으로 포인터와 이전 청크의 크기는 어디입니까?

답변

3

크로스 게시 덩어리가 의 다른 유형 (연결리스트)에서 개최되며, 해제 할 청크의 크기에 따라 security.stackexchange

에서 :

  • 정렬되지 않은 빈
  • 작은 쓰레기통
  • 대형 쓰레기통

빈이 유지되는 방법을 알고 싶다면 소스 코드를 살펴 보는 것이 좋습니다. 그러나이 모든 쓰레기통에 공통적 인 것은 목록이 이중 연결이라는 것입니다. 그래서 당신은 당신이 발견해야 당신의 가정에서 정확했다 모두 앞으로 뒤로 포인터 해제 된 청크 (그리고 아마도 이전 크기 필드) 그러나

, 특별한 다른 유형이있다 빈은 패스트 빈으로 알려져 있습니다. 매우 작은 크기의 청크 (일반적으로 16 ~ 80 바이트이지만 버전에 따라 약간 다를 수 있음)는이 패스트 빈에 보관됩니다. 일반 상자와 달리 은 단일 연결입니다. 그들은 크기에 따라 적절한 fastbin에 보관됩니다 (각 bin에는 동일한 크기의 청크가 들어 있습니다). 목록을 트래버스하지 않고 청크를 LIFO 순서로 추가 및 제거하여 성능을 향상시킬 수 있습니다. 또한 일반 덩어리와 달리 인접한 패스트 빈 덩어리는 통합되지 않습니다 (물론 조각화로 이어 지지만 더 빨리 자유롭게 만듭니다). 이것은 이전 청크의 크기도 필요 없다는 것을 의미합니다.

프로그램의 청크도이 fastbins 중 하나의 일부일 것입니다. 그러므로 당신이 기대하는 것을보기 위해서 더 큰 크기의 메모리를 할당하고 해제하십시오.