Thursday, December 2, 2021

Beginning malloc - C Programming

Currently supporting the SANS Rogers Ryerson Cyber Secure Catalyst Program as a mentor and one of the young ladies asked me to help her get a better understanding of malloc. This post is meant to aid her understanding ... and mine also. Nothing like teaching by learning.

malloc is used to allocate memory and is typically used with free to deallocate the previously allocated memory.

It allocates the size bytes and returns a pointer to the allocated memory. This memory is not initialized and if the size is 0 malloc returns NULL.     

Two things to keep in mind for malloc are:
    1. The memory is dynamically allocated on the Heap
    2. The pointer which is returned is stored on the Stack.

We will look at both of these points.

Here is the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdlib.h>
#include <stdio.h>

/*
Author: Nik Alleyne
Blog: www.securitynik.com

*/

int main()
 {
	// Variables declaration 
	unsigned int memory_size, *memory_ptr;
	unsigned int count = 0;
			
	printf("[*] How many bytes would you like to allocate?: ");
			
	// Read the number of bytes from the user
        scanf("%d", &memory_size);
			
	// store the return value into a variable
	memory_ptr = (int *) malloc(memory_size);
			
	// Check to see if the allocation was unsuccessful
	if (memory_ptr == NULL)
		{
		  printf("[!] Error allocating your %d bytes", memory_size);
		  return -1;
		}
				
	// If allocation successful
	printf("[*] Dynamically allocated %d 4 bytes (size of int) @ %p \n", memory_size, (void *)&memory_ptr);		
			
	// printing the contents of the memory
	printf("[*] Here is the memory contents ... \n");
	for ( count; count < memory_size; count++)
		{
		  printf("   -> Count: %d > 0x%lx \n", count, &memory_ptr[count]);
		}
			
	printf("[*] Freeing the previously allocated %d bytes", memory_size);
	free(memory_ptr);
		
	return 0;
			
}    

Compiling the program.

┌──(root💀securitynik)-[/tmp]
└─# gcc -ggdb /home/securitynik/beginning-malloc.c -o beginning-malloc.exe

When running the program, giving it 10 as input to print 10 bytes. Note, this is not printing 10 individual bytes but the size of int which is 4 bytes.

┌──(root💀securitynik)-[/tmp]
└─# ./beginning-malloc.exe
[*] How many bytes would you like to allocate?: 10
[*] Dynamically allocated 10 4 bytes (size of int) @ 0x7ffd66aba450 
[*] Here is the memory contents ... -> Count: 0 > 0x55b893337ac0 -> Count: 1 > 0x55b893337ac4 -> Count: 2 > 0x55b893337ac8 -> Count: 3 > 0x55b893337acc -> Count: 4 > 0x55b893337ad0 -> Count: 5 > 0x55b893337ad4 -> Count: 6 > 0x55b893337ad8 -> Count: 7 > 0x55b893337adc -> Count: 8 > 0x55b893337ae0 -> Count: 9 > 0x55b893337ae4 [*] Freeing the previously allocated 10 bytes

Good stuff. What do we have there. Let's dig deeper. Let's run the program again.

┌──(root💀securitynik)-[/tmp]
└─# ./beginning-malloc.exe
[*] How many bytes would you like to allocate?: 

As the program waits for input, let's grab the PID in another window.

┌──(root💀securitynik)-[/tmp]
└─# ps -u | grep beginning
root       52719  0.0  0.0   2316   644 pts/1    S+   22:55   0:00 ./beginning-malloc.exe
root       52758  0.0  0.1   6184  2144 pts/2    S+   22:56   0:00 grep --color=auto beginning

With the PID 52719, let's now look at the program memory map in the same window that we grabbed the PID from.

┌──(root💀securitynik)-[/tmp]
└─# cat /proc/52719/maps 
55d9f8f92000-55d9f8f93000 r--p 00000000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f93000-55d9f8f94000 r-xp 00001000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f94000-55d9f8f95000 r--p 00002000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f95000-55d9f8f96000 r--p 00002000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f96000-55d9f8f97000 rw-p 00003000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9fa801000-55d9fa822000 rw-p 00000000 00:00 0                          [heap]
7f3507906000-7f3507908000 rw-p 00000000 00:00 0 
7f3507908000-7f350792e000 r--p 00000000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f350792e000-7f3507a77000 r-xp 00026000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507a77000-7f3507ac2000 r--p 0016f000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac2000-7f3507ac3000 ---p 001ba000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac3000-7f3507ac6000 r--p 001ba000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac6000-7f3507ac9000 rw-p 001bd000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac9000-7f3507acf000 rw-p 00000000 00:00 0 
7f3507aee000-7f3507aef000 r--p 00000000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507aef000-7f3507b0f000 r-xp 00001000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507b0f000-7f3507b18000 r--p 00021000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507b18000-7f3507b19000 r--p 00029000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507b19000-7f3507b1b000 rw-p 0002a000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7ffd68cd7000-7ffd68cf8000 rw-p 00000000 00:00 0                          [stack]
7ffd68dec000-7ffd68df0000 r--p 00000000 00:00 0                          [vvar]
7ffd68df0000-7ffd68df2000 r-xp 00000000 00:00 0                          [vdso]


Key for us from above are [heap] and [stack]

Earlier at the beginning of this post, I stated malloc allocates the number of bytes we specify on the heap, while the pointer address is stored on the stack. Let's go back to our program, specify 10 bytes and let the program run to completion, then we will revisit the stack and heap output from above.

┌──(root💀securitynik)-[/tmp]
└─# ./beginning-malloc.exe
[*] How many bytes would you like to allocate?: 10
[*] Dynamically allocated 10 4 bytes (size of int) @ 0x7ffd68cf55f0 
[*] Here is the memory contents ... -> Count: 0 > 0x55d9fa801ac0 -> Count: 1 > 0x55d9fa801ac4 -> Count: 2 > 0x55d9fa801ac8 -> Count: 3 > 0x55d9fa801acc -> Count: 4 > 0x55d9fa801ad0 -> Count: 5 > 0x55d9fa801ad4 -> Count: 6 > 0x55d9fa801ad8 -> Count: 7 > 0x55d9fa801adc -> Count: 8 > 0x55d9fa801ae0 -> Count: 9 > 0x55d9fa801ae4 [*] Freeing the previously allocated 10 bytes

The first take away from the output above is 10 4 bytes (size of int) @ 0x7ffd68cf55f0

Looking at the address 0x7ffd68cf55f0 and comparing this to our stack output from above 7ffd68cd7000-7ffd68cf8000 , we see 0x7ffd68cf55f0 falls within the range previously for seen the stack. Specifically, we see 0x7ffd68c.... matches above. Which means f55f0 is somewhere within there. This confirms the point that pointer address is stored on the stack. 

Let's now confirm this points to an address on the heap. If we look at the output from the memory map, we see the heap is within the range: 55d9fa801000-55d9fa822000. Looking at the output from our 10 bytes above, we see the first few bytes of the heap 0x55d9fa801..., matching our output above. The last 3 bytes then confirms that our range falls within the range of the heap.

The reason why you are seeing jumps of 4, for example c0, c4, c8, cc, etc., is because the size of integer is 4 bytes.

That's it!


References:

No comments:

Post a Comment