Raw сокеты WinSocket C++

В данной статье – примере мне хотелось бы показать работу с простыми(raw) сокетами. Данные сокеты позволяют получить доступ к базовому протоколу передачи данных, это дает нам большие возможности создания таких сетевых утилит как Ping, Traceroute, а также для организации IP Spoofing, DDoS, ICMP-flood, и многого еще =) Рассматривать будем только протокол IP, так как большинство других протоколов вообще не поддерживают простые сокеты, разве только ATM.
Поддержка данного типа сокетов начинается только в Windows Sockets 2-ой версии. Первое, что необходимо сделать для использования данного типа сокета – это его создание. Для этого запускаем Visual C++, далее создаем новый проект(«CTRL+N» или File->New), тип проекта «Win32 Console Application», и пишем следующий код:

#include <Winsock2.h>//Ws2_32.lib
#include <Windows.h>
#include <iostream.h>
 
void ShowError()
{
        LPVOID lpMsgBuf = NULL;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,WSAGetLastError(),
                MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0,NULL);
        cout<<(LPCTSTR)lpMsgBuf<<endl;
        LocalFree(lpMsgBuf);
}
 
int main()
{
        WSADATA wsaData;
        if(WSAStartup(0x0202,&wsaData)){ShowError();}
        else
        {
                cout<<"WSAStartup - OK"<<endl;
                SOCKET sckt;
                sckt = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
                if(sckt == INVALID_SOCKET){ShowError();}
                {
                        cout<<"Raw scoket is created"<<endl;
                        if(closesocket(sckt)== SOCKET_ERROR){ShowError();}
                }
                
                if(WSACleanup()){ShowError();}
                else{cout<<"WSACleanup - OK"<<endl;}
        }
        
        return 0;
}

Как видно из приведенного кода, для создания простого сокета достаточно указать вторым аргументом функции socket, протокол SOCK_RAW, но самое интересное представляет третий аргумент, в приведенной выше коде мы использовали IPPROTO_ICMP, но так же можно использовать IPPROTO_IGMP, IPPROTO_IP, IPPROTO_UDP, IPPROTO_RAW, IPPROTO_TCP, все эти параметры позволяют нам манипулировать заголовками представленных протоколов. При самостоятельном заполнение IP заголовка, необходимо добавить в код следующие строки:

BOOL opt = TRUE;
if(setsockopt (sckt,IPPROTO_IP,IP_HDRINCL,(char*)&opt,sizeof(opt))==SOCKET_ERROR)
{ShowError();}

Это позволит функции отправки(send, sendto) отправить заголовок IP перед посылаемыми данными, а функция приема вернет этот заголовок вместе с данными. На рисунке представленном ниже показан заголовок IP.

Raw сокеты  WinSocket C++

Теперь нам надо определить структуру, которая будет представлять IP заголовок:

typedef struct ip_hdr
{
        unsigned char    ip_verlen;
        unsigned char    ip_tos;
        unsigned short   ip_total_len;
        unsigned short   ip_id;
        unsigned short   ip_offset;
        unsigned char    ip_ttl;
        unsigned char    ip_protocol;
        unsigned short   ip_checksum;
        unsigned int     sourceIP;
        unsigned int     destIP;
}IP_HDR;

Для начальных опытов мы попробуем создать простой UDP сокет, так как он проще, его величина всего 8 байт, содержит 4 поля, более наглядно показано на рисунке ниже.
Raw сокеты  WinSocket C++

Определение структуры UDP заголовка.

typedef struct udp_hdr
{
        unsigned short   source_port;
        unsigned short   dest_port;
        unsigned short   udp_len;
        unsigned short   udp_sum;
}UDP_HDR;

Как Вы знаете протокол UDP ненадежный и не гарантирует доставку данных, значит вычисление контрольной суммы не обязательно, хотя и можно это сделать. Для этого используется псевдозаголовок представленный на рисунке:
Raw сокеты  WinSocket C++
Контрольная сумма UDP рассчитывается точно так же, как контрольная сумма IP заголовка, сумма 16-битных слов с переполнением. Если UDP датаграмма состоит из нечетного количества байт то необходимо в конце датаграммы добавить нулевые байты заполнения(который не передаются). Для расчета контрольной суммы в коде программы мы будем использовать следующею функцию, которая одинаковая для всех протоколов TCP/IP:

USHORT checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;
    while (size > 1){
        cksum += *buffer++;
        size  -= sizeof(USHORT);   
    }
    if (size){
        cksum += *(UCHAR*)buffer;   
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);
    return (USHORT)(~cksum);
}

Вот полный вариант программы:

#include <Winsock2.h>//Ws2_32.lib
#include <ws2tcpip.h>
//#include <Windows.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
/*****************************************************************/
void ShowError()
{
        LPVOID lpMsgBuf = NULL;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,WSAGetLastError(),
                MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0,NULL);
        CharToOem((char*)lpMsgBuf,(char*)lpMsgBuf);
        cout<<(LPCTSTR)lpMsgBuf<<endl;
        LocalFree(lpMsgBuf);
}
 
USHORT checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;
    while (size > 1){
        cksum += *buffer++;
        size  -= sizeof(USHORT);   
    }
    if (size){
        cksum += *(UCHAR*)buffer;   
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);
    return (USHORT)(~cksum);
}
/*****************************************************************/
typedef struct ip_hdr
{
        unsigned char    ip_verlen;
        unsigned char    ip_tos;
        unsigned short   ip_total_len;
        unsigned short   ip_id;
        unsigned short   ip_offset;
        unsigned char    ip_ttl;
        unsigned char    ip_protocol;
        unsigned short   ip_checksum;
        unsigned int     sourceIP;
        unsigned int     destIP;
}IP_HDR;
 
typedef struct udp_hdr
{
        unsigned short   source_port;
        unsigned short   dest_port;
        unsigned short   udp_len;
        unsigned short   udp_sum;
}UDP_HDR;
 
/*****************************************************************/
 
int main()
{
        WSADATA            wsaData;
        struct sockaddr_in remote;
        IP_HDR                 ipHdr;
        UDP_HDR                udpHdr;
        unsigned short     iTotalSize,iIPSize,iUdpSize, iUdpChecksumSize,ver;
        char                       buf[4096],*ptr,szMessage[4068];
        strcpy(szMessage,"Code by Lazy_elf");
 
 
        if(WSAStartup(0x0202,&wsaData)){ShowError();}
        else
        {
                cout<<"WSAStartup - OK"<<endl;
                SOCKET sckt;
                sckt = WSASocket (AF_INET,SOCK_RAW,IPPROTO_UDP,NULL,0,0);
                if(sckt == INVALID_SOCKET){ShowError();}
                {
                        cout<<"Raw scoket is created"<<endl;
 
                        BOOL opt = TRUE;
                        if(setsockopt (sckt,IPPROTO_IP,IP_HDRINCL,(char*)&opt,sizeof(opt))==SOCKET_ERROR)
                        {ShowError();}
                        else
                        {
                                cout<<"setsockopt - OK"<<endl;
                                iTotalSize = sizeof(ipHdr)+sizeof(udpHdr)+strlen(szMessage);
                                iIPSize    = sizeof(ipHdr)/sizeof(unsigned long);
                                ver = 4;
                                ipHdr.ip_verlen    = (ver<<4) | iIPSize;
                                ipHdr.ip_tos       = 0;
                                ipHdr.ip_total_len = htons(iTotalSize);
                                ipHdr.ip_id        = 0;
                                ipHdr.ip_offset    = 0;
                                ipHdr.ip_ttl       = 128;
                                ipHdr.ip_protocol  = IPPROTO_UDP;
                                ipHdr.ip_checksum  = 0;
                                ipHdr.sourceIP     = inet_addr("10.10.10.1");
                                ipHdr.destIP       = inet_addr("10.10.10.2");
                                //-------------------------------------------//
                                iUdpSize = sizeof(udpHdr)+strlen(szMessage);
                                udpHdr.source_port = htons(3004);
                                udpHdr.dest_port   = htons(4004);
                                udpHdr.udp_len     = htons(iUdpSize);
                                udpHdr.udp_sum     = 0;
                                //-------------------------------------------//
                                iUdpChecksumSize = 0;
                                ptr = buf;ZeroMemory(buf,4096);
                                memcpy(ptr,&ipHdr.sourceIP, sizeof(ipHdr.sourceIP));  
                                ptr              += sizeof(ipHdr.sourceIP);
                                iUdpChecksumSize += sizeof(ipHdr.sourceIP);
                                memcpy(ptr,&ipHdr.destIP,sizeof(ipHdr.destIP));
                                ptr              += sizeof(ipHdr.destIP);
                                iUdpChecksumSize += sizeof(ipHdr.destIP);
                                ptr++;iUdpChecksumSize += 1;
                            memcpy(ptr,&ipHdr.ip_protocol,sizeof(ipHdr.ip_protocol));
                                ptr              += sizeof(ipHdr.ip_protocol);
                                iUdpChecksumSize += sizeof(ipHdr.ip_protocol);
                                memcpy(ptr,&udpHdr.udp_len,sizeof(udpHdr.udp_len));
                                ptr              += sizeof(udpHdr.udp_len);
                                iUdpChecksumSize += sizeof(udpHdr.udp_len);
                        memcpy(ptr,&udpHdr,sizeof(udpHdr));
                                ptr              += sizeof(udpHdr);
                                iUdpChecksumSize += sizeof(udpHdr);
                                for(unsigned int i = 0; i < strlen(szMessage); i++, ptr++)
                                        *ptr = szMessage[i];
                                iUdpChecksumSize += strlen(szMessage);
                                udpHdr.udp_sum =  checksum((USHORT *)buf, iUdpChecksumSize);
                                
                                ZeroMemory(buf,4096);ptr = buf;
 
                                memcpy(ptr,&ipHdr, sizeof(ipHdr)); ptr += sizeof(ipHdr);
                                memcpy(ptr,&udpHdr,sizeof(udpHdr));ptr += sizeof(udpHdr);
                                memcpy(ptr,szMessage,strlen(szMessage));
 
                                
                                remote.sin_family      = AF_INET;
                                remote.sin_port        = htons(4004);
                                remote.sin_addr.s_addr = inet_addr("10.10.10.2");
                                if(sendto(sckt,buf,iTotalSize,0,(SOCKADDR *)&remote,sizeof(remote))==SOCKET_ERROR){ShowError();}
                                else{cout<<"sendto - OK"<<endl;}
                        }
 
                        if(closesocket(sckt)== SOCKET_ERROR){ShowError();}
                        else{cout<<"closesocket - OK"<<endl;}
                }
                
                if(WSACleanup()){ShowError();}
                else{cout<<"WSACleanup - OK"<<endl;}
        }
        return 0;
}

Raw сокеты  WinSocket C++

Raw сокеты WinSocket C++: 4 комментария

  1. Nick

    WireShark не детектирует отсылку этих пакетов

  2. pet

    А можно похожий пример только вместо udp вставить icmp?

  3. pet

    if(setsockopt (sckt,IPPROTO_IP,IP_HDRINCL,(char*)&opt,sizeof(opt))==SOCKET_ERROR)
    у меня вылетает по 10049

Комментарии запрещены.