C
C is a high-level programming language which allows you to construct programs writing in a syntactical form. When compiled (typically using cc (short for C compiler) or gcc (GNU C compiler)), the C code will be converted into machine-readable code to execute the program. Most distributions have gcc as base package included so that no further setup is necessary in order to start developing C-programs, if not however you can install all necessary applications through your respective package manager with these commands:
- Debian/Ubuntu
apt-get install build-essential
- Arch Linux
pacman -S base-devel
Contents
Overview
Basic programs can be broken down into 3 main categories: variables, loops, and If/Else statements.
Basic Formatting
Each C program follows a general format.
Includes
Includes are calls from within a C program which reference a library - a database of predefined functions. They are used to provide a standardised set of functions that programmers can use rather than reinventing the wheel with each program.
Includes in C follow this syntax:
<syntaxhighlight lang="c">
//searches for library.h in the default directory of libraries
//searches for library.h in the defined path </syntaxhighlight> |
A few includes are necessary for every C program - namely stdio.h (a library defining functions to deal with basic input and output). By convention, includes are normally placed at the beginning of a program, although it is not necessary.
The main() Function
The concept of functions is beyond the scope of this introductory article, but simply put, a function is a block of code that is executed each time it is called and must return a value. There are many applications for functions, but the most important function is main().
The main() function is where the main code of a program is placed - when a program runs, main() is where the program starts executing code.
As such, a typical program may look like this:
<syntaxhighlight lang="c">
int main() { printf(%s, "Hello, world!\n"); return 0; } </syntaxhighlight> |
Variables
A variable is a value that stores data that can be edited, modified, and used at a later time. To declare a variable in the C language your first declare its type and then the variable name. Some of the basic variable types are:
int iName; float fName; double dName; char cName; |
Integer or int variables can store whole numbers while floats and doubles can hold integer values with decimal places. A char type variable can only hold a single character. while C itself does not have a string variable type you can create a array of characters refereed to as a CString to accomplish the same task.
Further you can use Variables of any type as pointers or arrays by adding their respective signs. These can be used to store multiple Values in one Variable (Array) or to store information about memory allocation (Pointer) :
int *iPointer; char cArray[]; |
Loops
In C there are three types of loops that allow the user to accomplish a repetitive task without repeating numerous lines of code. These three basic loops are called the for loop, the while loop, and the do while loop. Each loop has their own purpose for being used and normally follow the same syntax. All loops are based of an equation and if that equation does not evaluate to true then the looping will not halt. A for loop is good for a repetitive task that you know how many times you want to repeat, while a while loops is normally used when how many times you need to loop is unknown like when you are reading a text document. A do while loop is almost the same as a while loop except for one difference, it runs its code at least once before checking if it should stop looping
Examples:
<syntaxhighlight lang="c"> int i; for(i = 0; i < 10; i++) //integer "i" equals 0. when "i" is less then 10 increment "i" by one { //code to repeat 9 times } </syntaxhighlight> |
<syntaxhighlight lang="c"> char myChar; while(myChar != 'c') //While "myChar" does not equal "c" continue to loop { scanf("%c", &myChar); //get input from the user and put it into variable "myChar" } </syntaxhighlight> |
<syntaxhighlight lang="c"> do //loop at least once { x = x + 1; //variable x equals itself plus one (if x equals 0 then x = 0 + 1) } while(x < 2); //check to see if condition to stop looping is met </syntaxhighlight> |
If/Else
If/Else statements are used when you need some way to control the flow of execution of your code. These statements are just like asking questions and depending upon if the answer is true or false the program may execute differently.
Example:
<syntaxhighlight lang="c"> if(1 == 1) //if 1 equals 1 execute the true code block { printf("This is the true code block"); //execute the true code block }else{ //if the statement is not true printf("This is the false code block"); //execute the false code block } </syntaxhighlight> |
Optimizing Security of your Programs
In this part we will present you a few functions that should be avoided and their counter pieces which are to be preferred, as well as general advice on secure programming with C.
Avoiding Buffer Overflow Vulnerabilities
Buffer Overflows occur when programs try to store more information in a variable than it has memory allocated for. For example if you declare a variable that's defined as an array of 9 characters it has space for 8 characters plus the terminating null, so if this variable receives its input from stdin it is easy for a user to corrupt your programs functionality simply by assigning it 9 or more characters. This can only be avoided by sanitizing your input, for example consider the following code snippet:
<syntaxhighlight lang="c"> char password[9]; printf("Please enter your password: "); fflush(stdout); gets(password); </syntaxhighlight> |
The danger here lies within the gets() function which simply copies the whole input onto the stack which will most likely result in data corruption. The safe way to solve this would be to use fgets() instead, which has additional parameters that allow you to sanitize input. Simply change the last line of the program to
fgets(password, 9, stdin);
Like this the fgets() function only copies the first 8 characters from stdin onto the stack, following the terminating null. Because of that you should also always check the length of the input, it needs to be 'sizeof() - 1' in order to avoid overwriting the null byte.
Other functions that are to be avoided for the same reasons are strcpy(), strlen() and sprintf(). Instead use their safer counterparts strncpy(), strnlen() and snprintf().
Program Environment
A well written program should never be designed in such way that it relies on information about its environment, such as the working directory or the value of its umask, which is why you should only use full-path names instead of relative names in order to work with external files. Also you should consider what UID and GID you will let your program run under, for example it is extremely important that vulnerable or potentially malicious programs don't run as root. It is always advised to grant the program only the permissions it absolutely needs to perform its tasks, so you can for example consider to run the program under a designated UID and a correspondant GID, giving them only restricted access to whatever the program requires.
system() & popen()
These functions are used to call exterior programs that are installed on your system. They should be avoided because they will spawn a shell in order to do so. Instead use fork() or exec() to achieve the same goal without compromising the security of your programs environment.
Compilation
In order to run a finished program you will have to create an executable binary by compiling the source code, which is done with cc/gcc
gcc -Wall -o <output file> <sourcecode file> chmod +x <output file>
where -Wall enables full documentation of warnings during the compilation process.
Sometimes you will have to include a specific library and if it's not found, specify the directory in which it is contained. This is done with the -l and -L parameters, for example if you need to include the library libconv-core.a which is located in /usr/local/char/lib the command to use would look like this
gcc -Wall -lconv-core -L/usr/local/char/lib -o <output file> <sourcecode file>
Example Program
This simple little script makes use of two subfunctions (datew and daten) which are each called by another function (main->daten->datew) in order to generate a list of all dates inbetween the year 999 and the end of 2012, in all numeric and a few alphabetic formats, with only those simple methods described in this article.
<syntaxhighlight lang="c">
int daten(char *limiter) { int month; int day; int n; for (n = 999; n < 2013; n++) { for (month = 1; month < 13; month++) { for (day = 1; day < 32; day++) { if ((day < 10) && (month < 10)) { printf("0%d%s%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s%d%s0%d\n", n, limiter, month, limiter, day); printf("%d%s0%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s0%d%s%d\n", n, limiter, month, limiter, day); printf("0%d%s0%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s0%d%s0%d\n", n, limiter, month, limiter, day); printf("%d%s%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s%d%s%d\n", n, limiter, month, limiter, day); } else if ((day < 10) && (month >= 10)) { printf("0%d%s%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s0%d%s%d\n", month, limiter, day, limiter, n); printf("%d%s%d%s0%d\n", n, limiter, month, limiter, day); printf("%d%s%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s%d%s%d\n", month, limiter, day, limiter, n); printf("%d%s%d%s%d\n", n, limiter, month, limiter, day); } else if ((month < 10) && (day >= 10)) { printf("%d%s0%d%s%d\n", day, limiter, month, limiter, n); printf("0%d%s%d%s%d\n", month, limiter, day, limiter, n); printf("%d%s0%d%s%d\n", n, limiter, month, limiter, day); printf("%d%s%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s%d%s%d\n", month, limiter, day, limiter, n); printf("%d%s%d%s%d\n", n, limiter, month, limiter, day); } else { printf("%d%s%d%s%d\n", day, limiter, month, limiter, n); printf("%d%s%d%s%d\n", month, limiter, day, limiter, n); printf("%d%s%d%s%d\n", n, limiter, month, limiter, day); } datew(day, month, n, limiter); } } } } int datew(int day, int month, int n, char *limiter) { char *en1; char *en2; char *fr1; char *fr2; if (month == 1) { en1 = "January"; en2 = "january"; fr1 = "Janvier"; fr2 = "janvier"; } if (month == 2) { en1 = "February"; en2 = "february"; fr1 = "F\xe9" "vrier"; fr2 = "f\xe9" "vrier"; } if (month == 3) { en1 = "March"; en2 = "march"; fr1 = "Mars"; fr2 = "mars"; } if (month == 4) { en1 = "April"; en2 = "april"; fr1 = "Avril"; fr2 = "avril"; } if (month == 5) { en1 = "May"; en2 = "may"; fr1 = "Mai"; fr2 = "mai"; } if (month == 6) { en1 = "June"; en2 = "june"; fr1 = "Juin"; fr2 = "juin"; } if (month == 7) { en1 = "July"; en2 = "july"; fr1 = "Juillet"; fr2 = "juillet"; } if (month == 8) { en1 = "August"; en2 = "august"; fr1 = "Ao\xfb" "t"; fr2 = "ao\xfb" "t"; } if (month == 9) { en1 = "Septembre"; en2 = "septembre"; fr1 = "Septembre"; fr2 = "septembre"; } if (month == 10) { en1 = "October"; en2 = "october"; fr1 = "Octobre"; fr2 = "octobre"; } if (month == 11) { en1 = "November"; en2 = "november"; fr1 = "Novembre"; fr2 = "novembre"; } if (month == 12) { en1 = "December"; en2 = "december"; fr1 = "D\xe9" "cembre"; fr2 = "d\xe9" "cembre"; } if (day < 10) { printf("0%d%s%s%s%d\n", day, limiter, fr1, limiter, n); printf("%s%s0%d%s%d\n", fr1, limiter, day, limiter, n); printf("0%d%s%s%s%d\n", day, limiter, fr2, limiter, n); printf("%s%s0%d%s%d\n", fr2, limiter, day, limiter, n); printf("0%d%s%s%s%d\n", day, limiter, en1, limiter, n); printf("%s%s0%d%s%d\n", en1, limiter, day, limiter, n); printf("0%d%s%s%s%d\n", day, limiter, en2, limiter, n); printf("%s%s0%d%s%d\n", en2, limiter, day, limiter, n); } printf("%d%s%s%s%d\n", day, limiter, en1, limiter, n); printf("%s%s%d%s%d\n", en1, limiter, day, limiter, n); printf("%d%s%s%s%d\n", day, limiter, en2, limiter, n); printf("%s%s%d%s%d\n", en2, limiter, day, limiter, n); printf("%d%s%s%s%d\n", day, limiter, fr1, limiter, n); printf("%s%s%d%s%d\n", fr1, limiter, day, limiter, n); printf("%d%s%s%s%d\n", day, limiter, fr2, limiter, n); printf("%s%s%d%s%d\n", fr2, limiter, day, limiter, n); } int main(void) { daten("\\"); daten("/"); daten(" "); daten("_"); daten("*"); daten("^"); daten("-"); } </syntaxhighlight> |
In order to compile and save the binary under the filename "date-gen" run the following:
gcc <filename.c> -o date-gen chmod +x date-gen
If you sort -u the output and use awk to delete all entries that're shorter than 8 characters this script will create a wordlist of a few million entries which can be used for the purpose of WPA password-recovery and the likes.
./date-gen | sort -u >> wordlist.txt awk '{if ((length($0) >= 8) && (length($0) <= 63)){ print $0 }}' wordlist.txt > wordlist.wpa.txt