Raw Sockets – Packets with Winpcap

A previous post mentions how to send raw packets using winsock api on windows xp. Winpcap is a packet driver useful for packet capturing and sending raw packets on the windows platform.

Raw means we have to cook the whole packet ourselves. A TCP packet for example consists of:
1. Ethernet header
2. IP header
3. TCP header
4. The data supposed to be send

Each header has its own job to do in the whole transmission process.

Code :

u_char packet[65536];
char *dump = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

Winpcap gives us one function called pcap_sendpacket() to throw the packet on the network adapter which forwards it. We have to responsibly construct the ethernet , ip and tcp headers and attach the data.

1. Ethernet header looks like


+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |       Ethernet destination address (first 32 bits)            |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | Ethernet dest (last 16 bits)  |Ethernet source (first 16 bits)|
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |       Ethernet source address (last 32 bits)                  |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |        Type code              |                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Ethernet destination address is the mac-address of the primary gateway of the network interface being used.
Ethernet source is the mac-address of the network interface itself.
Type field determines the type of the packet e.g. IP , ARP etc.

Now our first task is to get the source and destination mac address.
Winpcap gives the ip-addresses of all available network interfaces that can be used.

now if srcip has the source ip in in_addr or long format then we can get the mac-address of this ip address using the function


GetMacAddress(s_mac , srcip);
printf("Selected device has mac address : %.2X-%.2X-%.2X-%.2X-%.2X-%.2X",s_mac[0],s_mac[1],s_mac[2],s_mac[3],s_mac[4],s_mac[5]);

GetMacAddress is like :

void GetMacAddress(unsigned char *mac , in_addr destip) {
    DWORD ret;
    in_addr srcip;
    ULONG MacAddr[2];
    ULONG PhyAddrLen = 6;  /* default to length of six bytes */

    srcip.s_addr=0;

    //Now print the Mac address also
    ret = SendArp(destip , srcip , MacAddr , &PhyAddrLen);
    if(PhyAddrLen) {
        BYTE *bMacAddr = (BYTE *) & MacAddr;
        for (int i = 0; i < (int) PhyAddrLen; i++)
            mac[i] = (char)bMacAddr[i];
    }
}

SendArp is the method that is used to retrieve the “mac-address of a IP” ;simple!
The above demonstration is mostly self-explaining. We got the mac-address of the network interface we want to use. Next we need the IP address of the primary gateway of this interface and then it mac-address.

GetGateway gets the gateway :

void GetGateway(struct in_addr ip , char *sgatewayip , int *gatewayip) {
    char pAdapterInfo[5000];
    PIP_ADAPTER_INFO  AdapterInfo;
    ULONG OutBufLen = sizeof(pAdapterInfo) ;

    GetAdaptersInfo((PIP_ADAPTER_INFO) pAdapterInfo, &OutBufLen);
    for(AdapterInfo = (PIP_ADAPTER_INFO)pAdapterInfo; AdapterInfo ; AdapterInfo = AdapterInfo->Next) {
        if(ip.s_addr == inet_addr(AdapterInfo->IpAddressList.IpAddress.String))
     strcpy(sgatewayip , AdapterInfo->GatewayList.IpAddress.String);
    }
    *gatewayip = inet_addr(sgatewayip);
}

GetAdaptersInfo is the function that retrieves a lot of information about a adapter.
This and SendArp are inside iphlpapi.dll ; IP helper api which we shall load and get the function pointers inside!

Buzz!

void loadiphlpapi() {
    HINSTANCE hDll = LoadLibrary("iphlpapi.dll");

    GetAdaptersInfo = (pgetadaptersinfo)GetProcAddress(hDll,"GetAdaptersInfo");
    if(GetAdaptersInfo==NULL)
        printf("Error in iphlpapi.dll%d",GetLastError());
    SendArp = (psendarp)GetProcAddress(hDll,"SendARP");
    if(SendArp==NULL)
 printf("Error in iphlpapi.dll%d",GetLastError());
}

So by now the source-ip , its mac-address , primary-gateway-mac should be into their respective variables. So now we have enough information to build our ethernet header.

Enjoy!

ETHER_HDR *ehdr;
    memcpy(ehdr->source , s_mac , 6); //Source Mac address
    memcpy(ehdr->dest,d_mac,6); //Destination MAC address
    ehdr->type = htons(0x0800); //IP Frames

Next comes the IP Header

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    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

and our structure for this header :

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 , IPHeader;

and then the TCP Header

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 our structure for this header :

// 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;

Now the headers can be build easily :

// *******************  IP Header *****************
    iphdr = (PIPV4_HDR)(packet + sizeof(ETHER_HDR));

    iphdr->ip_version = 4;
    iphdr->ip_header_len = 5; //In double words thats 4 bytes
    iphdr->ip_tos = 0;
    iphdr->ip_total_length = htons (sizeof(IPV4_HDR) + sizeof(TCP_HDR) + strlen(dump));
    iphdr->ip_id = htons(2);
    iphdr->ip_frag_offset = 0;
    iphdr->ip_reserved_zero=0;
    iphdr->ip_dont_fragment=1;
    iphdr->ip_more_fragment=0;
    iphdr->ip_frag_offset1 = 0;
    iphdr->ip_ttl    = 3;
    iphdr->ip_protocol = IPPROTO_TCP;
    iphdr->ip_srcaddr  = inet_addr("1.2.3.4");   //srcip.s_addr;
    iphdr->ip_destaddr = inet_addr("1.2.3.5");
    iphdr->ip_checksum =0;
    iphdr->ip_checksum = in_checksum((unsigned short*)iphdr, sizeof(IPV4_HDR));

    // *******************  TCP Header *****************
    tcphdr = (PTCP_HDR)(packet + sizeof(ETHER_HDR) + sizeof(IPV4_HDR));

    tcphdr->source_port = htons(SOURCE_PORT);
    tcphdr->dest_port = htons(80);
    tcphdr->sequence=0;
    tcphdr->acknowledge=0;
    tcphdr->reserved_part1=0;
    tcphdr->data_offset=5;
    tcphdr->fin=0;
    tcphdr->syn=1;
    tcphdr->rst=0;
    tcphdr->psh=0;
    tcphdr->ack=0;
    tcphdr->urg=0;
    tcphdr->ecn=0;
    tcphdr->cwr=0;
    tcphdr->window = htons(64240);
    tcphdr->checksum=0;
    tcphdr->urgent_pointer = 0;

almost done

data = (char*)(packet + sizeof(ETHER_HDR) + sizeof(IPV4_HDR) + sizeof(TCP_HDR));
strcpy(data,dump);

pcap_sendpacket(fp , packet , sizeof(ETHER_HDR) + sizeof(IPV4_HDR) + sizeof(TCP_HDR) + strlen(dump));

Thats should send the packet. Use Ethereal to check whether the packet was successfully transmitted. The above was an example of a TCP packet, similarly UDP ICMP or any other packet can be build.

Source Code :
Download

Popularity: 9% [?]

7 Responses to “Raw Sockets – Packets with Winpcap”

  1. what does the eth_hdr struct look like?

  2. typedef struct ethernet_header
    {
    UCHAR dest[6];
    UCHAR source[6];
    USHORT type;
    } ETHER_HDR , *PETHER_HDR , FAR * LPETHER_HDR , ETHERHeader;

  3. I can’t believe how easy you made this look. You should write some networking tutorials. Thanks heaps for this guide it’s amazing.

  4. Hmm maybe I can use this to make a nice winpcap class. Thanks again.

  5. thanks alot :D i’ve been looking for something like this for a long time :P :)

  6. It says its sending, but nothing is picked up by wireshark…

  7. Hi, I try to get your full source code, but I can’t open the link that you gave

Leave a Reply