О Фирме
Загрузка программ и дополнительных модулей
Документация на программы информационной системы
Прайс лист
Партнеры фирмы
Форум
Бесплано распространяемое программное обеспечение, учебные материалы
Рейтинг@Mail.ru

Генератор пакетов на RAW-сокетах в Windows 2000/XP .

Фундаментальной единицей всего сетевого программирования практически во всех операционных системах является сокет. Так же, как функции файлового ввода-вывода определяют интерфейс взаимодействия с файловой системой, сокет соединяет программу с сетью. Существует несколько типов сокетов, однако чаще всего встречаются следующие три типа:

  • SOCK _ STREAM – обеспечивает надежный дуплексный протокол на основе установления логического соединения. Если говорит о семействе протоколов TCP / IP , которое сейчас получило самое широкое распространение, то это TCP .
  • SOCK _ DGRAM – обеспечивает надежный сервис доставки датаграмм. В рамках TCP / IP это будет протокол UDP .
  • SOCK _ RAW – предоставляет доступ практически ко всем служебным полям заголовков протоколов, таких как: IP , TCP , UDP и ICMP .

Первые два типа сокетов чаще всего применяются на практике. По работе с ними написано очень много статей и книг, поэтому на них подробно мы останавливаться не будем. В данной будет рассмотрено программирование именно третьего вида сокетов (низкоуровневых сокетов). Иногда бывает необходимо создать пакет, неважно какой – IP , TCP, UDP или ICMP , с заданными служебными полями, будь то для сканирования портов, прохождения через файрвол, определения операционной системы или просто проведения DOS -атаки. В этот момент к нам на помощь приходят низкоуровневые («сырые») сокеты. В данной статье мы рассмотрим основы работы с данным типом сокетов и в ходе статьи напишем генератор IP , TCP , UDP и ICMP пакетов. Предполагается что читатель знаком с языком программирования С, в частности, имеет представление о работе с простыми сокетами и основами стека протоколов TCP / IP . Для работы с RAW -сокетам, прежде всего, нам необходима библиотека WinSock 2.2. Далее рассмотрим основные структуры, используемые при написании генератора пакетов. Данная стpyктypа содеpжит IP адpес 4-ой веpсии

struct in_addr 
{
in_addr_t   s_addr;
}; 

Стpyктypа, содеpжащая адpеснyю инфоpмацию о сокете

struct sockaddr_in
{
u_int8_t       sin_len;
sa_family_t    sin_family;
in_port_t      sin_port;
struct in_addr sin_addr;
int8_t         sin_zero[8];
}; 

Далее коротко рассмотрим формат заголовков используемых протоколов, а также структуры, описывающие эти заголовки.

Протокол IP (RFC791).

Заголовок данного протокола описывается следующей структурой:

struct ip_header
{
unsigned char   version; // номер версии протокола 
unsigned char   tos;     // тип сервиса 
unsigned short  length;  // общая длина пакета 
unsigned short  id ;     // идентификатор пакета
unsigned short  flags;  // флаги 
unsigned char   ttl ;   // Время жизни пакета 
unsigned char   proto;  // Протокол верхнего уровня 
unsigned short  crc;    // CRC заголовка 
unsigned int    src_addr; // IP- адрес отправителя 
unsigned int    dst_addr; // IP- адрес получателя 
}; 

Протокол TCP ( RFC 793).

Заголовок данного протокола описывается следующей структурой:

struct tcp_header 
{
unsigned short   src_port;   // порт отправителя
unsigned short   dst_port;   // порт получателя 
unsigned int     seq_n;      // номер очереди 
unsigned int     ack _ n ;   // номер подтверждения 
unsigned char    offset;     // смещение 
unsigned char    flags;      // флаги 
unsigned short   win;        // окно 
unsigned short   crc ;       // контрольная сумма заголовка 
unsigned short   urg _ ptr ; // указатель срочности 
}; 

Протокол UDP ( RFC 768).

Заголовок данного протокола описывается следующей структурой:

struct udp_header
{
unsigned short   src _ port ; // номер порта отправителя 
unsigned short   dst _ port ; // номер порта получателя 
unsigned short   length; // длина датаграммы 
unsigned short   crc ;   // контрольная сумма заголовка
}; 

Протокол ICMP ( RFC 792).

Заголовок данного протокола описывается следующей структурой:

struct icmp_header 
{
unsigned char   type; // тип ICMP- пакета
unsigned char   code; // код ICMP- пакета 
unsigned short  crc ; // контрольная сумма 
unsigned long   orig_timestamp; // дополнительные поля 
unsigned long   recv_timestamp; // уточняющие тип 
unsigned long   trns_timestamp; //ICMP- пакета
}; 

Также необходимо ввести структуру псевдозаголовка, которая позволит вычислять контрольную сумму в TCP и UDP пакетах.

struct pseudo_header
{
unsigned int src_addr; // адрес отправителя 
unsigned int dst_addr; // адрес получателя 
unsigned char zero ; //начальная установка 
unsigned char proto; // протокол
unsigned short length; // длина заголовка 
}; 

Далее требуется вспомнить порядок байт при работе с сокетами. Сyществyют сетевой(network) и хостовый(host) поpядки байта. Hапpимеp адpеса источника и назначения, поpты источника и назначения должны быть в сетевом поpядке байта. Для пеpевода с одного поpядка байта использyются следyющие фyнкции:

htons() -- "Host to Network Short"

htonl() -- "Host to Network Long"

ntohs() -- "Network to Host Short"

ntohl() -- "Network to Host Long"

" h " - означает хост, " n " - сеть, " s " - тип short , " l " - тип long .

Hапpимеp для того чтобы пеpевести поpт назначения (25) из хостового поpядка байта в сетевой мы сделаем так:

htons(25);

Вы можете использовать эти фyнкции для пеpевода любого из полей TCP/IP заголовка в сетевой поpядок байта и обpатно, но для IP адpеса использyется дpyгая фyнкция. В слyчае если y Вас есть стpока типа: "192.168.1.1" и Вам необходимо пеpевести ее в сетевой поpядок байта, то использyем однy из следyющих фyнкций:

in_addr_t inet_addr(const char *cp); 
int inet_aton(const char *cp, struct in_addr *addr); 

Использование последней функции пpедпочтительней, посколькy inet_addr устарела.

Далее поговорим о подсчете контрольных сумм в генерируемых пакетах. Контрольную сумму в IP и ICMP пакетах считает следующая функция:

unsigned short rs_crc (unsigned short * buffer, int length)
{ 
unsigned long crc = 0;
// Вычисление CRC 
while (length > 1)
 { 
 crc += *buffer++; 
 length -= sizeof (unsigned short); 
 } 
if (length) crc += *(unsigned char*) buffer;
// Закончить вычисления 
crc = (crc >> 16) + (crc & 0xffff);
crc += (crc >> 16);
//Смещение CRC , если необходимо
if (1) crc = crc << 1; 
// Возвращаем инвертированное значение 
if (Fdata->crash_crc) 
   return ( unsigned short )( crc ); //Если указана опция исказить CRC , 
//то возвращаем не инвертированное значение CRC 
else return (unsigned short)(~crc); 
} 

Для подсчета CRC в TCP и UDP пакетах необходимо воспользоваться следующей функцией, которая сначала создает псевдозаголовок, а уже потом вычисляет контрольную сумму.

unsigned short rs_pseudo_crc(char *data, int data_length,unsigned int src_addr, 
     unsigned int dst_addr, int packet_length, unsigned char proto) 
{ 
char * buffer; 
unsigned int full_length; 
unsigned char header_length;
struct pseudo_header ph; 
unsigned short p_crc = 0;
// Заполнение структуры псевдозаголовка 
ph.src_addr = src_addr; 
ph.dst_addr = dst_addr; 
ph.zero = 0; 
ph.proto = proto;
ph.length = htons (packet_length);
header_length = sizeof (struct pseudo_header);
full_length = header_length + data_length; 
buffer =(char *) calloc (full_length, sizeof (char)); 
// Генерация псевдозаголовка 
memcpy (buffer, &ph, header_length); 
memcpy (buffer + header_length, data, data_length); 
// Вычисление CRC. 
p_crc = rs_crc ((unsigned short*) buffer, full_length);
free (buffer); 
return p_crc;
} 

Теперь когда мы имеем всю необходимую справочную информацию, можем приступать непосредственно к написанию нашего генератора пакетов.

Первым шагом будет инициализация библиотеки WinSock 2.2.

int rs_init (int v_major, int v_minor)
{ 
WSADATA wsadata;
// Инициализация WinSock заданной версии
if (WSAStartup(MAKEWORD(v_major, v_minor), &wsadata))
 { 
 ShowMessage(" Ошибка инициализаци WinSock"); 
 return 1;
 }
// Проверка версии WinSock 
if (LOBYTE(wsadata.wVersion) != v_minor || HIBYTE(wsadata.wVersion) != v_major)
 { 
 rs_exit (); 
 ShowMessage ("Неверная версия WinSock "); 
 return 1; 
 } 
return 0;
} 

Далее необходимо объявить структуры заголовков создаваемых пакетов. После этого производится заполнение заголовка IP пакета. На следующем шаге созданем RAW -сокет, и если нет вложений протоколов верхнего уровня в пакет IP , то отправляем его, используя следующую функцию:

int rs_send_ip (SOCKET s, struct ip_header iph, unsigned char * data, 
                     int data_length, unsigned short dst_port_raw)
{ 
char * buffer;
int result; 
sockaddr_in target; 
unsigned char header_length;
unsigned int packet_length;
memset (&target, 0, sizeof (target));
target.sin_family = AF_INET; 
target.sin_addr.s_addr = iph.dst_addr; 
target.sin_port = dst_port_raw; 
// Вычисление длины и заголовка пакета 
header_length = sizeof (struct ip_header); 
packet_length = header_length + data_length; 
// Установка CRC. 
iph.crc = 0; 
// Заполнение некоторых полей заголовка IP . 
iph.version = header_length / 4 + (unsigned char) atoi ("4") * 16;
// Если длина пакета не задана , то 
//длина пакета приравнивается к длине заголовка 
if (!iph.length) iph.length = htons (packet_length); 
buffer =(char *) calloc (packet_length, sizeof (char)); 
// Копирование заголовка пакета в буфер ( CRC равно 0). 
memcpy (buffer, &iph, sizeof (struct ip_header));
// Копирование данных в буфер 
if (data) memcpy (buffer + header_length, data, data_length); 
// Вычисление CRC. 
iph.crc = rs_crc ((unsigned short *) buffer, packet_length); 
// Копирование заголовка пакета в буфер ( CRC посчитана). 
memcpy (buffer, &iph, sizeof (struct ip_header)); 
// Отправка IP пакета в сеть.
result = sendto (s, buffer, packet_length, 0,(struct sockaddr *)
                 &target, sizeof (target));
free (buffer); 
return result;
} 

Если в IP пакет вложен пакет протокола более высокого уровня, то сначала вызываем функцию заполнения и отправки пакета протокола более высокого уровня, которая в свою очередь вызовет функцию генерации и отправки IP пакета в сеть, причем в качестве вложенных данных в IP пакет будет передан сформированный заголовок протокола более высокого уровня. Для генерации пакетов TCP , UDP и ICMP служат следующие функции, соответственно:

int rs_send_tcp (SOCKET s, struct ip_header iph, struct tcp_header tcph, 
                 unsigned char * data, int data_length)
  { 
  char * buffer; 
  int result; 
  unsigned char header_length; 
  unsigned int packet_length; 
  // вычисление длин пакета и заголовка.
  header_length = sizeof (struct tcp_header);
  packet_length = header_length + data_length; 
  // Установка CRC. 
  tcph.crc = 0; 
  // Установка поля offset .
  tcph.offset = (header_length / 4) << 4;
  buffer =(char *) calloc (packet_length, sizeof (char)); 
  // Копирование заголовка пакета в буфер ( CRC равно 0). 
  memcpy (buffer, &tcph, sizeof (struct tcp_header)); 
  // Копирование протокола более высокого уровня (данных) 
  if (data) memcpy (buffer + header_length, data, data_length); 
  // Вычисление CRC. 
  tcph.crc = rs_pseudo_crc (buffer, packet_length, iph.src_addr, 
                            iph.dst_addr, packet_length, IPPROTO_TCP); 
  // Копирование заголовка пакета в буфер ( CRC посчитано). 
  memcpy (buffer, &tcph, sizeof (struct tcp_header)); 
  // Посылка IP пакета (в качестве данных передан заголовок TCP ) 
  result = rs_send_ip (s, iph, buffer, packet_length, tcph.dst_port); 
  free (buffer);
  return result; 
  } 

 

int rs_send_udp (SOCKET s, struct ip_header iph, struct udp_header udph, 
                 unsigned char * data, int data_length)
{ 
char * buffer; 
int result; 
unsigned char header_length;
unsigned int packet_length; 
//вычисление длин пакета и заголовка. 
header_length = sizeof (struct udp_header); 
packet_length = header_length + data_length; 
// Установка CRC. 
udph.crc = 0;
// Если длина пакета не задана , то
//длина пакета приравнивается к длине заголовка
if (!udph.length) udph.length = htons (packet_length); 
buffer =(char *) calloc (packet_length, sizeof (char)); 
// Копирование заголовка пакета в буфер ( CRC равно 0). 
memcpy (buffer, &udph, sizeof (struct udp_header));
// Копирование протокола более высокого уровня (данных) 
if (data) memcpy (buffer + header_length, data, data_length); 
// Вычисление CRC. 
udph.crc = rs_pseudo_crc (buffer, packet_length, iph.src_addr,
                          iph.dst_addr, packet_length, IPPROTO_UDP); 
// Копирование заголовка пакета в буфер ( CRC посчитана). 
memcpy (buffer, &udph, sizeof (struct udp_header)); 
// Отправка IP пакета со вложенным UDP пакетом. 
result = rs_send_ip (s, iph, buffer, packet_length, udph.dst_port); 
free (buffer); 
return result; 
} 
 int rs_send_icmp (SOCKET s, struct ip_header iph, struct icmp_header icmph, 
                      unsigned char * data, int data_length)
{
char * buffer; 
int result; 
unsigned char header_length; 
unsigned int packet_length; 
data_length = 0; 
// вычисление длин пакета и заголовка.
header_length = sizeof (struct icmp_header); 
packet_length = header_length + data_length; 
icmph.crc = 0; 
buffer = (char *)calloc (packet_length, sizeof (char)); 
// Копирование заголовка пакета в буфер ( CRC равно 0).
memcpy (buffer, &icmph, sizeof (struct icmp_header)); 
// Вычисление CRC.
icmph.crc = rs_crc ((unsigned short *) buffer, packet_length); 
// Копирование заголовка пакета в буфер ( CRC посчитана). 
memcpy (buffer, &icmph, sizeof (struct icmp_header)); 
// Отправка IP пакета со вложенным ICMP пакетом.
result = rs_send_ip (s, iph, buffer, packet_length, 0);
free (buffer); 
return result; 
} 

В итоге написан полноценный генератор пакетов на чистых сокетах без использования никаких дополнительных библиотек, например, таких, как WinPcap . Однако стоит сказать, что для работы с RAW -сокетами, также как и для работы с WinPcap необходимы права администратора.

Полнeую версию рассмотренного генератора пакетов с исходным кодом можно скачать здесь (zip-архив 19 Kбайт). Данная программа написана на C ++ Builder 6, однако никто не запрещает переделать ее под любой другой компилятор.

В данной статье мы рассмотрели одну из возможностей RAW -сокетов – генерация пакетов различной структуры. В следующей статье попытаемся рассмотреть еще одну очень интересную возможность низкоуровневых сокетов – это перехват всех пакетов с сетевой карты.

О всех замеченных недостатках и ошибках, а также свои вопросы и пожелания пишите автору.