Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей.
Здесь обсуждаются безопасность, программирование, технологии и многое другое.
Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
 |
|
@ mrim.pl: Написание скриптов, работающих по протоколу MMP |

16.04.2007, 04:11
|
|
Banned
Регистрация: 22.08.2006
Сообщений: 608
Провел на форуме: 6144796
Репутация:
1095
|
|
@ mrim.pl: Написание скриптов, работающих по протоколу MMP
@ mrim.pl: Написание скриптов,
работающих по протоколу MMP
by Digimortal
[ intr0 ]
Что такое Mail.Ru Агент, я думаю, знают все. Для тех кто не в курсе, это асикуподобный мессенджер с поддержкой разнообразных дополнительных возможностей, вроде отправки смс, голосового общения, игр и пр. В этой статье я достаточно подробно опишу основные моменты работы с протоколом, который испульзует Мэйл.ру Агент, что поможет тебе в написании различных тулз, работающих с этой системой (ни в коем случае не пишите смс-флудеры, МАгент-спаммеры или сборщики почтовых баз mail.ru!!! ;)) Как уже можно было понять из названия, примеры кода, приводящиеся в статье, будут написаны на самом классном скриптовом языке программирования :).
[ протокол MMP ]
Mail.Ru Агент использует собственный протокол - MMP, или mrim, который является частично открытым. На сайте agent.mail.ru выложено краткое описание протокола. В данном cписании присутствует только информация по основным возможностям мессенджера, но вооружившись сниффером пакетов, выяснить значения заголовков пакетов, например, для отправки смс, не составит труда. Помимо описания там присутствует С-хидер, с значениями полей, флагов и т.п. Я посчитал, что будет удобней объединить описание пакетов и этот заголовочный файл в один txt-файл, что и я сделал (смотри в ссылках). В дальнейшем думаю внести туда описания некоторых пакетов, которые не вошли в официальное описание.
MMP действует поверх установленного tcp-соединения. Клиент инициализирует соединение с сервером, и далее взаимодействие происходит путем обмена сообщениями, причем сообщения могут отпарвляться как клиентом так и сервером.
MMP является бинарным протоколом, кроме того, данные передаются не в общепринятом сетевом формате, а в little-endian'е, т.е. старший байт идет впереди. Основные типы данных, описанные в протоколе это:
- UL;
- LPS;
- UIDL.
Типом UL разработчики обозначили u_long или двойное слово, т.е. 4 байта.
LPS - это составной тип, в который кодируются текстовые строки. Он представляет собой идущий впереди UL, в котором содержится длина строки, и саму строку. Строки представлены в windows-1251 кодировке.
UIDL используется гораздо реже первых двух и в статье затрагиваться не будет. Представлен последовательностью из 8 символов из множества [a-z A-Z 0-9 _ - = +].
[ Структура пакетов ]
Рассмотрим теперь структуру пакетов протокола. Как и полагается, пакет состоит из заголовка и данных (данные могут и отсутствовать). Поля хидера:
Код:
<-4bytes->
,----------.
| magic | мэйджик
+----------+
| proto | версия протокола
+----------+
| seq | номер сообщения в текущем соединении (ответ будет иметь тот же номер)
+----------+
| msg | тип пакета
+----------+
| dlen | длина данных (без учета заголовка)
+----------+
| from | ip отправителя в inet_aton() формате
+----------+
| fromport | порт отправителя
+----------+
| | зарезервированные 16 байт, которые
+- -+ в текущих версиях протокола не используются
| reserved |
+- -+
| |
+- -+
| |
+==========+
| data | далее идут данные (если есть)...
'~~~~~~~~~~'
Самым важным для нас полем является "тип пакета". "Мэйджик" и "версия" у нас постоянны. "Номер сообщения" и "длина данных" подсчитуются по ходу дела, а правильный ip и port, как я понял, передавать вообще не обязательно, поэтому не спрашуй у меня, почему я заполняю их нулями (с значением поля seq тоже дозволены некоторые вольности).
Тип пакета - это команды (или ответы на них), которыми обмениваются между собой клиент и сервер. Команды могут иметь параметры, передающиеся как данные пакета. Значения этих команд и параметров иможно взять из того файла, что прикрепил к статье. Я сразу приведу здесь те, что будут использоваться в приводимых далее примерах:
Код:
my $CS_MAGIC = 0xDEADBEEF; ## Клиентский Magic
my $PROTO_VERSION = 0x1000A; ## Версия протокола
my $MRIM_CS_HELLO = 0x1001; ## C->S, empty
my $MRIM_CS_HELLO_ACK = 0x1002; ## S->C, UL mrim_connection_params_t
my $MRIM_CS_LOGIN2 = 0x1038; ## C->S, LPS login, LPS password, UL status, LPS uagent
my $MRIM_CS_LOGIN_ACK = 0x1004; ## S->C, empty
my $MRIM_CS_LOGIN_REJ = 0x1005; ## S->C, LPS reason
my $MRIM_CS_PING = 0x1006; ## C->S, empty
my $MRIM_CS_USER_STATUS = 0x100F; ## S->C, UL status, LPS user
my $STATUS_ONLINE = 0x00000001;
my $MRIM_CS_MESSAGE = 0x1008; ## S->C, UL flags, LPS to, LPS message, LPS rtf-message
my $MESSAGE_FLAG_NORECV = 0x00000004;
Пишем саб, формирующий пакет (правильнее будет сказать, формирующий его хидер - данные, которые ему передаются, должны быть уже заранее правильно сформированы):
Код:
sub make_mrim_packet
{
my ($msg, $data) = @_; ## получаем параметры
my ($magic, $proto, $seq, $from, $fromport) = ($CS_MAGIC, $PROTO_VERSION, $seq_real, 0, 0);
my $dlen = 0; ## длина данных равна 0 или
$dlen = length($data) if $data; ## если есть данные, то рассчитывается их длина
my $mrim_packet = pack("L11", $magic, $proto, $seq, $msg, $dlen, $from, $fromport, 0, 0, 0, 0);
## пакуем заголовок шаблоном "L"
$mrim_packet .= $data if $data; ## добавляем данные, если они есть
return $mrim_packet; ## возвращаем готовый к отправке пакет
}
[ Взаимодействие client <-> server ]
Для того, чтоб устаовить соединение с mrim-сервером, нужно прежде получить его ip и port. Для этого необходимо установить tcp-соединение с mrim.mail.ru:2042 или mrim.mail.ru:443. Проделав это, клиент получает рекомендуемый для соединения ip-адрес и порт. Итак, вот саб, возвращающий ip:port для коннекта:
Код:
sub get_host_port
{
my $sock1 = new IO::Socket::INET
(
PeerAddr => 'mrim.mail.ru',
PeerPort => 2042, ## как вариант можно использовать 443
PeerProto => 'tcp',
TimeOut => 10
);
sysread ($sock1, my $answ, 18);
close $sock1;
chomp $answ;
return split /:/, $answ;
}
Получив ip-адрес и порт, мы можем создавать соединение с сервером для прохождения авторизации, которая происходит следующим образом:
Код:
,---. ,---.
| C |---------MRIM_CS_HELLO------->| S |
| L |<------MRIM_CS_HELLO_ACK------| E |
| I | | R |
| E |--------MRIM_CS_LOGIN2------->| V |
| N |<------MRIM_CS_LOGIN_ACK------| E |
| T |(или <---MRIM_CS_LOGIN_REJ---)| R |
`---' `---'
Клиент посылает пакет "HELLO", в ответ получает "HELLO_ACK" с данными (UL) в которых содержится ожидаемая частота пинга (ping_period). Клиент должен пинговать сервер специальным пакетом через интервал, равный ping_period секунд. Сервер может изменять ping_period, посылая специальный пакет клиенту, но обычно ping_period равен 30 секундам. Напишем саб для отпавления HELLO-пакета и получения значения ping_period из него:
Код:
sub hello
{
print $sock make_mrim_packet($MRIM_CS_HELLO);
sysread ($sock, my $ack, 48); ## принимаем 48 байт (44 - хидер, 4 - данные)
my ($magic, $proto, $seq, $msg, $dlen, $from, $fromport, $r1, $r2, $r3, $r4, $data) = unpack ("L12", $ack);
$ping_period = $data; ## получаем значение ping_period
$seq_real++; ## $seq_real - это в моем коде счетчик seq
print "[+] connected..\n" if $data;
}
Заодно напишем и саб для осуществления пинга сервера. Он будет очень простым:
Код:
sub ping
{
print $sock make_mrim_packet($MRIM_CS_PING);
$seq_real++;
}
Теперь нужно переслать серверу пакет MRIM_CS_LOGIN2, который содержит информацию, необходимую для авторизации: LPS login, LPS password, UL status и LPS user_agent. login и password - это, понятно что, статус - тоже, а user_agent - это описание клиента, может быть любым. При удачной авторизации сервер отвечает нам пакетом MRIM_CS_LOGIN_ACK, при неудачной - MRIM_CS_LOGIN_REJ, в данных которого содержится причина отказа в авторизации. Теперь воплотим это в коде:
Код:
sub login
{
my $data = pack ("L", length($login)) . $login . ## упаковываем LPS-данные
pack ("L", length($password)) . $password .
pack ("L", $status) . ## а вот так UL
pack ("L", length($user_agent)) . $user_agent;
print "[~] try to login as $login:$password\n";
print $sock make_mrim_packet($MRIM_CS_LOGIN2, $data); ## посылаем пакет
$seq_real++; ## не забывая про счетчик сообщений
sysread ($sock, my $ack, 48); ## считываем ответ
my ($magic, $proto, $seq, $msg, $dlen, $from, $fromport, $r1, $r2, $r3, $r4, $data_ack) = unpack ("L12", $ack);
## и распаковываем его
if ($msg == $MRIM_CS_LOGIN_ACK) ## проверяем удачно ли прошла авторизация
{
print "[+] authorization succesfull\n";
}
elsif ($msg == $MRIM_CS_LOGIN_REJ)
{
print "[-] bad authorization:$data_ack\n";
}
else
{
print "[!] something wrong!\n";
}
}
[ Ложки нету =) ]
По сути, у нас есть уже все необходимое, чтоб вывести в онлайн наш mrim.pl. Но просто висящий в онлайне скрипт - это совсем неинтересно, и я решил добавить в статью код, отправяляющий сообщение на указанный адрес. Саб этот я упростил до безобразия, не сделав возможность устанавливать флаги, не сделав проверку на получение адресатом сообщения и еще многие вещи, которые можно было бы сделать (впрочем, практически во всех вышеприведенных сабах стоило бы доработать некоторые моменты).
Данные которые должен содержать пакет сообщения: UL flags, LPS to, LPS message, LPS rtf-message. Установив нужные флаги, можно указать тип сообщения (например, указать, что пересылаемые данные являются списком контактов). Я установил только флаг, означающий отсутствие необходимости присылать пакет о подтверждении доставки сообщения. Сообщение можно оформить и в rtf-формате, но мне это нафиг не нужно, потому в rtf-message отправляется ноль. Итак саб, отправляющий сообщение:
Код:
sub message
{
my ($to, $text) = @_; ## получаем мыло адресата и текст сообщения
my $data = pack ("L", $MESSAGE_FLAG_NORECV) . ## выставим нужный флаг
pack ("L", length($to)) . $to .
pack ("L", length($text)) . $text .
pack ("L", '0');
print $sock make_mrim_packet($MRIM_CS_MESSAGE, $data);
$seq_real++;
}
Теперь соберем все это воедино, добавив основной код:
Код:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
## config ##
my $login = 'anti@mail.ru'; ## логин
my $password = 'g00dp4ss'; ## пасс
my $user_agent = 'mrim.pl'; ## описание агента
## constants ## ниже вставь константы из первой врезки с кодом
< ...constants here... >
## vars ##
my $seq_real = 0; ## счетчик комманд
my $ping_period = 30; ## интервал для пинга
my $status = $STATUS_ONLINE; ## статус
## main ##
my ($host, $port) = get_host_port(); ## берем хост:порт
print "[~] connecting to $host:$port..\n";
my $sock = new IO::Socket::INET ## коннектимся
(
PeerAddr => $host,
PeerPort => $port,
PeerProto => 'tcp',
TimeOut => 10
);
hello(); ## хеллоу и ..
login(); ## логинимся
sleep $ping_period; ## просто повисим в онлайне с полминуты
ping(); ## пинг
message('matrix@bk.ru', 'There is no spoon, Neo..');
## отправляем сообщение на matrix@bk.ru
sleep $ping_period; ## продолжаем еще некоторое время на-
ping(); ## ходиться в онлайне
sleep 10;
## subs ## а ниже добавь все сабы, которые присутствовали в статье
< ...subs here... >
## eof..
Запускаем:
Код:
D:\perl-mrim>perl mrim.pl
[~] connecting to 194.67.57.244:2041..
[+] connected..
[~] try to login as anti@mail.ru:g00dp4ss..
[+] authorization succesfull
Наблюдая за anti@mail.ru из официального mrim-клиента, видим как он выходит в онлайн, а затем получаем от него сообщение: "There is no spoon, Neo.." =)
[ outr0 ]
На этом все, т.к. приведенной информации уже вполне достаточно для того, чтоб ты мог начать писать свои тулзы, взаимодействующие с системой МАгент. Жду теперь интересных релизов по этой теме..
[ Links ]
http://agent.mail.ru - официальный сайт Mail.ru Агент
http://agent.mail.ru/dev-license.html - официальное описание протокола MMP
http://digimortal.0x48k.cc/articlz/mrim-packets.txt - отредактированное мной описание пакетов mrim
http://hellknights.void.ru - сайт моей тимы
http://0x48k.cc - форум DarkSide ResearcherZ
Специально для форума Античат..
Последний раз редактировалось Digimortal; 16.04.2007 в 13:42..
|
|
|

16.04.2007, 12:13
|
|
Участник форума
Регистрация: 08.11.2006
Сообщений: 167
Провел на форуме: 539434
Репутация:
54
|
|
кста, посоветуйте хороший сниффер
|
|
|

16.04.2007, 13:18
|
|
Banned
Регистрация: 22.08.2006
Сообщений: 608
Провел на форуме: 6144796
Репутация:
1095
|
|
Сообщение от x-treem
кста, посоветуйте хороший сниффер
wireshark (он же ethereal)..
|
|
|

16.04.2007, 13:48
|
|
Постоянный
Регистрация: 06.06.2006
Сообщений: 515
Провел на форуме: 1985206
Репутация:
963
|
|
Весьма познавательно)
Насчет кода: я никак не пойму, зачем многие люди юзают sysread/write ? Ты вроде юзаешь IO::Socket, так почему нельзя обойти просто $sock->send/recv ? Тем более, что sysread/write могут вызывать проблемы в некоторых ситуациях...
ЗЫ:
*** (20:22:04 2/04/2007)
но протокол магента писали бляди какие-то
-
*** (20:22:41 2/04/2007)
нет ну нах им надо делать при получении кл стока полей и писать, чтобы мы ИГНОРИРОВАЛИ ИХ
-
*** (20:25:30 2/04/2007)
нахуя им строки длиной в размер DWORD?
-
*** (20:25:36 2/04/2007)
этоже более 2 гб даты)
-
*** (20:26:29 2/04/2007)
нахуя они шлют норм мессаги "как надо", а оффлайн мессаги в формате рфц, т.е. бля получать сообщения - легко, а вот переделать этот двиг под офф мессаги за 5 мин никак
Тем не менее, этот человек разобрался в протоколе довольно глубоко)
|
|
|

16.04.2007, 14:29
|
|
Участник форума
Регистрация: 08.11.2006
Сообщений: 167
Провел на форуме: 539434
Репутация:
54
|
|
KSURi упорно подражает Perl Underground)))
шутко
зачем ты придераешься к коду? ведь фишка статьи обьяснить работу протокола а не показать примеры красивого кода. Обычный пример.
я думаю ты излишне строг.
|
|
|

16.04.2007, 14:33
|
|
Познавший АНТИЧАТ
Регистрация: 21.11.2004
Сообщений: 1,137
Провел на форуме: 2487541
Репутация:
761
|
|
Хорошо. Многим будет интересно. В своё время разобрался в этом протоколе и очень полезный опыт получил. Никогда до того бинарными протоколами не занимался.
|
|
|

16.04.2007, 14:36
|
|
Banned
Регистрация: 22.08.2006
Сообщений: 608
Провел на форуме: 6144796
Репутация:
1095
|
|
Сообщение от KSURi
Насчет кода: я никак не пойму, зачем многие люди юзают sysread/write ? Ты вроде юзаешь IO::Socket, так почему нельзя обойтись просто $sock->send/recv ? Тем более, что sysread/write могут вызывать проблемы в некоторых ситуациях...
тут ты прав.. немного позже поправлю код.. (у меня почему-то уже вошло в привычку использовать IO::Socket тока для открытия сокета =/)..
KSURi упорно подражает Perl Underground)))
шутко
зачем ты придераешься к коду? ведь фишка статьи обьяснить работу протокола а не показать примеры красивого кода. Обычный пример.
я думаю ты излишне строг.
да нет, KSURi все верно говорит.. ) и Perl Underground - это тру ).. если не обращать внимание на "мелкие" погрешности в своем коде, то они со временем становятся привычкой, и каждый твой скрипт будет в итоге говном.. а код просто обязан быть красивым, имхо =)
Последний раз редактировалось Digimortal; 16.04.2007 в 14:45..
|
|
|

16.04.2007, 14:43
|
|
Постоянный
Регистрация: 06.06.2006
Сообщений: 515
Провел на форуме: 1985206
Репутация:
963
|
|
Сообщение от x-treem
KSURi упорно подражает Perl Underground)))
шутко
зачем ты придераешься к коду? ведь фишка статьи обьяснить работу протокола а не показать примеры красивого кода. Обычный пример.
я думаю ты излишне строг.
Не совсем верно. Я взял себе за правило писать на более-менее хорошем тоне, а не подражать Perl Underground. Езин всего лишь толкает хороший тон в массы, он его не изобрел.
А по поводу sysread/write:
It bypasses
buffered IO, so mixing this with reads (other than sysread()), "print", "write", "seek", "tell", or "eof" may cause confusion because the perlio and stdio layers usually buffers data.
Т.е. при использовнии sysread и print (или syswrite и while <$sock>) могут получиться проблемы.
За исследование протокола я уже сказал спасибо в виде +мах. Жду исследований на тему отправки смс 
|
|
|

16.04.2007, 17:32
|
|
Участник форума
Регистрация: 08.11.2006
Сообщений: 167
Провел на форуме: 539434
Репутация:
54
|
|
MRIM_CS_HELLO_ACK:
EF BE AD DE 0D 00 01 00 00 00 00 00 02 10 00 00 я╛н▐............
04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
48 58 EB 0E 74 34 BF BF D8 EE BE BF 1E 00 00 00 HXы.t4┐┐╪ю╛┐....
[48 bytes]
такс. стоп. где в этом пакете время пинга($ping_period)? не могу найти
|
|
|

16.04.2007, 17:41
|
|
Banned
Регистрация: 22.08.2006
Сообщений: 608
Провел на форуме: 6144796
Репутация:
1095
|
|
1E 00 00 00 - так вот оно )
1Eh = 30d
|
|
|
|
 |
|
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|