Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Raw Sockets Using Winsock

0.00/5 (No votes)
3 Jan 2007 1  
Raw socket programming using the Winsock API.

Introduction

Raw sockets, or "Raw Packets", give you the facility to access the entire contents of a packet or datagram, both for reading and writing purpose. In other words, you can fabricate a whole packet according to your likes and dislikes. For example, a TCP packet would contain an IP header, a TCP header, and then the actual data that needs to be transmitted. When working with normal sockets, whatever we send to a socket is actually the data part. In such a scenario, the OS network stack takes the responsibility of adding the header with all fields set to relevant values. When we send the data to a destination, the stack adds the headers and sends the packet, and when we receive some data, then the stack removes the headers and hands out the data to our application. So we are saved from the work of designing the headers. For normal internet applications, there is no need to be concerned about the header operations as they are there for the safe transmission and reception of data, and once the transfer is complete, their need is over and they are dumped. But the story doesn't end there, there are some people who need raw sockets. Raw sockets are widely used in the field of network security for creating both security and insecurity!

In this article, we will take a look at the contents of a general TCP packet, and try to make a raw packet and transmit it. We shall do this on Windows XP using the VC++ 6.0 compiler. OK, so let's have a look at the IP and TCP headers.

RFC 791 gives the structure of an IP header as:

   0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Next comes the TCP header for transmission using the TCP protocol. RFC 793 gives the structure.

  0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

and at the end is your data, bla bla bla bla bla bla.......................

-----------------------------------------------------------    <<end of packet

To understand the significance of each field, read up the necessary RFC or some other good TCP/IP tutorial on the net as there are plenty. If you have previous knowledge of socket programming, then the headers are self-explanatory. Now, why is the raw socket feature of importance to network security? Well, one important aspect of network security which needs this feature is scanning. Scanning is of many types. For example, scanning for open ports, scanning the type of OS, scanning for vulnerabilities etc.

Raw Sockets and Windows

First of all, it must be understood very clearly that raw sockets is not a feature of the network API (although it must be present there as an option) but of the OS protocol stack. To implement raw sockets, all we have to do is to inform the OS that the packet buffer we are providing will have the header and so the OS should transmit it as is without "adding any header"; that's all, nothing more to do. The Unix operating system has raw socket support since ancient times. But the problem is with Windows. None of Windows 95, 98, 98SE supported raw sockets. Raw sockets became available on Windows from Windows 2000; Windows XP continued this. But suddenly, raw socket support was removed from Windows XP through a patch in SP2. Vista probably doesn't have it. Windows 95, 98, 98SE do not support raw sockets, but this doesn't end the story. If you want the facility, then the solution is to use a third party packet driver like Winpcap. Such packet drivers will do your task irrespective of what the OS likes and dislikes. Windows XP and XP SP1 have full raw socket support and so life is easy. So if you want to do raw socketing on Windows, then either use Winpcap or don't feel desperate to install SP2, or otherwise use Windows 2003 which, as per my knowledge, has raw socket support. So let's brief up.

  1. Windows 95, 98, 98SE, NT4.0 -- Only raw ICMP and IGMP with restricted features.
  2. Windows 2000, XP, XP SP1, 2003 -- Full raw socket support for both receiving and sending purposes.
  3. Windows XP SP2 -- Only raw ICMP, IGMP, and UDP with proper source address (IP spoofing restricted) can be sent. But, full raw sockets can be received, which means you can sniff all incoming data and read their headers.

Note : Winsock Ver. >=2.0

So if your system doesn't support raw sockets, then switch to Linux or use Winpcap.

The Code

SOCKET s;
s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); //Create a RAW socket

int optval=1;
setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&optval, 
           sizeof optval);  //Set it to include the header

The last line, setsockopt, tells the OS that the socket s will have the header included (IP_HDRINCL) at the IP (IPPROTO_IP) level in the data buffer it sends. IPPROTO_RAW creates an absolutely raw socket, and you have to write all headers yourself. IPPROTO_UDP, IPROTO_TCP are also available for the respective types of packets.

Now, we shall need two structures like this:

typedef struct ip_hdr
{
unsigned char ip_header_len:4; // 4-bit header length (in 32-bit words) 

                               // normally=5 (Means 20 Bytes may be 24 also)

unsigned char ip_version :4;   // 4-bit IPv4 version

unsigned char ip_tos;          // IP type of service

unsigned short ip_total_length; // Total length

unsigned short ip_id;          // Unique identifier 


unsigned char ip_frag_offset :5; // Fragment offset field


unsigned char ip_more_fragment :1;
unsigned char ip_dont_fragment :1;
unsigned char ip_reserved_zero :1;

unsigned char ip_frag_offset1; //fragment offset


unsigned char ip_ttl;          // Time to live

unsigned char ip_protocol;     // Protocol(TCP,UDP etc)

unsigned short ip_checksum;    // IP checksum

unsigned int ip_srcaddr;       // Source address

unsigned int ip_destaddr;      // Source address

} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;


// TCP header

typedef struct tcp_header 
{ 
unsigned short source_port;   // source port 

unsigned short dest_port;     // destination port 

unsigned int sequence;        // sequence number - 32 bits 

unsigned int acknowledge;     // acknowledgement number - 32 bits 


unsigned char ns :1;          //Nonce Sum Flag Added in RFC 3540.

unsigned char reserved_part1:3; //according to rfc

unsigned char data_offset:4;    /*The number of 32-bit words 
                                  in the TCP header. 
                                  This indicates where the data begins. 
                                  The length of the TCP header 
                                  is always a multiple 
                                  of 32 bits.*/

unsigned char fin :1; //Finish Flag

unsigned char syn :1; //Synchronise Flag

unsigned char rst :1; //Reset Flag

unsigned char psh :1; //Push Flag 

unsigned char ack :1; //Acknowledgement Flag 

unsigned char urg :1; //Urgent Flag


unsigned char ecn :1; //ECN-Echo Flag

unsigned char cwr :1; //Congestion Window Reduced Flag


////////////////////////////////


unsigned short window; // window 

unsigned short checksum; // checksum 

unsigned short urgent_pointer; // urgent pointer 

} TCP_HDR , *PTCP_HDR , FAR * LPTCP_HDR , TCPHeader , TCP_HEADER;

Little/Big Endian

Did you notice a difference between the RFC specification and the structures declared above? IP header and version have swapped their positions.The urg, ack, and psh flags of the TCP header are all in reverse order? Mistake? Well, this depends on the byte order that is implemented in the machine architecture. There are two types: Little Endian and Big Endian. In Big Endian, the bytes and bits are arranged in their normal order as we read them, which means the MSB (most significant byte) comes first and the LSB (least significant byte) last. But in Little Endian, the thing is totally reversed. And it must be remembered that all bits are byte wise reversed, which means they are reversed in groups of 8. That's the rule for making segments of sizes 3 or 5 etc. If it's a long or int, then a htons() will do the job. Well, enough said, now let's make our packet.

char packet[65536];   //thats big!

IPV4_HDR *v4hdr=NULL;
TCP_HDR *tcphdr=NULL;
v4hdr = (IPV4_HDR *)packet; //lets point to the ip header portion

v4hdr->ip_version=4;
v4hdr->ip_header_len=5;
v4hdr->ip_tos = 0;
v4hdr->ip_total_length = htons ( sizeof(IPV4_HDR) + 
                                    sizeof(TCP_HDR) + payload );
v4hdr->ip_id = htons(2);
...............and so on

tcphdr = (TCP_HDR *)&buf[sizeof(IPV4_HDR)];
//get the pointer to the tcp header in the packet


tcphdr->source_port = htons(1234);
tcphdr->dest_port = htons(50000);

tcphdr->cwr=0;
tcphdr->ecn=1;
tcphdr->urg=0;
tcphdr->ack=0;
..................and so on

// Initialize the TCP payload to some rubbish

data = &buf[sizeof(IPV4_HDR) + sizeof(TCP_HDR)];
memset(data, '^', payload);

Get the remote host details in a sockaddr_in dest and call:

sendto(s , buf , sizeof(IPV4_HDR)+sizeof(TCP_HDR) + 
              payload, 0,(SOCKADDR *)&dest, sizeof(dest))

where payload is the size of the data after the TCP header. That's it! We are done.

To check whether the packets went out as you expected them to, use a sniffer like Ethereal and sniff them. Note: If you have any firewall running, then raw packets may be blocked.

Feel free to send your comments: prashant.pugalia@yahoo.co.in.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here