0x0F – Introduction to Heap

Heap ကေတာ့ လံုးဝကို မေရးဖူးတာျဖစ္ပါတယ္။ ဒီေတာ့ ဒီအခန္းမွာ ေသခ်ာေလး ေလ့လာၾကတာေပါ့ ။

Heap Definition

The heap is pool of memory used for dynamic 
allocations at runtime

exploitation ေတြမလုပ္ခင္ က်ေနာ္႕အေနနဲ႕ သိလာရတာတစ္ခုကေတာ့ နားလည္မွရမယ္ဆိုတာပါပဲ ၊ အဲဒါေလးတစ္ခုသတိထားေစခ်င္တယ္ အလြတ္က်က္လို႕ရတဲ့ဟာမ်ိဳးမဟုတ္ေတာ့ exploitation ကို ေသခ်ာနားလည္ခ်င္ရင္ ဘာအတြက္သံုးတာလဲ ? ဘယ္လိုအလုပ္လုပ္လဲ စသည္ျဖင့္ သိထားဖို႕လိုပါတယ္။ မဟုတ္ရင္ေတာ့ ရြာလည္ေနေတာ့မွာေပါ့။ Video Tutorial ၾကည့္ျပီး က်ေနာ္တို႕ လုပ္လို႕ရေကာင္းရမယ္ ။ နည္းနည္းေလးလွည့္လိုက္ရင္ကိုမရေတာ့ဘူးဆိုရင္ နားမလည္ေသးဘူးဆိုရမွာေပါ့ ။ အဲ့ေတာ့ ဒါေတြကို စျပီးေတြ႕ခဲ့တဲ့ လူေတြဆိုရင္ ဘယ္ေလာက္ေတာင္ေလးစားဖို႕ေကာင္းမလဲ

Pointer ေတြအေၾကာင္းေတာ့သိထားဖို႕လိုပါမယ္။ Pointer အေၾကာင္း နည္းနည္းေရးထားပါတယ္။

ဒီမွာ ဖတ္ေပးပါ ။

Heap ဟာ runtime မွာ သံုးတဲ့ dynamic memory pool တစ္ခုျဖစ္တယ္တဲ့ ။ ဘယ္အခ်ိန္မွာသံုးတာလဲ ?

Objects , big buffers ,structs , persistent စတဲ့ ပိုျပီး size ၾကီးတဲ့ေနရာေတြမွာ သံုးပါတယ္။

Stack လိုမ်ိဳး Compiler ကေန ျဖစ္လာတဲ့ Memory မဟုတ္ပဲ heap က programmer ေရးလို႕ျဖစ္လာတဲ့ Dynamic memory ျဖစ္ပါတယ္။

malloc/calloc/recalloc/free စတဲ့ function ေတြကိုသံုးျပီး heap ကို allocate လုပ္ၾကတယ္။ ဒီလိုဆိုရင္ ပိုျပီးနားလည္ေအာင္လုပ္ၾကမယ္။

Stack Vs Heap

#include <stdio.h>
int main()
{
        int a=1;
        int b=2;

        int *p;
        p=(int*)malloc(sizeof(int));
        *p=10;
        int *p1;
        p1=(int*)malloc(sizeof(int));
        *p1=20;
        printf("Stack \n");
        printf("Address of a = %p\n",&a);
        printf("Address of b = %p\n",&b);
        printf("Heap \n");
        printf("Address of p = %p\n",p);
        printf("Address of p1= %p\n",p1);
}

Stack နဲ႕ Heap ကို ကြဲျပားေစခ်င္လို႕ address ေတြကိုထုတ္ၾကည့္ထားတာျဖစ္ပါတယ္။

ဒါကေတာ့ result

root@exploitdev:~/Heap# ./heap
Stack
Address of a = 0xffffd65c
Address of b = 0xffffd660
Heap
Address of p = 0x804b008
Address of p1= 0x804b018

malloc(sizeof(int)) ဆိုတာဟာ 4 bytes စာ allocate လုပ္မယ္ေျပာေပမဲ့ ဆက္တိုက္မလာဘူးဆိုတာကို result အရက်ေနာ္တို႕ေတြ႕ရတယ္။ ဒီေတာ့ Heap ရဲ႕ တကယ့္ size က 4 bytes မဟုတ္ဘူးေပါ့ ။ တကယ္ကဘယ္ေလာက္လဲ

Heap sizes from MBE course ( github link )

/* compiled with: gcc -o sizes sizes.c */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{

    unsigned int lengths[] = {32, 4, 20, 0, 64, 32, 32, 32, 32, 32};
    unsigned int * ptr[10];
    int i;
   
    /* make arbitrary chunks on the heap */
    for(i = 0; i < 10; i++)
        ptr[i] = malloc(lengths[i]);
   
    /* print distance between chunks, eg size of chunks */
    for(i = 0; i < 9; i++)
        printf("malloc(%2d) is at 0x%08x, %3d bytes to the next pointer\n", 
                lengths[i],
                (unsigned int)ptr[i],
                (ptr[i+1]-ptr[i])*sizeof(unsigned int));
   

   return 0;
}

Run ၾကည့္လိုက္မယ္။

root@exploitdev:~/Heap# ./sizes
malloc(32) is at 0x0804b008, 40 bytes to the next pointer
malloc( 4) is at 0x0804b030, 16 bytes to the next pointer
malloc(20) is at 0x0804b040, 24 bytes to the next pointer
malloc( 0) is at 0x0804b058, 16 bytes to the next pointer
malloc(64) is at 0x0804b068, 72 bytes to the next pointer
malloc(32) is at 0x0804b0b0, 40 bytes to the next pointer
malloc(32) is at 0x0804b0d8, 40 bytes to the next pointer
malloc(32) is at 0x0804b100, 40 bytes to the next pointer
malloc(32) is at 0x0804b128, 40 bytes to the next pointer

malloc( 4) is at 0x0804b030, 16 bytes to the next pointer က က်ေနာ္တို႕နဲ႕ size တူတယ္။ 4 bytes ေပါ့ ။ ေနာက္ထပ္ pointer ကိုေရာက္ဖို႕ 16 bytes ေတာင္ၾကားထဲမွာရွိေနတာကိုေတြ႕တယ္။ 0 ယူတာေတာင္ 16 bytes ျဖစ္ေနတယ္။

MBE course က example နဲ႕ပဲေလ့လာမယ္။ ref

/* compiled with: gcc -o heap_chunks heap_chunks.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define LEN 15

void print_chunk(unsigned int * ptr, unsigned int len)
{
    
    printf("[ prev - 0x%08x ][ size - 0x%08x ][ data buffer (0x%08x) -------> ... ] - from malloc(%d)\n", \
            *(ptr-2),
            *(ptr-1),
            (unsigned int)ptr,
            len);
}

int main()
{
    unsigned int * ptr[LEN];
    unsigned int lengths[] = {0, 4, 8, 16, 24, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384};
    int i;

    /* malloc chunks, and them print their fields */
    printf("mallocing...\n");

    for(i = 0; i < LEN; i++)
        ptr[i] = malloc(lengths[i]);
    
    for(i = 0; i < LEN; i++)
        print_chunk(ptr[i], lengths[i]);
   
    return 0;
}

Result

root@exploitdev:~/Heap# ./heap_chunks
mallocing...
[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b410) -------> ... ] - from malloc(0)
[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b420) -------> ... ] - from malloc(4)
[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b430) -------> ... ] - from malloc(8)
[ prev - 0x00000000 ][ size - 0x00000019 ][ data buffer (0x0804b440) -------> ... ] - from malloc(16)
[ prev - 0x00000000 ][ size - 0x00000021 ][ data buffer (0x0804b458) -------> ... ] - from malloc(24)
[ prev - 0x00000000 ][ size - 0x00000029 ][ data buffer (0x0804b478) -------> ... ] - from malloc(32)
[ prev - 0x00000000 ][ size - 0x00000049 ][ data buffer (0x0804b4a0) -------> ... ] - from malloc(64)
[ prev - 0x00000000 ][ size - 0x00000089 ][ data buffer (0x0804b4e8) -------> ... ] - from malloc(128)
[ prev - 0x00000000 ][ size - 0x00000109 ][ data buffer (0x0804b570) -------> ... ] - from malloc(256)
[ prev - 0x00000000 ][ size - 0x00000209 ][ data buffer (0x0804b678) -------> ... ] - from malloc(512)
[ prev - 0x00000000 ][ size - 0x00000409 ][ data buffer (0x0804b880) -------> ... ] - from malloc(1024)
[ prev - 0x00000000 ][ size - 0x00000809 ][ data buffer (0x0804bc88) -------> ... ] - from malloc(2048)
[ prev - 0x00000000 ][ size - 0x00001009 ][ data buffer (0x0804c490) -------> ... ] - from malloc(4096)
[ prev - 0x00000000 ][ size - 0x00002009 ][ data buffer (0x0804d498) -------> ... ] - from malloc(8192)
[ prev - 0x00000000 ][ size - 0x00004009 ][ data buffer (0x0804f4a0) -------> ... ] - from malloc(16384)

ဘယ္လိုယူလဲဆိုတာကိုေတြ႕နိုင္တယ္။

previous chunk size က 4 bytes , chunk size က 4 bytes , ေနာက္ဆံုးက data buffer က က်ေနာ္တို႕ pointer က point တကယ္လုပ္တဲ့ data ေပါ့ ။ ဒီေတာ့ Stack တုန္းကလို ေဒါင္လိုက္ျပမယ္ဆိုရင္

-------------------------------
previous chunk size
chunk size
data
-------------------------------
previous chunk size
chunk size
data
-------------------------------

ေနာက္တစ္ခုက higher address ကို သြားေနတာေတြ႕ရမယ္။ Stack က lower address ကိုသြားတယ္။ heap ကေတာ့ higher memory address ကိုသြားတယ္ဆိုတာ အေပၚက result ေတြကိုၾကည့္ရင္သိနိုင္ပါတယ္။

previous chunk size + chunk size + data တစ္တြဲကို heap chunk လို႕ေခၚတယ္။

Heap chunk မွာ အေျခေနက ၂ ခုရွိတယ္။ တစ္ခုက in use အဲဒါကအေပၚမွာတုန္းကသံုးခဲ့တဲ့ malloc() နဲ႕သံုးေနတဲ့ အေျခအေနရယ္ ေနာက္တစ္ခုက free() ကို သံုးတဲ့အေျခအေန

free(buffer);

free ကိုသံုးလိုက္ျပီဆိုရင္ heap chunk ကဘယ္လိုျဖစ္သြားလဲဆိုေတာ့

-------------------------------
previous chunk size
chunk size
Forward Pointer (FD)
Backward Pointer (BK)
-------------------------------
previous chunk size
chunk size
Forward Pointer (FD)
Backward Pointer (BK)
-------------------------------

ဒါကိုလဲနားလည္ေအာင္ MBE က example ေလးနဲ႕ စမ္းၾကည့္လိုက္မယ္

Link

/* compiled with: gcc -o print_frees print_frees.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define LEN 15

void print_chunk(unsigned int * ptr)
{
    /* LOL MAGIC */
    int is_free = *((*(ptr-1) & ~7)/4 + (ptr-1)) & 1;
    
    if(is_free)
    {
        printf("[ prev - 0x%08x ][ size - 0x%08x ][ data buffer (0x%08x) ----> ... ] - Chunk 0x%08x - In use\n", \
                *(ptr-2),
                *(ptr-1),
                (unsigned int)ptr,
                (unsigned int)(ptr-2));
    }else{
        
        printf("[ prev - 0x%08x ][ size - 0x%08x ][ fd - 0x%08x ][ bk - 0x%08x ] - Chunk 0x%08x - Freed\n", \
                *(ptr-2),
                *(ptr-1),
                *ptr,
                *(ptr+1),
                (unsigned int)(ptr-2));
    }

}

int main()
{
    unsigned int * ptr[LEN];
    unsigned int lengths[] = {0, 4, 8, 16, 24, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384};
    int i;

    printf("mallocing...\n");
    for(i = 0; i < LEN; i++)
        ptr[i] = malloc(lengths[i]);
    
    for(i = 0; i < LEN; i++)
        print_chunk(ptr[i]);
   
    printf("\nfreeing every other chunk...\n");
    for(i = 0; i < LEN; i+=2)
        free(ptr[i]);

    for(i = 0; i < LEN-1; i++)
        print_chunk(ptr[i]);
    
    return 0;
}

Result

root@exploitdev:~/Heap# ./print_frees
mallocing...
[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b410) ----> ... ] - Chunk 0x0804b408 - In use
[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b420) ----> ... ] - Chunk 0x0804b418 - In use
[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b430) ----> ... ] - Chunk 0x0804b428 - In use
[ prev - 0x00000000 ][ size - 0x00000019 ][ data buffer (0x0804b440) ----> ... ] - Chunk 0x0804b438 - In use
[ prev - 0x00000000 ][ size - 0x00000021 ][ data buffer (0x0804b458) ----> ... ] - Chunk 0x0804b450 - In use
[ prev - 0x00000000 ][ size - 0x00000029 ][ data buffer (0x0804b478) ----> ... ] - Chunk 0x0804b470 - In use
[ prev - 0x00000000 ][ size - 0x00000049 ][ data buffer (0x0804b4a0) ----> ... ] - Chunk 0x0804b498 - In use
[ prev - 0x00000000 ][ size - 0x00000089 ][ data buffer (0x0804b4e8) ----> ... ] - Chunk 0x0804b4e0 - In use
[ prev - 0x00000000 ][ size - 0x00000109 ][ data buffer (0x0804b570) ----> ... ] - Chunk 0x0804b568 - In use
[ prev - 0x00000000 ][ size - 0x00000209 ][ data buffer (0x0804b678) ----> ... ] - Chunk 0x0804b670 - In use
[ prev - 0x00000000 ][ size - 0x00000409 ][ data buffer (0x0804b880) ----> ... ] - Chunk 0x0804b878 - In use
[ prev - 0x00000000 ][ size - 0x00000809 ][ data buffer (0x0804bc88) ----> ... ] - Chunk 0x0804bc80 - In use
[ prev - 0x00000000 ][ size - 0x00001009 ][ data buffer (0x0804c490) ----> ... ] - Chunk 0x0804c488 - In use
[ prev - 0x00000000 ][ size - 0x00002009 ][ data buffer (0x0804d498) ----> ... ] - Chunk 0x0804d490 - In use
[ prev - 0x00000000 ][ size - 0x00004009 ][ data buffer (0x0804f4a0) ----> ... ] - Chunk 0x0804f498 - In use

freeing every other chunk...
[ prev - 0x00000000 ][ size - 0x00000011 ][ fd - 0x0804b428 ][ bk - 0x0804b450 ] - Chunk 0x0804b408 - Freed
[ prev - 0x00000010 ][ size - 0x00000010 ][ data buffer (0x0804b420) ----> ... ] - Chunk 0x0804b418 - In use
[ prev - 0x00000000 ][ size - 0x00000011 ][ fd - 0x0804c488 ][ bk - 0x0804b408 ] - Chunk 0x0804b428 - Freed
[ prev - 0x00000010 ][ size - 0x00000018 ][ data buffer (0x0804b440) ----> ... ] - Chunk 0x0804b438 - In use
[ prev - 0x00000000 ][ size - 0x00000021 ][ fd - 0x0804b408 ][ bk - 0xf7fc97b0 ] - Chunk 0x0804b450 - Freed
[ prev - 0x00000020 ][ size - 0x00000028 ][ data buffer (0x0804b478) ----> ... ] - Chunk 0x0804b470 - In use
[ prev - 0x00000000 ][ size - 0x00000049 ][ fd - 0xf7fc97b0 ][ bk - 0x0804b568 ] - Chunk 0x0804b498 - Freed
[ prev - 0x00000048 ][ size - 0x00000088 ][ data buffer (0x0804b4e8) ----> ... ] - Chunk 0x0804b4e0 - In use
[ prev - 0x00000000 ][ size - 0x00000109 ][ fd - 0x0804b498 ][ bk - 0x0804b878 ] - Chunk 0x0804b568 - Freed
[ prev - 0x00000108 ][ size - 0x00000208 ][ data buffer (0x0804b678) ----> ... ] - Chunk 0x0804b670 - In use
[ prev - 0x00000000 ][ size - 0x00000409 ][ fd - 0x0804b568 ][ bk - 0x0804c488 ] - Chunk 0x0804b878 - Freed
[ prev - 0x00000408 ][ size - 0x00000808 ][ data buffer (0x0804bc88) ----> ... ] - Chunk 0x0804bc80 - In use
[ prev - 0x00000000 ][ size - 0x00001009 ][ fd - 0x0804b878 ][ bk - 0x0804b428 ] - Chunk 0x0804c488 - Freed
[ prev - 0x00001008 ][ size - 0x00002008 ][ data buffer (0x0804d498) ----> ... ] - Chunk 0x0804d490 - In use

အရင္ဆံုး freed လုပ္တဲ့ဟာကိုၾကည့္လိုက္မယ္။

maloc(0) က inuse မွာဆိုရင္ heap chunk က ေအာက္ကအတိုင္းျဖစ္ေနတယ္။

[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b410) ----> ... ] - Chunk 0x0804b408 - In use

0x0804b408 ကို freed လုပ္တဲ့အခါမွာ

[ fd - 0x0804b428 ][ bk - 0x0804b450 ] - Chunk 0x0804b408 - Freed

fd က ဘာကိုညႊန္ျပသလဲဆိုရင္ next freed chunk ကိုညႊန္ထားတယ္။ ဟုတ္တယ္ free လုပ္ထားတာက တစ္ခုေက်ာ္ျဖစ္တဲ့အတြက္ ေအာက္က heap chunk ကုိ သူျပေနတယ္။

[ prev - 0x00000000 ][ size - 0x00000011 ][ data buffer (0x0804b430) ----> ... ] - Chunk 0x0804b428 - In use

bk ကေတာ့ previous freed chunk ကို ညႊန္ျပတာျဖစ္တယ္။ ဒါကိုရွင္းေအာင္သိခ်င္ရင္ေတာ့

ခုနက freed လုပ္ခဲ့တဲ့ 0x0804b408 မွာ ေနာက္တစ္ခါ freed လုပ္မယ့္ next freed chunk က 0x0804b428 ဆိုရင္ အဲ့ကိုေရာက္လုိ႕ freed လုပ္တဲ့အခါမွာ previous freed chunk က 0x0804b408 ျဖစ္ရမယ္မဟုတ္လား

[ prev - 0x00000000 ][ size - 0x00000011 ][ fd - 0x0804c488 ][ bk - 0x0804b408 ] - Chunk 0x0804b428 - Freed

ဒီလိုဆိုရင္ heap ရဲ႕ memory မွာ allocate လုပ္ပံုကို နားလည္ျပီလို႕ထင္ပါတယ္။

Thanks

Ref : http://security.cs.rpi.edu/courses/binexp-spring2015/

အဲဒီ course ကလဲ ေတာ္ေတာ္ေလးရွင္းထားတာေကာင္းပါတယ္။

 

1 Trackback / Pingback

  1. 0x11 – Understanding Use After Free – Legion of LOL

Comments are closed.