Raw socket programming on windows with winsock

By | July 30, 2020

Raw sockets using winsock

Raw sockets or "Raw Packets", enable a program 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, they are discarded. But there are applications that need raw sockets.

Raw sockets are widely used in the field of network security for creating both security and insecurity!

Coding Raw Sockets

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.

The examples shown here would construct a raw tcp packet and send it over the network interface.

Winsock has limited raw socket support across various version of windows, as discussed later on. As an alternative you can create raw sockets on windows using the winpcap library as well.

IP and TCP Headers

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…………………..

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 some knowledge of socket programming, then the headers should be 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 Socket support on 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.

Operating systems like Linux have full raw socket support.
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 doesn’t have it.

A security patch called MS05-019 (http://support.microsoft.com/kb/897656) is what disables raw sockets on XP SP2 and can do the same to even SP1.

Probably Windows 2003 SP1 also implements the same the result being the end of raw sockets.

An indepth summary is available at http://seclists.org/nmap-hackers/2005/0005.html. 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. Winpcap can capture and send raw packets of all kinds.

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. http://technet.microsoft.com/hi-in/library/bb457156(en-us).aspx should tell more.

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 does not support raw sockets, then switch to Linux or use Winpcap. There are articles on this site that explain how to use winpcap to send raw packets. Search for them.

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.

Source Code :

//raw tcp packet crafter

#include "stdio.h"
#include "winsock2.h"
#include "ws2tcpip.h" //IP_HDRINCL is here
#include "conio.h"

#pragma comment(lib,"ws2_32.lib") //winsock 2.2 library

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;

int main()
{
char host[100],buf[1000],*data=NULL,source_ip[20]; //buf is the complete packet
SOCKET s;
int k=1;

IPV4_HDR *v4hdr=NULL;
TCP_HDR *tcphdr=NULL;

int payload=512 , optval;
SOCKADDR_IN dest;
hostent *server;

//Initialise Winsock
WSADATA wsock;
printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsock) != 0)
{
fprintf(stderr,"WSAStartup() failed");
exit(EXIT_FAILURE);
}
printf("Initialised successfully.");
////////////////////////////////////////////////

//Create Raw TCP Packet
printf("\nCreating Raw TCP Socket...");
if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==SOCKET_ERROR)
{
printf("Creation of raw socket failed.");
return 0;
}
printf("Raw TCP Socket Created successfully.");
////////////////////////////////////////////////

//Put Socket in RAW Mode.
printf("\nSetting the socket in RAW mode...");
if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval))==SOCKET_ERROR)
{
printf("failed to set socket in raw mode.");
return 0;
}
printf("Successful.");
////////////////////////////////////////////////

//Target Hostname
printf("\nEnter hostname : ");
gets(host);
printf("\nResolving Hostname...");
if((server=gethostbyname(host))==0)
{
printf("Unable to resolve.");
return 0;
}
dest.sin_family = AF_INET;
dest.sin_port = htons(50000); //your destination port
memcpy(&dest.sin_addr.s_addr,server->h_addr,server->h_length);
printf("Resolved.");
/////////////////////////////////////////////////

printf("\nEnter Source IP : ");
gets(source_ip);

v4hdr = (IPV4_HDR *)buf; //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);
v4hdr->ip_frag_offset = 0;
v4hdr->ip_frag_offset1 = 0;
v4hdr->ip_reserved_zero = 0;
v4hdr->ip_dont_fragment = 1;
v4hdr->ip_more_fragment = 0;
v4hdr->ip_ttl = 8;
v4hdr->ip_protocol = IPPROTO_TCP;
v4hdr->ip_srcaddr = inet_addr(source_ip);
v4hdr->ip_destaddr = inet_addr(inet_ntoa(dest.sin_addr));
v4hdr->ip_checksum = 0;

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;
tcphdr->psh=0;
tcphdr->rst=1;
tcphdr->syn=0;
tcphdr->fin=0;
tcphdr->ns=1;

tcphdr->checksum = 0;

// Initialize the TCP payload to some rubbish
data = &buf[sizeof(IPV4_HDR) + sizeof(TCP_HDR)];
memset(data, '^', payload);

printf("\nSending packet...\n");

while(!_kbhit())
{
printf(" %d packets send\r",k++);
if((sendto(s , buf , sizeof(IPV4_HDR)+sizeof(TCP_HDR) + payload, 0,
(SOCKADDR *)&dest, sizeof(dest)))==SOCKET_ERROR)
{

printf("Error sending Packet : %d",WSAGetLastError());
break;
}
}

return 0;
}

VC++ can be used to compile this code. Simply create a project and put this file in it and click run. Notice the while loop. In a loop this will send too many raw TCP packets to the target system. Modify the values according to need.

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

12 Comments

Raw socket programming on windows with winsock
  1. Gyanendar

    This sample doesnt run on windows 10. Though its not giving any error as such but no packet seen in wireshark.
    As per MSDN, RAW Socket with TCP is not supported .

    1. Silver Moon Post author

      you are correct, raw tcp sockets are not supported with the native winsock api.
      you need to use winpcap instead to construct and send tcp data over raw sockets

  2. Mothi

    I have tried this , but I did’nt get any ip data in that respective destination IP…can somenone helps regarding this issue

  3. Mothi

    i have tried this one but i couldnt get the ip packet in the respective Destination IP….can some one help me regarding this

  4. Anonymous

    And what about the fact that in the ip packet is initially field version, and only then the header size? In addition, the first two fields char structure, but they are in the specification should be 4 bits (two fields are equal to one char)? These must be packed in fragments through bitwise shift. On each line of 32 bits. This example is not workable.

  5. Anonymous

    Sorry I should have read the whole thing before I commented. Disregard my stupid comment. This blog is excellent.

    1. Paddy

      Yes, the RAW socket is limited to the paket layer (OSI 3). Under this Header there is still the Ethernet Header (Link Layer, OSI 2) to determine the Destination hardware address (MAC) and senders MAC. There is also a field to determine the type of the first paket. In the example above the first paket is the the IP-Header. This are the first data the raw socket do provide.

Leave a Reply

Your email address will not be published. Required fields are marked *