Делать было нечего, кроме таго ничего не хотелось делать. Решил прочитать журнальчик. Звать журнальчик - ][akep . В одной статье описывалась прога Essential NetTools. Оооочень ничего, но можно и лутше - подумал я. И принялся за работу... Уже написал многие состовляющии (снифер устройсв, сканер портов, снифер адресов адресов...) Пишу на Дельфи (за это особо не ругайте , решения на других языках будут подключаться при помощи DLL).
Кто имел опыт разработки подобных програм и кому интиресно - прошу постить!
p.s. с названием я еще не определиля, у каго есть предложения - пишите!
mindw0rk Я тебе прислал ссылку, видя что тебе руки приложить некуда, а тут можно приложить и денег заработать. Больше ничего писать не буду. Если у тебя возникнет интерес, то ты по пойдешь по ссылке и сам там все сделаешь.
type USHORT = WORD; ULONG = DWORD; time_t = Longint;
// ip заголовок // Более подробно в RFC 791 // http://rtfm.vn.ua/inet/prot/rfc791r.html TIPHeader = packed record iph_verlen: UCHAR; // версия и длина заголовка iph_tos: UCHAR; // тип сервиса iph_length: USHORT; // длина всего пакета iph_id: USHORT; // Идентификация iph_offset: USHORT; // флаги и смещения iph_ttl: UCHAR; // время жизни пакета iph_protocol: UCHAR; // протокол iph_xsum: USHORT; // контрольная сумма iph_src: ULONG; // IP-адрес отправителя iph_dest: ULONG; // IP-адрес назначения end; PIPHeader = ^TIPHeader;
// tcp заголовок // Более подробно в RFC 793 // http://rtfm.vn.ua/inet/prot/rfc793r.html TTCPHeader = packed record sourcePort: USHORT; // порт отправителя destinationPort: USHORT; // порт назначения sequenceNumber: ULONG; // номер последовательности acknowledgeNumber: ULONG; // номер подтверждения dataoffset: UCHAR; // смещение на область данных flags: UCHAR; // флаги windows: USHORT; // размер окна checksum: USHORT; // контрольная сумма urgentPointer: USHORT; // срочность end; PTCPHeader = ^TTCPHeader;
// udp заголовок // Более подробно в RFC 768 // http://rtfm.vn.ua/inet/prot/rfc768r.html TUDPHeader = packed record sourcePort: USHORT; // порт отправителя destinationPort: USHORT; // порт назначения len: USHORT; // длина пакета checksum: USHORT; // контрольная сумма end; PUDPHeader = ^TUDPHeader;
// ICMP заголовок // Более подробно в RFC 792 // http://rtfm.vn.ua/inet/prot/rfc792r.html TICMPHeader = packed record IcmpType : BYTE; // Тип пакета IcmpCode : BYTE; // Код пакета IcmpChecksum : WORD; IcmpId : WORD; IcmpSeq : WORD; IcmpTimestamp : DWORD; end; PICMPHeader = ^TICMPHeader;
// Структуры для выполнения GetAdaptersInfo IP_ADDRESS_STRING = record S: array [0..15] of Char; end; IP_MASK_STRING = IP_ADDRESS_STRING; PIP_MASK_STRING = ^IP_MASK_STRING;
//Функция преобразвывающся введённый адрес сервера в спец формат //Если введено символьное имя, то она преобразовывает его сначала в IP адрес, //а потом переводит в спец формат function TForm1.LookupName: TInAddr; var HostEnt: PHostEnt; InAddr: TInAddr; begin if Pos('.', AddressEdit.Text)>0 then InAddr.s_addr := inet_addr(PChar(AddressEdit.Text)) else begin HostEnt := gethostbyname(PChar(AddressEdit.Text)); FillChar(InAddr, SizeOf(InAddr), 0); if HostEnt <> nil then begin with InAddr, HostEnt^ do begin S_un_b.s_b1 := h_addr^[0]; S_un_b.s_b2 := h_addr^[1]; S_un_b.s_b3 := h_addr^[2]; S_un_b.s_b4 := h_addr^[3]; end; end end; Result := InAddr; end;
function GetLocalIP: string; var WSAData: TWSAData; Host: PHostEnt; Buf: array [0..127] of Char; begin Result := ''; if WSAStartup($101, WSAData) = 0 then begin if GetHostName(@Buf, 128) = 0 then begin Host := GetHostByName(@Buf); if Host <> nil then Result := iNet_ntoa(PInAddr(Host^.h_addr_list^)^); end; WSACleanup; end; end;
function TSnifferThread.InitSocket: Boolean; var PromiscuousMode: Integer; begin // инициализируем WinSock Result := WSAStartup($202, WSA) = NOERROR; if not Result then begin LogData := 'Ошибка: ' + SysErrorMessage(WSAGetLastError); Synchronize(ShowPacket); Exit; end; // создаем сокет hSocket := socket(AF_INET, SOCK_RAW, IPPROTO_IP); if hSocket = INVALID_SOCKET then begin DeInitSocket(WSAGetLastError); Exit; end; FillChar(Addr_in, SizeOf(sockaddr_in), 0); Addr_in.sin_family:= AF_INET; // указываем за каким интерфейсом будем следить Addr_in.sin_addr.s_addr := inet_addr(PChar(Host)); // связываем сокет с локальным адресом if bind(hSocket, Addr_in, SizeOf(sockaddr_in)) <> 0 then begin DeInitSocket(WSAGetLastError); Exit; end; // Переключаем интерфейс на прием всех пакетов проходящих через интерфейс - promiscuous mode. PromiscuousMode := 1; if ioctlsocket(hSocket, $98000001, PromiscuousMode) <> 0 then begin DeInitSocket(WSAGetLastError); Exit; end; Result := True; end;
procedure TSnifferThread.DeInitSocket(const ExitCode: Integer); begin // Если была ошибка - выводим ее if ExitCode <> 0 then begin LogData := 'Ошибка: ' + SysErrorMessage(ExitCode); Synchronize(ShowPacket); end; // Закрываем сокет if hSocket <> INVALID_SOCKET then closesocket(hSocket); // Деинициализируем WinSock WSACleanup; end;
procedure TSnifferThread.Execute; var PacketSize: Integer; begin // Производим инициализацию if InitSocket then try // Крутим поток до упора while not Terminated do begin // Ждем получения пакета (блокирующий режим) PacketSize := recv(hSocket, Packet, $10000, 0); // Если есть данные - производим их разбор if PacketSize > SizeOf(TIPHeader) then ParcePacket(PacketSize); end; finally // В конце освобождаем занятые ресурсы DeInitSocket(NO_ERROR); end; end;
procedure TSnifferThread.ParcePacket(const PacketSize: Word); var IPHeader: TIPHeader; TCPHeader: TTCPHeader; SrcPort, DestPort: Word; I, Octets, PartOctets: Integer; PacketType, DumpData, ExtendedInfo: String; Addr, A, B: TInAddr; begin Move(Packet[0], IPHeader, IPHeaderSize); SrcPort := 0; DestPort := 0; ExtendedInfo := '';
// определяем тип протокола if IPHeader.iph_protocol = IPPROTO_TCP then begin PacketType := 'TCP'; // Читаем ТСР заголовок Move(Packet[IPHeaderSize], TCPHeader, TCPHeaderSize); // Смотрим порт отправителя и получателя SrcPort := TCPHeader.sourcePort; DestPort := TCPHeader.destinationPort;
// Пишем IP адрес отправителя с портом Addr.S_addr := IPHeader.iph_src; LogData := inet_ntoa(Addr);
// Выводим все что напарсерили в Memo Synchronize(ShowPacket); end; end;
procedure TForm1.Button1Click(Sender: TObject); var i,j,s, opt, index: Integer; FSocket: array [0..41] of TSOCKET; //Массив сокетов busy : array [0..41] of boolean; //Массив, в котором будет храниться информация о каждом сканируемом сокете port : array [0..41] of integer; //Массив сканируемых портов addr : TSockAddr; hEvent : THandle; //Объект для обработки сетевых событий fset : TFDset; tv : TTimeval; tec :PServEnt; PName:String; GInitData : TWSADATA; begin // Устанавливаю максимальное и минимальное значение полоски состояния сканирования //Я устанавливаю в минимум начальный порт сканирования, а в максимум - конечный порт ProgressBar1.Min:=StrToInt(StartPortEdit.Text); ProgressBar1.Max:=StrToInt(EndPortEdit.Text);
//Записываю в переменную i значение начального порта i:=StrToInt(StartPortEdit.Text);
//Заполняю основные поля структуры addr, которая будет использоваться //при вызове функции connect addr.sin_family := AF_INET; addr.sin_addr.s_addr := INADDR_ANY;
//Вывожу сообщение о том, что начат поиск введённого хоста DisplayMemo.SelAttributes.Color:=clTeal; DisplayMemo.SelAttributes.Style:=DisplayMemo.SelAttributes.Style+[fsBold]; DisplayMemo.Lines.Add('Поиск хоста');
//LookupName - эта функция написана выше и она возвращяет адрес в спец формате указанного сервера //Результат этой функции я записываю в поле адреса сервера структуры addr addr.sin_addr := LookupName;
//Вывожу сообщение о том, что начато сканирование DisplayMemo.SelAttributes.Color:=clTeal; DisplayMemo.SelAttributes.Style:=DisplayMemo.SelAttributes.Style+[fsBold]; DisplayMemo.Lines.Add('Сканирование...');
//В index находиться количество сокетов проверяемых за один раз index:=40;
//Создаю объект для обработки сетевых событий hEvent := WSACreateEvent(); while i<StrToInt(EndPortEdit.Text) do begin ///Всем элементам массива busy присваиваю значение false for j:=0 to index do busy[j]:=false;
//В этом цикле будут асинхронно посылаться запросы на моединение //переменная j будет изменяться от 0 до максимального количества //элементов в массиве for j:=0 to index do begin //Если j-й порт превысил значение указанного максимального //порта, то прервать цикл if i>StrToInt(EndPortEdit.Text) then begin index:=j-1; break; end;
//Инициализирую очередной j-й сокет из массива FSocket FSocket[j] := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
//Добавляю j-й сокет к объекту событий с помощью WSAEventSelect //1-й параметр - Добавляемый сокет //2-й параметр - объект событий, который был создан с помощью WSACreateEvent //3-й параметр - какие события ожидать. Тут я указываю FD_WRITE - события записи и FD_CONNECT - события о заключении соединения WSAEventSelect(FSocket[j], hEvent, FD_WRITE + FD_CONNECT);
//Указываем порт, на который надо произвести попытку соединения addr.sin_port := htons(i);
//Попытка коннекта на очередной порт connect(FSocket[j], addr, sizeof(addr));
//Даём ОС поработать и обработать накопившиеся события. //Если этого не делать, то вовремя сканирования будет //происходить эффект зависания Application.ProcessMessages;
//Проверяю, были ли ошибки. if WSAGetLastError()=WSAEINPROGRESS then begin //Если ошибка произошла, то закрываю этот порт closesocket (FSocket[j]); //Устанавливаю соответствующий элемент в массиве busy в true //чтобы потом не проверять этот порт, потому что он всё равно //уже закрыт busy[j]:=true; end;
//Указываю в массиве port, на какой именно порт мы сейчас послали запрос port[j]:=i;
//Увеличиваю счётчик i в котором я отслеживаю, какой порт сейчас сканируеться //чтобы на следующем этапе цикла for запустить сканирование следующего порта i:=i+1; end;
//Обнуляю переменную fset FD_Zero(fset);
//Заполняю сканируемый массив сокетов в переменную fset for j := 0 to index do begin if busy[j] <> true then FD_SET (FSocket[j], fset); end;
//Даём ОС поработать и обработать накопившиеся события. Application.ProcessMessages;
//Заполняю структуру, в которой указано время ожидания события от сокета tv.tv_sec := 1; //Мы будем ждать 1 секунду tv.tv_usec := 0;
//Ожидаем пока произойдёт хотя бы одно событие от любого из сокетов s:=select (1, nil, @fset, nil, @tv);
//Даём ОС поработать и обработать накопившиеся события. Application.ProcessMessages;
//Запускаю массив, в котором будет проверятся, какие из сокетов в массиве FSocket //прошли коннект успешно, а какие нет. for j := 0 to index do begin //Проверяем, был ли закрыт соответствующий порт из-за ошибки //Если да, то нет смысла его проверять if busy[j] then continue;
if FD_ISSET (FSocket[j], fset) then begin //В переменную s записываеться размер перменной Opt s:=Sizeof(Opt); opt:=1; //Получаю состояние текущего j-го сокета //результат состояния будет в переменной opt getsockopt(FSocket[j], SOL_SOCKET, SO_ERROR, @opt, s);
//Если opt равно 0 то порт открыт и к нему можно подключится if opt=0 then begin //Пытаюсь узнать символьное имя порта tec := getservbyport(htons(Port[j]),'TCP'); if tec=nil then PName:='Unknown' else begin PName:=tec.s_name; end; //Вывожу сообщение об открытом порте DisplayMemo.Lines.Add('Хост:'+AddressEdit.Text+': порт :'+IntToStr(Port[j])+' ('+Pname+') '+' открыт '); end; end; //Закрыть j-й сокет, потому что он больше уже не нужен closesocket(FSocket[j]); end; //Увеличивею позицию в ProgressBar1 ProgressBar1.Position:=i; end; //Закрываю объект событий WSACloseEvent(hEvent);
//Вывожу сообщение о конце сканирования DisplayMemo.SelAttributes.Color:=clTeal; DisplayMemo.SelAttributes.Style:=DisplayMemo.SelAttributes.Style+[fsBold]; DisplayMemo.Lines.Add('Сканирование закончено...'); ProgressBar1.Position:=0; end;
// Читаем все IP адреса со всех присутствующих // в системе сетевых интерфейсов procedure TForm1.ReadLanInterfaces; var InterfaceInfo, TmpPointer: PIP_ADAPTER_INFO; IP: PIP_ADDR_STRING; Len: ULONG; begin // Смотрим сколько памяти нам требуется? if GetAdaptersInfo(nil, Len) = ERROR_BUFFER_OVERFLOW then begin // Берем нужное кол-во GetMem(InterfaceInfo, Len); try // выполнение функции if GetAdaptersInfo(InterfaceInfo, Len) = ERROR_SUCCESS then begin // Перечисляем все сетевые интерфейсы TmpPointer := InterfaceInfo; repeat // перечисляем все IP адреса каждого интерфейса IP := @TmpPointer.IpAddressList; repeat cbInterfaces.Items.Add(Format('%s - [%s]', [IP^.IpAddress.S, TmpPointer.Description])); IP := IP.Next; until IP = nil; TmpPointer := TmpPointer.Next; until TmpPointer = nil; end; finally // Освобождаем занятую память FreeMem(InterfaceInfo); end; end; // Смотрим - можем ли мы продолжать работу программы? if cbInterfaces.Items.Count = 0 then begin memReport.Text := 'Снифер сетевых устройств.' + sLineBreak + 'Сетевые интерфейсы не обнаружены.'; btnStartStop.Enabled := False; Exit; end else cbInterfaces.ItemIndex := 0; end;
// Запуск остановка потока procedure TForm1.btnStartStopClick(Sender: TObject); begin if FSnifferThread <> nil then begin FSnifferThread.Terminate; FSnifferThread := nil; btnStartStop.Caption := 'Start'; end else begin FSnifferThread := TSnifferThread.Create(True); FSnifferThread.Host := Copy(cbInterfaces.Text, 1, Pos(' ', cbInterfaces.Text)); FSnifferThread.FreeOnTerminate := True; FSnifferThread.Resume; btnStartStop.Caption := 'Stop'; end; end; //+++++++++++++++++++++++++++++++++++++++++++//
procedure TForm1.Button3Click(Sender: TObject); begin f3.Visible:=False; f1.Visible:=False; f2.Visible:=True; end;
procedure TForm1.Button2Click(Sender: TObject); begin f3.Visible:=False; f2.Visible:=False; f1.Visible:=True; end;
procedure TForm1.CloseButtonClick(Sender: TObject); begin close; end;
procedure TForm1.f3ButtonClick(Sender: TObject); begin if FSnifferThread <> nil then begin FSnifferThread.Terminate; FSnifferThread := nil; f3Button.Caption := '>>> Start >>>'; end else begin FSnifferThread := TSnifferThread.Create(True); FSnifferThread.Host := f3Edit.Text; FSnifferThread.FreeOnTerminate := True; FSnifferThread.Resume; f3Button.Caption := '*** Stop ***'; end; end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin if FSnifferThread <> nil then begin FSnifferThread.Terminate; FSnifferThread := nil; end; end;
procedure TForm1.Button4Click(Sender: TObject); begin f3.Visible:=True; f2.Visible:=False; f1.Visible:=False; end;
Ну мне, интересно, что ты сделал. И вообще, что там как, и в чём фишка. Не с нуля ведь начинал, так? Ну вот, а чтобы весь код не лопатить, любопытствую только о твоей работе.