Я думаю, ты знаешь что такое sniffer и для чего он нужен. Скорей всего в твоем арсенале уже существует пару тварей с этим гордым названием. Но настоящий Х-мэн должен сам уметь создать своего боевого друга. Сегодня тебе предстоит познакомится с основами этого нелегкого труда.
Для тех, кто только проснулся сообщаю: пакетный сниффер - это программа, которая принимает и сохраняет пакеты из сети, причем не всегда адресованные данному интерфейсу. Хорошо звучит? Очень сильно отдает твоими любимыми средствами получения информации - хороший троян, соколиный глаз, большое ухо, горячий утюг :). И вот именно этого монстра нам предстоит сегодня создать собственными руками.
Любая работа с сетью - сидение в чате, заливка патчей для M$-WC - есть обмен информацией. Она пакуется в пакеты и если надо, то фрагментируется (born to frag, хехе...). К пакетам цепляется заголовок, содержащий сведения для его доставки и отправляются по IP адресу. Нет, конечно есть еще маска подсети (255.255.255.0), нужная для отделения адреса компа от адреса сети. Но это на сегодня не колышет.
В Win9x отправкой и приемом данных занимается Winsock. В первой версии этой библы вообще не было ничего для прямой работы с пакетами, а про прямой доступ к сетевухе MS вообще забыла. Ну что поделаешь, если у их программеров склероз. Во второй версии уже появились функции для работы с пакетами. Но ты мне не поверишь, но они не работают :). Ну нельзя сказать, что совсем, но работают не так как нам хочется.
На первый взгляд все безнадежно и написать sniffer без прямого доступа к сетевухе или пакетам мы не сможем. Но это только первый взгляд, потому что мы можем обойти MS и все же получить прямой доступ к сетевухе. Для этого есть уже готовая библиотека packet.dll (ее, кстати, юзает известный "DSniff"). Вот и мы ее сегодня поюзаем. В ней уже есть все необходимые функции для работы с пакетами, самые нужные из которых я сейчас и опишу в качестве курса лекций :)
Эта функция возвращает список доступных сетевых адаптеров. Функция возвращает TRUE, если все путем, FALSE - если произошла ошибка.
PAdapterDescs - после вызова, сюда запишутся имена и адреса доступных адаптеров. Ты должен сюда передать массив из структур типа ADAPTER_DESC. Его длинну можешь установить в 4, потому что в среднестатистическом компе не бывает больше 2-3 адаптеров.
NAdapterDescs- длинна массива.
pnAdapterDescsMax- сюда запишут, сколько адаптеров реально установлено в компе.
function PacketOpenAdapter(
AdapterName: LPSTR
): DWORD; stdcall;
Открываем инсталлированный адаптер. В качестве параметра нужно указать имя, которое ты получил после вызова PacketGetAdapterNames. Функция возвращает указатель на адаптер.
function PacketAllocatePacket(
AdapterObject: LPADAPTER
): DWORD; stdcall;
Распределение пакета. AdapterObject - указатель на адаптер, который ты получил после вызова PacketOpenAdapter. Функция возвращает указатель на подготовленный пакет.
Sync - режим получения пакета (синхронный или асинхронны).
BytesReceived - сколько байт получили (это архиважно для идентификации приложения, пославшего пакет).
Если ты будешь использовать асинхронный режим, то тебе придется еще пользоваться функцией PacketWaitPacket для ожидания пакета. Для нас это лишнее усложнее задачи, поэтому я этим гемором заниматься не буду.
С основными функциями покончено. Этого достаточно для написания простейшего сниффера. Как видишь, ни в одной из функций нет указаний на IP адреса или порт. Как ты думаешь, почему? Как же тогда отправляются пакеты? Да очень просто. Мы здесь работаем на самом низком из возможного сетевом уровне, а здесь порты не хляют. Все это ты должен ручками обрабатывать в структуре LPPACKET.
Теперь перейдем к примеру, в процессе которого я покажу тебе еще, как определить MAC-адрес и как установить фильтр для входящих пакетов.
Запускай Delphi. Брось на форму 4 компонента TLabel. Два компонента Tedit и две кнопки. Постарайся сделать это, как на рисунке 1.
Рисунок 1
Теперь дадим названия всем этим компонентам:
Label1: Свойство Caption='Всего байт'.
Label2: Свойство Caption='0'.
Label3: Свойство Caption='Файл лога'.
Label4: Свойство Caption='Исп. Адаптер'.
Button1: Свойство Caption='START!'.
Button2: Свойство Caption='STOP'.
Все, форма готова. Если ты все правильно делал, то у тебя получится нечто похожее на рисунок 2.
Рисунок 2
Теперь можно переходить к кодингу. Объяви в разделе private переменные, указанные во врезке 1 "Объявление переменных". Все эти переменные понадобятся нам во время кодинга.
Как только все объявишь, создай обработчик события для формы OnCreate и напиши там текст, указанный во врезке 2 "Обработчик события OnCreate"
Теперь создай обработчик события OnClick для кнопки Button1. Там напиши все, что указано во врезке 3 "Обработчик OnClick".
Пример готов, можешь его компилировать. Более полный вариант исходника ты можешь забрать у меня на сайте. Здесь я привел необходимый минимум, непосредственной работы с сетью. Все, что касается оформления я опустил, чтобы не забивать место журнала. Если ты давно уже кодишь на Delphi, то без проблем и сам улучшишь этот маленький снифер. Ну а если ты начинающий, то бегом ко мне на сайт.
Рисунок 3
Я запустил свой снифер и одновременно пингонул соседную машину. После пинга я посмотрел в файл лога, созданный снифером. Ты тоже можешь увидеть этот лог на рисунке 4. Честно сказать, разобраться в чем-то здесь очень тяжело, но иногда это единственный способ.
Примерчик получился простейший, и его еще можно очень сильно улучшить. Например, опрос сетевухи лучше убрать в отдельный поток, что улучшит скорость опроса и прога не будет выглядеть тормознутой.
После появления рубрики "Кодинг", ко мне повалили письма с просьбой добавить в нее описания сетевых прог. Возможно твоя мечта когда-нибудь сбудется. Но главное - начало этому положено.
Исходники примера, как всегда, ты можешь взять с моего сайта www.cysoft.com/vr-online/. Там же есть и необходимые заголовочные файлы. Только одно предупреждение - пример работает только в Win9x. В NT и тем более в win2000 прога будет матерится, как прапорщик.
Итак, мне осталось только подвести итог. Мы написали простейший снифер с использованием Борман Delphi. И пусть теперь кто-нибудь скажет, что на Delphi это не возможно. Возможно все, надо только постараться.
P.S. Совсем недавно мне посчастливилось познакомится с совершенно новым типом сниферов. Этот гад ворует из подноса газеты из моего почтового ящика :). Да не, не из мыльника, а из реального ящика, который висит в подъезде. Слушай ты, снифер хренов. Поймаю, ноги вырву :)))).
procedure TForm1.FormCreate(Sender: TObject);
var
FadapterCount:Integer;
Begin
//Получим доступн. адаптеры
PacketGetAdapterNames(@FAdapterDescs, 4, @FAdapterCount);
//Получим имя адаптера
FAdapterName:= StrPas (@FAdapterDescs[0].SzAdapterDesc);
//Расскажем о юзаемом адаптере
Edit2.Text:= FAdapterName;
NumPack:=0; NumByt:= 0;
end;
Врезка 3. Обработчик OnClick :
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
FuMac: array[0..5] of UCHAR;
begin
FhAdapter:= PacketOpenAdapter(FAdapterDescs[0].szAdapterName);
IF FhAdapter=0 then
begin
MessageDlg ('Не могу открыть адаптер.',mtError,[mbIgnore],0);
exit;
end;
//Получить описание адаптера
PacketAdapterDesc(LPADAPTER(FhAdapter),
@FuBuffer[0], sizeof(FuBuffer), @i);
//Получить MAC адрес
PacketGetAddress(LPADAPTER(FhAdapter),
@FuMac[0], 6, @i);
//Установить фильтр для входящих пакетов
PacketSetFilter(LPADAPTER(FhAdapter),
NDIS_PACKET_TYPE_PROMISCUOUS);
FpPacket := LPPACKET( PacketAllocatePacket(LPADAPTER(FhAdapter)) );
IF (FpPacket= Nil) then
begin
MessageDlg ('Облом',mtError,[mbNo],0);
PacketCloseAdapter(LPADAPTER(FhAdapter));
HALT;
end;
//Пора определить буфер и его размер
PacketInitPacket(FpPacket, @FuBuffer[0], 1520);
{==Ну, полетела душа в рай...будем хапать==}
LogF:= TFileStream.Create(Edit1.Text,fmCreate oR fmOpenWrite);
Nado:= True; //Переменная надобности ;)
While Nado= TRUE do
begin
Application.ProcessMessages;
PacketReceivePacket(LPADAPTER(FhAdapter),
FpPacket, true, @Bytes);
if Bytes>0 then
LogF.Write(FuBuffer,Bytes);//запишем полученное
NumByt:= NumByt+Bytes;
Label2.Caption:= IntToStr (NumByt);
Application.ProcessMessages;
end;
LogF.Free; //Закроем логфайл
end;