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.
- Windows 95, 98, 98SE, NT4.0 -- Only raw ICMP and IGMP with restricted features.
- Windows 2000, XP, XP SP1, 2003 -- Full raw socket support for both receiving and sending purposes.
- 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);
int optval=1;
setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&optval,
sizeof optval);
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;
unsigned char ip_version :4;
unsigned char ip_tos;
unsigned short ip_total_length;
unsigned short ip_id;
unsigned char ip_frag_offset :5;
unsigned char ip_more_fragment :1;
unsigned char ip_dont_fragment :1;
unsigned char ip_reserved_zero :1;
unsigned char ip_frag_offset1;
unsigned char ip_ttl;
unsigned char ip_protocol;
unsigned short ip_checksum;
unsigned int ip_srcaddr;
unsigned int ip_destaddr;
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;
typedef struct tcp_header
{
unsigned short source_port;
unsigned short dest_port;
unsigned int sequence;
unsigned int acknowledge;
unsigned char ns :1;
unsigned char reserved_part1:3;
unsigned char data_offset:4;
unsigned char fin :1;
unsigned char syn :1;
unsigned char rst :1;
unsigned char psh :1;
unsigned char ack :1;
unsigned char urg :1;
unsigned char ecn :1;
unsigned char cwr :1;
unsigned short window;
unsigned short checksum;
unsigned short 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];
IPV4_HDR *v4hdr=NULL;
TCP_HDR *tcphdr=NULL;
v4hdr = (IPV4_HDR *)packet;
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)];
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
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.