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.

Теперь нам надо определить структуру, которая будет представлять 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 поля, более наглядно показано на рисунке ниже.

Определение структуры UDP заголовка.
typedef struct udp_hdr
{
unsigned short source_port;
unsigned short dest_port;
unsigned short udp_len;
unsigned short udp_sum;
}UDP_HDR;
Как Вы знаете протокол UDP ненадежный и не гарантирует доставку данных, значит вычисление контрольной суммы не обязательно, хотя и можно это сделать. Для этого используется псевдозаголовок представленный на рисунке:

Контрольная сумма 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;
}

WireShark не детектирует отсылку этих пакетов
Вероятно данный метод уже устарел, статья писалась очень давно.
А можно похожий пример только вместо udp вставить icmp?
if(setsockopt (sckt,IPPROTO_IP,IP_HDRINCL,(char*)&opt,sizeof(opt))==SOCKET_ERROR)
у меня вылетает по 10049
Обсуждение закрыто.