Какой самый популярный IM-клиент в России? Конечно же, аська.
А в США? Конечно, MSN. Даже те, кто сидят на маках (в смысле, компьютерах), пользуются этим мессенджером. И даже те, кто сидят на маках опийных, тоже пользуются майкрософтовской системой быстрых сообщений.

 

Как оно работает?

Из этого следует простой вывод — хочешь внедриться в спам-бизнес, затрагивающий цивилизованные страны (те, где у людей есть деньги) — изучай их ПО, знай их протоколы передачи данных.

Когда я впервые (это было довольно давно) решил разобраться с работой протокола Microsoft Messenger, то оказалось, что в интернете не так уж и много информации по этой теме. При этом сама Microsoft по ходу пьесы притворилась Буратино, или тем, из чего это существо сделано — я беседовал с сотрудниками компании, искал в интернете блоги программистов, работающих в MS, просил их поделиться знаниями. Все бесполезно.

На некоторое время я оставил попытки узнать про протокол, но через полгодика наткнулся на библиотеку MSNPSharp (http://code.google.com/p/msnp-sharp/). Долгое время эта библиотека лежала у меня на диске мертвым грузом, но вот недавно на работе мне пришлось писать модуль, который должен был рассылать сообщения на MSN-клиенты. Я тут же вспомнил про MSNPSharp, скачал последнюю версию и разобрался с ней в два счета.

 

Ползучая тварь

Точек приложения для этой библиотеки много — от легального софта до реализации различных противозаконных программулин, вроде спамеров и червяков. Ну, например, работающих вот так:

  1. Пытаемся разослать приглашения на общения;
  2. Если кто-то отзывается, то пытаемся заставить пользователя принять и запустить файл. В свое время таким же образом распространялись вирусы по почте, и вполне успешно;
  3. Если кто-то повелся и запустил файл, то этот файл рассылает себя дальше.

Еще один сценарий — написать программу, которая будет подбирать пароли к аккаунтам из списка по словарю. Если аккаунт взломан (а чем больше аккаунтов, тем проще найти ламера с примитивным паролем), то можно прочитать содержимое контактов и разослать файл от имени ламера. Такая рассылка отработает намного эффективнее, потому что MSN’у пока доверяют.

 

Асинхронная модель

Все в библиотеке крутится вокруг класса Messenger, который создается банально и без параметров:

Messenger messenger = new Messenger();

Класс работает с протоколом в асинхронном режиме. И за это разработчикам MSNPSharp однозначно нужно пожать руку. Дело в том, что если бы класс работал в синхронном режиме, то на нас свалилась бы куча проблем по асинхронизации. Некоторые команды выполняются очень долго. Например, в момент скачивания контактов класс может капитально заснуть на несколько секунд. Если выполнять операцию синхронно, то в спячку ушло бы все приложение, а так — только класс.

Просто вызывай нужную функцию, и жди, когда она сгенерит соответствующее событие. Событий у класса довольно много. Вот минимальный набор, который может пригодиться в реальной жизни:

  • NameserverProcessor.ConnectionEstablished — генерируется, когда соединение удачно установлено;
  • Nameserver.SignedIn — мы вошли в систему;
  • Nameserver.SignedOff — удачно вышли, прямо как Винни-Пух, у которого все прекрасно входит и выходит;
  • Nameserver.AuthenticationError — авторизация не прошла;
  • ConversationCreated — начато общение с удаленным клиентом.

Достаточно установить обработчик на каждое событие, и можно начинать соединение с сервером. Хотя подожди. Сначала нужно указать свои данные — те, с которыми мы подключаемся к серверу. У класса мессенджера есть свойство Credentials, которое имеет одноименный тип.

У конструктора класса Credentials есть два параметра — почтовый ящик и пароль в чистом и красивом виде. Итак, параметры учетной записи, с помощью которой мы будем подключаться к серверу сообщений, прописываем следующим образом:

messenger.Credentials = new Credentials(
"youaccount@hotmail.com", "qwerty");

Если ты подцепился на все нужные нам обработчики событий и указал учетную запись, можно запускать процесс коннекта к серверу методом Connect();.

 

Поболтаем

Разговоры между незнакомыми абонентами категорически запрещены, и этот запрет представляет собой реальное западло для хакера. Чтобы кого-то вызвать на серьезный и жесткий разговор, нужно сначала направить приглашение, и только если твой invite приняли, можно посылать сообщения. Такой протокол должен предотвращать бесконтрольные рассылки заразы, которые происходят в классической e-почте. Правда, теоретически вполне реально написать червя, который будет распространяться по алгоритму Морриса, ведь все хорошее новое — это хорошо забытое старое. Вспомним, как работал один из самых нашумевших червей. Для взлома аккаунтов он использовал подбор паролей по словарю, который брал из *nix-системы. Так как в те времена о безопасности мало думали даже специалисты, червь удачно ломал системы.

Сейчас о безопасности думают больше, но только специалисты и продвинутые пользователи. Ламеров на просторах нашей Сети все еще очень много, поэтому можно попробовать повторить взлом. Проникая на компьютер, можно попытаться взломать MSN-аккаунты банальным перебором всех почтовых ящиков, которые есть в адресной книге. Если что-то взломалось, то подключаемся от имени лохоюзера и рассылаем себя других лохоюзерам Сети. Известный факт — когда файлы приходят от друзей, то их запускают намного чаще. Итак, чтобы отправить кому-то сообщение, мы сначала должны пригласить его в свой список друзейтоварищей.

Все, что касается адресной книги MSN, находится в классе Nameserver мессенджера. Чтобы пригласить нового друга, вызываем метод AddNewContact сервиса ContactService. Запутался? Проще один раз показать, чем десять раз рассказать:

Messenger.Nameserver.ContactService.
AddNewContact("pamela_anderson@hotmail.com");

Теперь о том, как получить список контактов. Контакты находятся в свойстве ContactList, которое является достаточно серьезным классом. Если тебе нужны все контакты, то обращайся к коллекции All (ContactList.All).

Помимо этого можно увидеть следующие типы контактов:

  • Allowed — разрешенные контакты;
  • BlockedList — здесь мы можем увидеть юзеров, которых заблокировали.

Сам по себе список не синхронизируется с сервером, и сразу после подключения все списки контактов будут пустыми. Дело в том, что эта операция происходит довольно медленно. Если нужно забрать с сервера свои контакты, то можно изменить свойство AutoSynchronize на true:
messenger.Nameserver.AutoSynchronize = true;

Если же скорость старта тебя особенно не волнует, то изменение свойства AutoSynchronize можно поместить в обработчике события ConnectionEstablished. Это вполне логично — сразу после установки подключения синхронизировать контакты.

 

Общение

Когда мы вошли в систему, то есть произошло событие SignedIn, мы можем изменять состояние и начинать общаться. Чтобы показать, что ты в сети и готов к общению, измени свое состояние на online следующим образом:

messenger.Owner.Status = PresenceStatus.Online;

Разумеется, для спама и прочих сомнительных с точки зрения законности мероприятий статус значения не имеет — сообщения можно слать и при PresenceStatus.Busy. Лично мне кажется, что с психологической точки зрения занятость даже более предпочтительна — если собеседник занят, но отправляет тебе что-то, ты не будешь приставать к нему с лишними вопросами о том, что это и зачем.

Теперь сама отправка сообщения. Этот процесс чуть более запутан. Для этого нужно создать новое общение, за которое отвечает класс Conversation:

Conversation conversation =
messenger.CreateConversation();

Теперь просто дожидаемся, когда сработает событие ConversationCreated. А вот уже когда новое общение создано, мы должны найти в списке контактов человека, которому хотим отправить послание, и пригласить его на разговор.

Допустим, что messenger-аккаунт человека, которому мы хотим отправить сообщение, находится в строковой переменной MsnAccountTo:

private void messenger_ConversationCreated(
object sender, ConversationCreatedEventArgs e)
{
if (e.Initiator != null)
{
foreach (MSNPSharp.Contact
contact in messenger.ContactList.All)
{
if (contact.Mail == MsnAccountTo)
{
e.Conversation.ContactJoined +=
new EventHandler<ContactEventArgs>(
ContactJoined);
e.Conversation.Invite(contact);
return;
}
}
messenger.Nameserver.ContactService.AddNewContact
(MsnAccountTo);
}
}

Код банален — мы просто ищем контакт в списке всех, кого мы знаем. Если чей-то почтовый ящик совпадает с тем, кого мы хотим нежно и ласково полюбить, то вызываем этого человека на разговор. Созданная беседа покоится в свойстве Conversation второго параметра события.

Мы должны подписаться на событие ContactJoined этого класса общения, чтобы узнать, когда удаленный пользователь готов пообщаться:

e.Conversation.ContactJoined +=
new EventHandler<ContactEventArgs>(ContactJoined);

А приглашение на этот разговор делается вызовом метода Invite и указанием ящика бедолаги:

e.Conversation.Invite(contact);

Тут нужно быть внимательным и аккуратным. Событие ConversationCreated может вызываться несколько раз. Я не понял, с какого перепуга это происходит, но факт есть факт — приходится гасить повторяющуюся генерацию события. Это можно сделать, например, вводя дополнительную булеву переменную, которая будет устанавливаться при создании общения и гаситься после обработки события. Если переменная не установлена, то игнорировать событие.

Но и это еще не все. Теперь в недрах библиотеки снова начинается асинхронный для нас процесс, по завершению которого вызывается событие ContactJoined. И вот в нем мы уже можем отправить пользователю сообщение. Следующий пример показывает, как отправить простое текстовое послание:

private void ContactJoined(object sender,
ContactEventArgs e)
{
TextMessage message =
new TextMessage(currentmessage);
(sender as Conversation).SendTextMessage(message);
}

В первой строке мы подготавливаем текстовое сообщение к отправке. Оно должно иметь тип класса TextMessage. Конструктору этого класса мы банально передаем строку текста.
Так как событие генерирует класс Conversation, то первый параметр как раз на него и указывает, и мы можем его использовать. Например, для вызова метода SendTextMessage с целью непосредственной отправки сообщения.

На уже созданном классе Conversation и пришедшем на разговор удаленным клиентом, можно отсылать несколько сообщений подряд. Если пользователь не в сети, Conversation все равно будет создан.

 

Итого

Вот и все. Еще раз повторю, что MSN реально популярен в США — лишь однажды я видел контору, в которой использовали Skype, но при ближайшем рассмотрении оказалось, что большинство ее программистов сидят в Петербурге. На диске к журналу ты найдешь небольшой класс-помощник, который упрощает работу с библиотекой и сводит отправку сообщений к вызову всего одного метода, если не считать конструктора, который вызывается автоматом при инициализации объекта. В большинстве простых ситуаций этого класса будет достаточно.

Оставить мнение

Check Also

Скрытая сила пробела. Эксплуатируем критическую уязвимость в Apache Tomcat

В этой статье мы поговорим о баге в Apache Tomcat, популярнейшем веб-сервере для сайтов на…