Error » Certification & Programming center Error !! » Programming tutorials » Pointers Ow Can They Work

Programming tutorials All Knowledge Info and links to posted here

Post New Thread Reply
  Pointers Ow Can They Work
LinkBack Thread Tools Display Modes
Old 23-Feb-2007, 07:07 AM   #1 (permalink)
Fixed Error!
 
kingaff's Avatar

Posts: 330
Join Date: Feb 2007
Rep Power: 2 kingaff is on a distinguished road

IM:
Default Pointers Ow Can They Work

Prologue
I remember the time I started learning C. It was not too long ago. I liked programming, I always did, but I didn't know a lot of languages. The only languages I knew were useless, such as Visual Basic, PHP, ASP, and the basics of Javascript and VBScript.
So I decided to learn a real language: C. So I downloaded some tutorials and started reading. The well known "Hello, world!" program didn't cause any difficulties, nor did the few programs after it.
But then some new word showed up in the tutorial: "pointer". What was this horrible thing? I kept reading, but I just could not understand it. Maybe it was because my first language wasn't English, and the explenation was too hard? I didn't know - I still don't actually. Maybe my English was too bad back then?
Fortunately I didn't give up and kept on reading. I understood everything except for the pointer, and it was a huge pain in the ass. Pointers are used very often in C. And it took a long time before I really understood how pointers worked and yet it was actually very easy... Once you understand it. So I will try to explain it in this tutorial. Basic knowledge of C is required.



What is a pointer?
A pointer is nothing else than an address of some place in the memory. A pointer can be defined like:
char* test;
In this case, test is a number, not a char as you might think. It's a number - an address to be exact. This address is an address of a place in the memory.
Let's describe the memory as a bunch of lines. Every line (-) is one byte. The "^" points to a line, thus to a byte. Now have a look at the following drawing:
--------------------------------------------------------------------------
^

The "^" points to a byte. It just shows you one place in the memory. It's a pointer(!) to a place in the memory.

So the pointer is an address. The address may or may not already point to a valid address in the memory right after you declared it, that depends on the way you declare it:
char* test;
will NOT point to a valid address in the memory. You will have to make it point somewhere before you read/write from/to it, or else a "Segmentation Fault" will occur.
char test[10];
will point to a valid address in the memory. You can read/write from/to the memory, but be careful: you can not read/write more than 10 characters (note the [10]?). If you DO read/write more than 10 characters, a "Segmentation Fault" will occur. Of course this 10 can be changed, which will allow more characters to be read/written.



What's the use of pointers
There are many uses of pointers. A lot of functions need pointers to be passed. One example is printf if you want to print a string: it will need a pointer to this string. Another use is the ability to use arrays. You may not know what it means now, but I will cover that later. I do assume you know what printf does and how it works. If you do not, please read the printf manual.



Point the pointer
In case you define it the first way ("char* test"), it will not yet point to a valid address in the memory. You can make it point somewhere with malloc. Malloc will reserve the specified amount of bytes and return the memory address of this.
So malloc will create a place in the memory where you can read/write from/to without any problems. It will "allocate" some space in the memory (malloc = Memory ALLOCate). The malloc function will take the ammount of space to allocate as a parameter and return the pointer to the memory. It will return a "void *", but that doesn't matter since all pointers are actually handled exactly the same. Though, there are differences in how you can read/write from/to the memory, so always use the type you want to read or write.
An example:
char* test;
test = (char*)malloc(10);
This will allocate 10 bytes (10 bytes, not 10 characters! The size of a character is 1 byte most of the times, but it may differ.), and assign the location of the memory to test. Note that I use "(char*)" before malloc. This will not convert anything and this is not required. Though, if you don't do this your compiler will display a warning ("warning: assignment makes pointer from integer without a cast"), and it is best to have as little warnings in your program as possible.



Malloc or [#]
So there are two ways to create a pointer to the memory to write to:
char test[10];
or
char* test;
test = (char*)malloc(10);
Of course the number 10 can be changed into any number above 0 (although there is a maximum, but you will probably never reach it).
There are two differences in these ways. The first difference is that from "char test[10];", the size can not be changed. It will keep a length of 10 characters but you can always re-allocate the space for a "char* test;". The second difference is that "char test[10];" will reserve 10 characters while malloc will reserve 10 bytes if you specify 10 as the parameter. On most machines a character will be 1 byte, but this is not on all machines. You can use "malloc(10 * sizeof(char));" to allocate 10 characters in stead of 10 bytes (sizeof(char) will return the size of one character, multiplied with the number of characters to allocate will be the memory in bytes to allocate). You should always do this so that your program will be portable to other computers.
Malloc may fail. This can for example happen when there wasn't enough memory to allocate the specified bytes. If this happens, malloc will return NULL. You should always check whether its NULL after you allocated some space. If you write to a NULL pointer, a "Segmentation Fault" will occur.
I will use "[#]" the rest of this tutorial, because that's easier. Note that the other method may be required sometimes, but since that not the case now and "[#]" will only be one line of code, I will use that method.



Free
If you have a pointer created by malloc which you will not use anymore, you should ALWAYS use free(pointer); (where pointer is the pointer). You must do this before you use malloc on the same pointer again, too. If you do not do this, your program will leak memory. This is bad if it happens once or twice, but can kill your program if it happens very often!
But there is another way to kill your program, which you should watch out for, too. You may not EVER free a pointer which either has not yet been allocated, which failed to be allocated or which has been defined with "[#]". If you do this, a "Segmentation Fault" will occur.
So to make sure you are not freeing a pointer that failed to be created, you should always add a check before you free it:
if(test != NULL)
free(test);
But if you declared test with "char* test;", test will not be NULL. So if it is not allocated and it tries to free it, the program will crash. This is why you should alway declare pointers you will malloc later with "char* test = NULL;". That way the default will be NULL, and the above check will also prevent it from freeing unallocated parts of the memory.
One more tricky part: free will not make the pointer NULL again. So if it is possible it would free it more than once, always use the following check:
if(test != NULL)
{
free(test);
test = NULL;
}
test will be set to NULL when freed, and will not be freed anymore until it has been re-allocated.



Access the memory one by one
You can access the memory on several ways. One way is to access it one by one. I can't say byte by byte, since it differs on how you declared it. For example, if you used a pointer to an integer, this will work integer by integer. If it's to a character it will work character by character.
For example, let's define it as a char:
char test[10] = "0123456789";
This will allocate the space for 10 characters (10 x sizeof(char) bytes), and put "0123456789" in the memory on that place. Test will point to the first character to this string: "0". Now lets say you have defined a character: "char test2;" and you want to put the second character of test in it. What could you do?
What we actually got is nothing else than an array of characters: 10 characters after each other. You can simply access these characters in the memory with "test[#];". Replace the "#" with the number of the character you need, minus one. In this case, "test[0]" equals "0". This way of accessing it will select a char from the memory, not a byte. So in this case, you can pick any number from 0 to 9, and test[#] (where # is your chosen number) will have your number stored as character. Make sure you do not pick a number below 0 or higher than 9, or else it will cause the program to crash with a "Segmentation Fault" or just return a character from another part of the memory.
So test[1] will be in the memory with the address test + 1 * sizeof(char). Test[#] will not return a pointer, but a char, but "test + 1 * sizeof(char)" is possible and will return a pointer. Of course, test[2] will be in the memory with the address test + 2 * sizeof(char), etc.
You can also get a character of a specified memory address by adding a "*" in front of it. For example, "char test2 = *test;" will put the character test points to in test2. Again, you can also use "char test2 = *(test + 1 * sizeof(char));", which will select the second character in the array.
So this means that
"test[0]" equals "*test",
"test[1]" equals "*(test + 1 * strlen(char));",
"test[2]" equals "*(test + 2 * strlen(char));", and so on.
Here, too, you must pay attention: you may not read in a memory location you did not allocate. Otherwise the result will be a character of something different in the memory from or, again, cause a "Segmentation Fault".
Of course you can write to the memory the same way:
test[0] = 'a';
will put the character "a" in the memory ('' is for characters, "" is for pointers to strings), and
*test = 10;
will put a return character (\n, ascii code 10) in the memory test points to.



Value to pointer
Let's say you have a character and you would like to know where in the memory it is. It's very simple to do this: just put a "&" in front of the variable you want the pointer of, and it will return a pointer. So:
char* test;
char test2 = '!'; //This character is somewhere in the memory... but Where?
test = &test2; //Ah, there it is!
Now test contains the address of the memory where test2 is stored. This can be used on, for example, the following way:
char test[11] = "0123456789\0";
//\0 Makes sure it ends with a 0-character. If not, the printf
//function will just keep on reading until it sees a 0-char.
//So it will display characters which should not be shown.
//Or may cause a "Segmentation Fault"
printf("%s\n", &test[1]);
The first thing to explain is the term a "0-character". It's a character with the ascii code 0. In a string, it identifies the end of the string. If this is read, the functions know what the end of the string is.
test[1] will select the second character. The & will make a pointer of it. So the pointer to the second character of "0123456789" will be passed, thus the memory address of the character '1' will be passed to printf. Printf will read the memory, and show the characters there. So it will show "123456789". If test[10] would not be 0, it would keep on reading memory outside test's allocated memory, and possible show characters that should not be shown, maybe even complete passwords if you're unlucky or it may crash with a "Segmentation Fault".



Copy functions
You can use the method described above to assign values to the memory manually. Of course, this is a lot of work for some tasks. For example, if you would want to put "test" in the memory, ending with a 0 character so that you can use it with printf(); later, you could use:
char test[5];
test[0] = 't';
test[1] = 'e';
test[2] = 's';
test[3] = 't';
test[4] = 0;
Too much work. And you can't use
char test[10] = "0123456789";
test = "9876543210";
either, because that would try to put the string "9876543210" somewhere in the memory, and try to put the pointer in test. Not the string itself into the memory where the pointer points to.
For this use some functions were developed. I won't cover all of them, but I will cover the ones you will probably use most:
- sprintf and snprintf
- strcpy and strncpy
- memcpy
Note that all those functions can cause so called "buffer overflows" these are exploits that can be used for causing the program to crash, or even become (both remote or local) root, or just get a remote shell. You should NEVER try to copy more characters to the destination than allocated for the destination! Check all the data you want to write before you actually write it!



sprintf and snprintf
I said earlier in this tutorial I assume you know how printf works. That's required to understand this function too: this is simply because the sprintf function is almost identical to the printf function. Everything works exactly the same, but it will require one more paramter: the first parameter has to be the pointer pointing to the place in the memory to write to. The output will unlike the printf function not be sent to the screen, but to the memory the pointer points to. An example:
char test[11];
sprintf(test, "%s", "0123456789");
will write "0123456789" to the memory where test points to. This string will be terminated with a 0-character so that you can pass it to functions like printf. Note you will have to spare one character for this 0-character too! But this is still unsafe.
For this matter, snprintf has been developed. This is almost identical to the sprintf function with one difference: it has one more parameter, inserted between the first and second parameter, which will be the MAXIMUM number of characters to write to the memory. This is including the 0-character, thus:
char test[10];
snprintf(test, 10, "%s", "0123456789");
Will only print "012345678" to the memory, ended with a 0-character. Note that it will not only stop when the maximum number of characters has been reached, but also when a 0-character is seen. This is because the 10th character is the last character allowed to be written and has to be changed to a 0-character in order be allowed to be passed to functions such as printf, and a lot others. If the string to write is less than the number specified, the number is just ignored. This function is to prevent data from being written to memory where it may not be written.
Both functions will return the number of characters successfuly written, the 0-character not counted. For both functions the strings written to the memory will end with a 0-character.



strcpy and strncpy
Strcpy is even easier than sprintf. Strcpy requires two parameters, both pointers. It will read all data from the memory the second parameter points to and write this to the memory the first parameter points to. It will not stop before it reaches a 0-character (it WILL copy the 0-character too), so you should be 100% sure it ends with one! An example of this function:
char test[11];
strcpy(test, "0123456789");
will copy "0123456789" to the memory test points to, including the terminating 0-character (which is added automatically, if you use "").
Strncpy will work exactly the same way, except that it allows a third parameter: the MAXIMUM number of characters to copy. It will stop when the 0-character is reached or if the maximum number of characters has been reached. This means it is possible it does not end with a 0-character. For example:
char test[5];
strncpy(test, "0123456789", 5);
will write "01234" to the memory test points to, but it will not be terminated with a 0-character, so you may NOT pass it to functions such as printf before you checked or added the 0-character. This function is to prevent data from being written to memory where it may not be written.



memcpy
Memcpy is another function to write something to the memory. It works the same as strncpy, except that it will not terminate after a 0-character. This is useful for things other than strings. For example if you have two variables with the same structure, and you want to copy them. The first parameter is the destination, the second the source and the third the number of bytes to write (so it's the same as strncpy). An example:
struct sockaddr test;
struct sockaddr test2;
memcpy(&test, &test2, sizeof(test2));
This will copy test2 to the memory where test is located. Test will be exactly the same as test2 after this. Sizeof(test2) characters were copied, which is the complete size. So everything.



Compare functions
Now you may need a way to compare two memory locations. For exmaple, are two strings the same, or are two memory locations the same. For this you will need to know three more functions:
- strcmp and strncmp
- memcmp



strcmp and strncmp
Strcmp is the easiest function to compare. It compares two strings (both the parameters), and it will return 0 if the strings are the same. Both strings must be terminated with a 0-character. An example:
char test[11] = "0123456789";
char test2[11] = "9876543210";
if(strcmp(test, test2) == 0)
printf("The strings are the same (1)\n");
strcpy(test2, "0123456789");
if(strcmp(test, test2) == 0)
printf("The strings are the same (2)\n");
This will only display the string "The strings are the same (2)\n". Thus only the second time there was a match.
Strncmp is almost identical, but it will take a third parameter: the number of characters to test. It will ignore all the characters behind this. An example:
char test[11] = "0123456789";
if(strncmp(test, "01234", 5) == 0)
printf("The first 5 characters are equal (1)!\n");
if(strncmp(test, "01234!", 6) == 0)
printf("The first 6 characters are equal (2)!\n");
This will only display the string "The first 5 characters are equal (1)!\n". Thus only the first time there was a match.



memcmp
memcmp is identical to strncmp, except that it will not be terminated by the 0-character. This is useful to compare two non-strings, for example structures. Let's take the example from memcpy a bit further:
struct sockaddr test;
struct sockaddr test2;
memcpy(&test, &test2, sizeof(test2));
if(memcmp(&test, &test2, sizeof(test2)) == 0)
printf("test equals test2\n");
This equals both test's. Since they were just copied, they are identical.



Comments
This version of this textfile is released in astalavista.net and has been written under the name of my group, the Raiders of the Lost Circuit (RLC). You can contact me (Spoofed Existence) using u2u if you got any questions, comments, etc. You can also contact me with u2u if you would like to join the Raiders of the Lost Circuit. We do not yet have a website, but we are working on it.
The Raiders of the Lost Circuit is not a hacking group. It's a group of all kind of people: we have (and still need) authors, programmers, researchers, exploits finders and lots more. If you think you are good enough for any job, even when this job isn't mentioned here, u2u me and we will talk about it.
And please, DO comment. What isn't clear (enough)? What should be improved?
kingaff is offline  
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!Spurl this Post!Reddit!
Reply With Quote
   


   
Post New Thread Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On
Forum Jump


All times are GMT -8. The time now is 03:52 PM.

Powered by vBulletin® Version 3.7.2
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0

DMCA Policy

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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230