Home C programming - Pointers 0x00
Post
Cancel

C programming - Pointers 0x00

Hello there, dear reader.

In this article, we are going to get a glimpse of pointers in the C programming language.

Now I’m not a C wizard I can assure you that, but I think i have a good grasp of it and I’m here to share the knowledge !

What is a pointer ?

To make it quick, pointers are just variables.

The special thing about them is that instead of storing a value, they store addresses.

But how ?

Let’s take this simple example

1
2
3
4
5
6
7
8
9
int main(void)
{
  int a;
  int *p;

  a = 1337;
  p = &a;
}

Pointers are defined by prepending an asterisk (*) to the variable name.

As we said, pointers point to data, they hold addresses.

To get an address out of a variable, we append & to it.

To get the value of data pointed by a pointer, we use * to dereference our pointer.

Important Note : the asteriks * used to declare a pointer and dereference it are different.

  • The first tells the compiler that the variable is a pointer, after that we don’t use it again.
  • The second is when we need to access the value of data that our pointer is pointing to (which is called dereferencing)

Here is a simple illustration to make it a bit clear

  • The left column represents the identifiers (the name of our variables ).
  • The middle column is our memory.
  • The right column represents addresses - where those variables live in the memory.

As you can see in the illustration, when we initialized a = 1337 , our a has been reserved 4 bytes in memory, which I will represent by 0x04 for the sake of understanding.

Note : 4 bytes since our variable is an int, and int is often 4 bytes. You can check its size by printing sizeof(int)

As for p, you can notice 2 things :

  • memory reserved for p is almost doubled compared to a
  • p stores the address of a

Our pointer will always get allocated 8 or 4 bytes of memory depending on the processor’s architecture ( 4 bytes for x86 processors, and 8 bytes for x64 processors ) regardless of the type it points to (char, int , …).

But why ?

Why do we need pointers 😕 ??

Jokes on you, they are made for one purpose : to make our lives harder 😨.

Okey jokes aside, Pointers are awesome 😈 (Yeah i sound crazy ikr), they have many use cases and are very powerful when it comes to memory management.

Now it would be boring 🥱 if I start listing pointers use cases and giving definitions, that’s not my goal.

Instead, I want to keep you entertained and challenged at the same time, so here is a problem for you :

Write a c function that swaps the values of two integers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

void swap(...)
{
  // your code goes here
}

int main(void)
{
  int a;
  int b;

  a = 1;
  b = 2;

  printf("before swapping...\n");
  printf("a : %d, b : %d\n",a,b); //a : 1, b : 2
  //swap(...)

  printf("after swapping...\n");
  printf("a : %d, b : %d\n",a,b); //a: 2, b : 1
}

I’ll wait for you to finish…

Got it? good job 😁.

You used pointers? Excellent JOB 😍.

Have I made my point ? HELL YEAH 😍.

You can find the solution at the end of the article.

Pointers - a bit trippy ?

Let’s dive a little bit deeper.

Trippy 0x00

You know that, when declaring an array, you are just making a pointer that points to the first element of that array.

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>

int main(void)
{
        int arr[] = {1,2,3,4,5};

        printf("%d",*arr);

        return 0;
}

After compiling and executing our program, what do you think the output would be?

1
1

As I said, arr is just a pointer to the first element of the array, since the first element is 1, when dereferencing we get 1.

Trippy 0x01

Trippy enough? little do you know 🤭

Let’s create a simple program that accomplishes the following tasks:

  • creating a simple static array of 5 integers.
  • making a pointer that points to that array.
  • print the elements of the array using the pointer.

Sounds simple enough…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
        int     arr[] = {1, 2, 3, 4 ,5};

        int     *ptr;
        int     i;

        ptr = arr;

        for(i = 0 ; i < 5; i++)
        {
                printf("arr[%d] : %d\n",i,*(ptr + i));
        }
        return 0;
}
1
2
3
4
5
6
#Program stdout
arr[0] : 1
arr[1] : 2
arr[2] : 3
arr[3] : 4
arr[4] : 5

Trippy 0x02

Well, it’s fun isn’t it 🥰 ?

Let’s use the same program we used above, and change one thing , our pointer’s type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
        int     arr[] = {1, 2, 3, 4 ,5};

        char    *ptr; // we changed int to char
        int     i;

        ptr = (char *)arr; // this is called typecasting, basically i just added it to avoid the compiler's warnings

        for(i = 0 ; i < 5; i++)
        {
                printf("arr[%d] : %d\n",i,*(ptr + i));
        }
        return 0;
}

Technically we just said to the compiler that ptr points to char instead of int.

What would happen in this case? Let’s run our program and see.

1
2
3
4
5
6
#Program stdout
arr[0] : 1
arr[1] : 0
arr[2] : 0
arr[3] : 0
arr[4] : 2

Let me explain…

By definition, char is 1 byte long, and int is 4 bytes long.

Now if we take the first element of our array, which is 1, and write it in binary, respecting the 4 bytes rule, we get this :

00000000 00000000 00000000 00000001

Each byte is 8 bits, and we need 4 of them.

Now let’s rewrite this in hexadecimal.

0x00000001 <=> 00 00 00 01

Is the image getting clearer ? let’s continue…

You are most likely using an intel / amd processor.Both those processors use Little Endian byte order.

Basically little endian would store bytes from the least significant byte to the most significant byte.

Meaning 00 00 00 01 will be stored as 01 00 00 00

Do you get it now ? 01 00 00 00 are 4 bytes (no shit 😮).

And since our pointer is a pointer to char, it prints 1 byte at a time inside our program’s loop.

the first byte is 1, then 0, then 0 then 0.

Trippy 0x03 ~ Challenge

Okey for this part, you are going to prove to me that you grasped what we’ve discussed earlier.

Your task is to fill in the array with exactly one element to match the output.

Note : use a pen ✍🏼 and paper 📄, it’s really helpful.

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>

int main(void)
{

        int int_array[1] = {/*put one element here*/};
        char *ptr = (char *) int_array;

        for(int i = 0 ; i < 4 ; i++)
                printf("%d\n",*(ptr + i));
        return 0;
}
1
2
3
4
5
#Program stdout
1
2
3
4

The solution will be in the next article ~ Inshallah.

Solutions

exercise 1 - swap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// the function
void swap(int *q, int *p)
{
  int tmp;

  tmp = *q;
  *q = *p;
  *p = tmp;
}

int main(void)
{
  //...
  swap(&a,&b) // since swap accepts pointers, you need to provide addresses
  //...
  return 0;
}

Bibliography

📖 Hacking: The Art of Exploitation, 2nd Edition

https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Pointers

This post is licensed under CC BY 4.0 by the author.

Chill Hack

Bandit level 25 to 27 - Walkthrough