Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #99, МАРТ 2007 г.

ICQ-бот для хакера

Alek Silverstone

Хакер, номер #099, стр. 122

(aleksi@pisem.net)

Тетя Ася – универсальная помощница

У каждого интернетчика есть аська. Все пользуются ей: кто-то редко, кто-то всегда в онлайне. В основном с ее помощью обмениваются сообщениями, гораздо реже пересылают файлы и играют в игры. И очень немногие используют ее для чего-то другого. Сегодня я покажу тебе, как на Delphi с помощью компонента TICQclient написать такого бота, который наверняка будет тебе полезен.

Установка

Думаю, начать нужно с регистрации нового номера для бота. Заходим на https://www.icq.com/register и отвечаем на несколько пикантных вопросов. Затем необходимо найти на нашем диске или в интернете (смотри ссылку) сам компонент TICQclient. Распаковываем архив в отдельную папку (например, TICQclient) и открываем файл ComponentICQ.dpk. Если ты качал компонент из инета, то перед компиляцией и установкой исходники нужно пропатчить. Дело в том, что компонент довольно старый, а ICQ Inc. некоторое время назад поменяла протокол в надежде побороть альтернативные мессенджеры. Обновления для них появились уже на следующий день, а чуть позже хорошие люди нашли решение и для этого компонента. Итак, тыкаем два раза на ICQclient.pas, находим строчку «FUIN := GetTLVStr(@pkt, T);» и меняем на нижеследующий блок:

Патч для исходников

T := GetInt(@pkt, 2);

if T = $008e then // TLV(142 [0x008E])

begin

Inc(pkt.Len, GetInt(@pkt, 2));

FUIN := GetTLVStr(@pkt, T);

end else

if T = $0001 then // TLV(1 [0x0001])

FUIN := GetWStr(@pkt);

Кстати, на последнем этапе написания этой статьи выяснилось, что протокол опять поменяли. Решение было найдено быстро, но, ввиду его размера, я не могу привести его тут, и всех любителей ручного труда мне придется отослать на SourceForge в багтрак проекта. Все остальные смело берут уже пропатченный компонент на диске (по состоянию на первые числа февраля он рабочий).

Итак, будем считать, что нормальный компонент мы тем или иным способом добыли. Сохраняем, закрываем окно с кодом, жмем «Install». Компонент установится на вкладку «Samples». Теперь создаем новый проект и в его свойствах, в разделе «Directories» в строке «Search path», указываем путь к папке TICQclientComponent. Вот и все, можно писать код. Добавлю только, что вместе с компонентом идут два примера и описание свойств, событий и методов на русском и английском языках.

Основы

Для начала перечислю самые основные свойства, методы и события компонента, без которых нам никак не обойтись:

свойство UIN – номер аськи для бота;

свойство Password – пароль от номера;

свойство ConverToPlaintext определяет, нужно ли преобразовывать полученные RTF-сообщения в обычный текст; для того чтобы можно было общаться с ботом при помощи официального клиента ICQ, нужно установить значение true;

метод Login – подключение к серверу;

метод Disconnect – отключение от сервера;

метод SendMessage(UIN, msg) – отправка сообщения msg на номер UIN;

событие MessageRecv – вызывается при получении сообщения.

Для наших целей нам хватит и описанного (на списки видимости/невидимости, авторизацию и т.д. можно и забить). Разумеется, ничто не мешает тебе потом использовать все возможности компонента для создания более мощного кода: отправки файлов по прямому соединению, работы через прокси и т.д.

Для примера мы напишем бот-шлюз. После включения по нашей команде он будет пересылать все полученные от нас сообщения на указанный номер. Разумеется, то же самое будет происходить с сообщениями нашего собеседника. Фишка в том, что он не увидит твоего номера, а только номер бота. Итак, кидаем на форму компонент, устанавливаем свойства UIN и Password. Дважды щелкаем по форме и пишем вот такой обработчик:

MasterUIN:='<номер «хозяина»>';

work:=false;

ICQclient1.Login;

Поднимаемся чуть выше и под строкой «Form1: TForm1;» пишем:

OpponentUIN, MasterUIN:string;

work:boolean;

Это переменные для хранения номера собеседника и хозяина бота, а также статуса работы. Теперь начнем писать обработку входящих сообщений – событие OnMessageRecv. Сначала – команда создания соединения:

Создание канала связи

if (pos('/start ',Msg)=1)and(UIN=MasterUIN) then begin

delete(Msg,1,length('/start '));

while pos(#10,Msg)<>0 do delete(Msg,pos(#10,Msg),1);

while pos(#13,Msg)<>0 do delete(Msg,pos(#13,Msg),1);

OpponentUIN:=Msg;

ICQclient1.SendMessage(StrToInt(MasterUIN),'Channel created.');

work:=true;

exit;

end;

Бот, получив от хозяина команду вида «/start <номер>», сохранит номер собеседника, отправит подтверждение хозяину и изменит статус работы. Два цикла нужны специально для официального клиента, который любит в конце сообщения вставлять перевод строки. Мы его вырезаем, чтобы выделить номер.

Фрагмент обработки команды завершения соединения полностью аналогичен:

Разрыв связи

if (pos('/stop',Msg)=1)and(UIN=MasterUIN) then begin

ICQclient1.SendMessage(StrToInt(MasterUIN),'Channel destroyed.');

work:=false;

exit;

end;

Ну, и последние строчки – пересылка сообщений:

Пересылка

if not work then ICQclient1.SendMessage(StrToInt(UIN),'Not connected.')

else

if UIN=MasterUIN then ICQclient1.SendMessage(StrToInt(OpponentUIN),Msg)

else ICQclient1.SendMessage(StrToInt(MasterUIN),Msg);

Тут сначала делается проверка статуса (если соединение разорвано, то отправляется сообщение об ошибке), затем определяется отправитель сообщения, которое пересылается другому собеседнику.

Вот и все! 5 минут – и твой персональный шлюз готов! Дорабатывать его можно бесконечно: разрешить пользоваться им всем, добавить бан-списки, разрешить передачу файлов… Все в твоих руках.

Записная книжка

Дом, работа, интернет-кафе… И везде - свой клиент аськи. А если нужно сохранить важное сообщение? Записать на бумажку? Есть решение получше: написать своего бота – всюду доступную записную книжку. Он будет сохранять полученные сообщения и по команде показывать их. Нравится идея? Создавай новый проект, располагай компонент, выставляй свойства UIN и Password. Обработчик формы – почти как в прошлом примере:

MasterUIN:='<номер «хозяина»>';

ICQclient1.Login;

Теперь будем обрабатывать входящие сообщения. В событии OnMessageRecv объявляем четыре переменные:

fs:TFileStream;

i:integer;

ch:char;

send:string;

Это файловый поток, куда мы будем сохранять заметки, счетчик для цикла, считанный из потока символ и буфер для отправки. Принцип работы такой: получаем сообщения только от хозяина; если получено сообщение «/show», то выводим все заметки; иначе сохраняем сообщение. Сам код смотри на врезке. В нем в качестве разделителя заметок используется символ с кодом 0. При записи мы добавляем символы перевода строки.

Код записной книжки

if UIN<>MasterUIN then exit;

send:='';

if FileExists('notes.txt') then

fs:=TFileStream.Create('notes.txt',fmOpenReadWrite)

else

fs:=TFileStream.Create('notes.txt',fmCreate);

if pos('/show',Msg)=1 then begin

fs.Seek(0,soFromBeginning);

for i:=1 to fs.Size do begin

fs.Read(ch,1);

if ch<>#0 then send:=send+ch

else begin

ICQclient1.SendMessage(StrToInt(MasterUIN),send);

send:='';

end;

end;

end else begin

fs.Seek(0,soFromEnd);

send:=DateTimeToStr(now)+#10#13+Msg+#10#13#0;

fs.Write(send[1],length(send));

ICQclient1.SendMessage(StrToInt(MasterUIN),'Note saved.');

end;

fs.Free;

Remote control

Теперь сделаем кое-что посерьезнее: напишем программу для удаленного управления компом по аське. Только не надо сразу думать о троянах ;), применение этой программы может быть вполне мирным. Вот тебе пример из моего опыта. Я участвую в развитии сети, состоящей из нескольких сегментов. Находясь в одном из них, я могу по аське дать команду своему компу, находящемуся в другом, пинговать определенный хост. Пингуя его с разных сторон-сегментов, можно быстро локализовать проблему. Удобно? Еще как! Сейчас я покажу тебе, как можно просто создать программу, превращающую обычный клиент аськи в некоторое подобие удаленной консоли.

Создай новый проект, кинь на форму компонент, установи свойства UIN и Password. Теперь поищи на диске модуль uCmdPipe, написанный мной, скопируй его в папку с проектом и добавь в uses. Этот модуль берет на себя все основные функции по созданию консоли и связи ее с пайпами. Теперь найди в коде строчку «var Form1:TForm1» и напиши под ней:

MasterUIN:string;

p:TCmdPipe;

cmd:shortstring;

Первая переменная будет хранить UIN хозяина бота, вторая - класс нашей консоли, а третья – путь к cmd.exe. Теперь тыкаем два раза в форму и пишем обработчик ее создания:

MasterUIN:=’ <номер «хозяина»>’;

ICQclient1.Login;

SetLength(cmd,255);

GetEnvironmentVariable('ComSpec',@(cmd[1]),255);

Первой строчкой мы задаем номер хозяина (впиши свой), потом коннектимся и последними двумя записываем значение переменной среды ComSpec, содержащей путь к исполняемому файлу командного интерпретатора, в нашу переменную cmd. Далее пишем обработку входящих сообщений (OnMessageRecv):

if UIN<>MasterUIN then exit;

if pos('/cmd',Msg)=1 then p.CreateCmdPipe(cmd)

else p.WriteStringCmdPipe(Msg);

Тут мы проверяем номер отправителя (принимаем сообщения только от хозяина) и анализируем текст. Если в тексте содержится «/cmd», то создаем новую консоль, в противном случае пишем в созданную ранее. Класс TCmdPipe создает только одну консоль и перед любой операцией делает проверку ее существования, что избавляет нас от лишних проблем. Все его методы смотри в комментариях в модуле uCmdPipe. Теперь нам нужно отослать результат команд обратно. Есть одна тонкость – мы никогда не можем быть уверенны во времени прихода данных в пайп. Как сказал Zero Ice: «Приход данных – это великая тайна». Поэтому мы будем делать его в отдельном потоке, не пытаться получить сразу после отправки команды. Итак, кидаем на форму TTimer, устанавливаем интервал поменьше, а свойство Enable в true и пишем обработчик OnTimer:

if p.DataPresent then begin

send:=p.ReadStringCmdPipe;

ICQclient1.SendMessage(StrToInt(MasterUIN),send);

end;

Мы проверяем наличие данных в пайпе (и заодно самого пайпа) и, если они есть, считываем и отсылаем. В этом же обработчике нужно объявить переменную send типа string.

Заметил, как все просто? Всего несколько строчек – и мы написали очень полезный код. А все из-за продуманности компонента и моего модуля.

Альтернативы

У компонента TICQclient есть одно неоспоримое преимущество – простота. Главный же его недостаток состоит в том, что он больше не поддерживается. В случае изменения протокола никто не гарантирует выпуск патча. Хотя, как показывает практика, толковых людей, использующих его, предостаточно, и они в состоянии написать патч и поделиться им со всеми. Но тогда тебе вручную нужно будет изменять исходник. Есть альтернатива – использовать библиотеку IcqOscarJ. Это один из основных компонентов Miranda IM, доступный в исходных кодах, и с документацией. Он регулярно обновляется. Ты можешь реализовать в своей программе автоматическую загрузку библиотеки с сайта проекта. Вот только заголовочные модули написаны на С++, так что придется посидеть и попереводить. Полезная программка c2pas32 поможет тебе в этом, хотя и сделает далеко не всю работу.

Другим вариантом может быть использование развивающейся сейчас библиотеки ICQkid2 – удобной кроссплатформенной реализации протокола ICQ. Опять же хедеры написаны на С++.

Вот и все. Основы я тебе рассказал. Теперь ты можешь сделать такого бота, какого захочешь. Главное – идея, а реализовать ее будет несложно. Удачи! Будут проблемы – пиши мне, адрес указан в начале статьи.

И еще - хотелось бы сказать большое спасибо человеку по имени Zero Ice за помощь с пайпами.

WWW

http://sourceforge.net/projects/ticqlib;

http://addons.miranda-im.org/details.php?action=viewfile&id=1683;

http://icqkid2.sourceforge.net;

www.astonshell.com/files/c2pas32.zip.

DVD

На диске ты найдешь пропатченный компонент, модуль uCmdPipe и примеры из статьи.

INFO

Зайди на smarterchild.com или напиши «hi» на номер 35000 и посмотри, на что способны асечные боты.

Содержание
ttfb: 14.848947525024 ms