Здесь рассказано про динамический анализ пакетов, но это не единственный вариант. Вы можете использовать jadx на мобильной версии, чтобы проводить статический анализ. Он даст больше понимания.
Также здесь собраны базовые данные, которые можно использовать для дальнейшего анализа или создания своих проектов.
Хотя в большинстве случаев отправляемые данные называются сообщениями, я буду использовать понятие пакета, чтобы не создавать путаницу.
У Max существует два API:
- WSS - для web-версии
- TLS - для приложений
По факту это один и тот же API, но разница между ними все же есть. Как пример: в web-версии может быть заблокировано что-то, что не заблокировано в app-версии.
- Заходим на сайт: web.max.ru
- Открываем панель разработчика:
Ctrl + Shift + I - Переходим во вкладку Network
- В фильтрах сверху выбираем WS
- Перезагружаем страницу
- Выбираем WebSocket-подключение
- Переходим во вкладку Messages
После выбора пакета в левом нижнем углу можно изменить режим отображения View. По умолчанию там выставлен UTF-8, но вы, скорее всего, будете использовать Hex viewer. Кнопкой рядом можно скопировать пакет в формате Hex stream для стороннего редактора.
Здесь не обойтись без стороннего софта.
- Скачиваем и устанавливаем Windows-версию приложения: download.max.ru
- Скачиваем и устанавливаем mitmproxy: mitmproxy.org
- Запускаем mitmweb
- Устанавливаем сертификат
Путь к файлу:
C:\Users\%Username%\.mitmproxy\mitmproxy-ca-cert.cer
Устанавливать для: Локального компьютера
Хранилище: Доверенные корневые центры сертификации
- Запускаем Max
В интерфейсе mitmproxy:
- Переходим во вкладку Capture
- Включаем Local Applications
- Выбираем
max.exe - Переходим во вкладку Flow List
- Очищаем список: File -> Clear All
- Перезапускаем Max.
- Выбираем TCP-подключение
После выбора подключения в правом верхнем углу можно выбрать режим View. Как и в случае с Web API, чаще всего вы будете пользоваться Hex Dump, а для стороннего использования Hex Stream.
Для побайтового анализа пакета можно использовать:
- hexed.it - отличный онлайн-инструмент для работы с бинарными данными.
- HxD - полноценный офлайн-аналог.
Заголовок составляет 10 байт.
| Номер байта | Описание |
|---|---|
| 1 | ver |
| 2 | cmd |
| 3..4 | seq |
| 5..6 | opcode |
| 7 | cof |
| 8..10 | Длина payload в байтах |
| 11.. | payload - содержание пакета (кодируется в формате MsgPack) |
Версия протокола. Не изменяется с начала соединения.
| Значение | Описание |
|---|---|
| 0 | Request - начало операции |
| 1 | Response - результат операции, если операция прошла успешно |
| 3 | Error - результат операции, если операция прошла неудачно |
Порядковый номер операции. Связывает запрос и ответ, т.е. с каким seq был отправлен запрос, с тем же seq придёт ответ. Обычно начинается с 0 и увеличивается на 1 с каждым новым запросом. Когда доходит до максимума требует переподключения.
Байт cof отображает то, насколько сжат payload при помощи LZ4. Если cof равен 0, то сжатие не используется.
Формула расчета:
Сжатие применяется только в том случае, если длина payload превышает 32 байта.
Здесь собраны все возможные opcode.
Актуально для Android-приложения версии 26.32.1 и версии протокола: 10.
Знак ~ означает, что opcode не подтвержден исходниками выбранной версии приложения.
| Opcode | Описание |
|---|---|
| 1 | PING |
| 2 | DEBUG |
| 3 | RECONNECT |
| 5 | LOG |
| 6 | SESSION_INIT |
| 8 | LOGIN2 |
| 9 | GET_PROMOTED_CONTENT |
| 10 | PROMOTED_CONTENT_CALLBACK |
| 16 | PROFILE |
| 17 | AUTH_REQUEST |
| 18 | AUTH |
| 19 | LOGIN |
| 20 | LOGOUT |
| 21 | SYNC |
| 22 | CONFIG |
| 23 | AUTH_CONFIRM |
| 25 | PRESET_AVATARS |
| 26 | ASSETS_GET |
| 27 | ASSETS_UPDATE |
| 28 | ASSETS_GET_BY_IDS |
| 29 | ASSETS_ADD |
| 32 | CONTACT_INFO |
| 33 | CONTACT_ADD |
| 34 | CONTACT_UPDATE |
| 35 | CONTACT_PRESENCE |
| 36 | CONTACT_LIST |
| 37 | CONTACT_SEARCH |
| 38 | CONTACT_MUTUAL ~ |
| 39 | CONTACT_PHOTOS |
| 40 | CONTACT_SORT |
| 42 | CONTACT_VERIFY |
| 43 | REMOVE_CONTACT_PHOTO |
| 46 | CONTACT_INFO_BY_PHONE |
| 48 | CHAT_INFO |
| 49 | CHAT_HISTORY |
| 50 | CHAT_MARK |
| 51 | CHAT_MEDIA |
| 52 | CHAT_DELETE |
| 53 | CHATS_LIST |
| 54 | CHAT_CLEAR |
| 55 | CHAT_UPDATE |
| 56 | CHAT_CHECK_LINK |
| 57 | CHAT_JOIN |
| 58 | CHAT_LEAVE |
| 59 | CHAT_MEMBERS |
| 60 | PUBLIC_SEARCH |
| 61 | CHAT_PERSONAL_CONFIG |
| 62 | CHAT_LIVESTREAM_INFO |
| 63 | CHAT_CREATE ~ |
| 64 | MSG_SEND |
| 65 | MSG_TYPING |
| 66 | MSG_DELETE |
| 67 | MSG_EDIT |
| 68 | CHAT_SEARCH |
| 70 | MSG_SHARE_PREVIEW |
| 71 | MSG_GET |
| 72 | MSG_SEARCH_TOUCH |
| 73 | MSG_SEARCH |
| 74 | MSG_GET_STAT |
| 75 | CHAT_SUBSCRIBE |
| 76 | VIDEO_CHAT_START |
| 77 | CHAT_MEMBERS_UPDATE |
| 78 | VIDEO_CHAT_START_ACTIVE |
| 79 | VIDEO_CHAT_HISTORY |
| 80 | PHOTO_UPLOAD |
| 81 | STICKER_UPLOAD |
| 82 | VIDEO_UPLOAD |
| 83 | VIDEO_PLAY |
| 84 | VIDEO_CHAT_CREATE_JOIN_LINK |
| 86 | CHAT_PIN_SET_VISIBILITY |
| 87 | FILE_UPLOAD |
| 88 | FILE_DOWNLOAD |
| 89 | LINK_INFO |
| 91 | GET_COMMENTS_UPDATES |
| 92 | MSG_DELETE_RANGE |
| 94 | MSG_DELETE_USER |
| 96 | SESSIONS_INFO |
| 97 | SESSIONS_CLOSE |
| 98 | PHONE_BIND_REQUEST |
| 99 | PHONE_BIND_CONFIRM |
| 101 | AUTH_LOGIN_RESTORE_PASSWORD |
| 103 | GET_INBOUND_CALLS |
| 104 | AUTH_2FA_DETAILS |
| 105 | EXTERNAL_CALLBACK |
| 106 | PHONE_WEBAPP_SHARE |
| 107 | AUTH_VALIDATE_PASSWORD |
| 108 | AUTH_VALIDATE_HINT |
| 109 | AUTH_VERIFY_EMAIL |
| 110 | AUTH_CHECK_EMAIL |
| 111 | AUTH_SET_2FA |
| 112 | AUTH_CREATE_TRACK |
| 113 | AUTH_CHECK_PASSWORD |
| 115 | AUTH_LOGIN_CHECK_PASSWORD |
| 116 | AUTH_LOGIN_PROFILE_DELETE |
| 117 | CHAT_COMPLAIN |
| 118 | MSG_SEND_CALLBACK |
| 119 | SUSPEND_BOT |
| 124 | LOCATION_STOP |
| 125 | LOCATION_SEND |
| 126 | LOCATION_REQUEST |
| 127 | GET_LAST_MENTIONS |
| 128 | NOTIF_MESSAGE |
| 129 | NOTIF_TYPING |
| 130 | NOTIF_MARK |
| 131 | NOTIF_CONTACT |
| 132 | NOTIF_PRESENCE |
| 134 | NOTIF_CONFIG |
| 135 | NOTIF_CHAT |
| 136 | NOTIF_ATTACH |
| 137 | NOTIF_CALL_START |
| 139 | NOTIF_CONTACT_SORT |
| 140 | NOTIF_MSG_DELETE_RANGE |
| 142 | NOTIF_MSG_DELETE |
| 143 | NOTIF_CALLBACK_ANSWER |
| 144 | CHAT_BOT_COMMANDS |
| 145 | BOT_INFO |
| 147 | NOTIF_LOCATION |
| 148 | NOTIF_LOCATION_REQUEST |
| 150 | NOTIF_ASSETS_UPDATE |
| 152 | NOTIF_DRAFT ~ |
| 153 | NOTIF_DRAFT_DISCARD ~ |
| 154 | NOTIF_MSG_DELAYED |
| 155 | NOTIF_MSG_REACTIONS_CHANGED |
| 156 | NOTIF_MSG_YOU_REACTED |
| 158 | OK_TOKEN |
| 159 | NOTIF_PROFILE |
| 160 | WEB_APP_INIT_DATA |
| 161 | COMPLAIN |
| 162 | COMPLAIN_REASONS_GET |
| 163 | CALL_HISTORY |
| 164 | CALL_HISTORY_CLEAR |
| 165 | NOTIF_CALL_HISTORY |
| 166 | VIDEO_CHAT_JOIN |
| 167 | VIDEO_CHAT_HANGUP |
| 176 | DRAFT_SAVE ~ |
| 177 | DRAFT_DISCARD ~ |
| 178 | MSG_REACTION |
| 179 | MSG_CANCEL_REACTION |
| 180 | MSG_GET_REACTIONS |
| 181 | MSG_GET_DETAILED_REACTIONS |
| 193 | STICKER_CREATE |
| 194 | STICKER_SUGGEST |
| 195 | VIDEO_CHAT_MEMBERS |
| 196 | CHAT_HIDE |
| 198 | CHAT_SEARCH_COMMON_PARTICIPANTS |
| 199 | PROFILE_DELETE |
| 200 | PROFILE_DELETE_TIME |
| 202 | TRANSCRIBE_MEDIA |
| 203 | PHOTO_URL_REFRESH |
| 208 | STORIES_LIST |
| 209 | STORIES_LIST_BY_OWNER_ID |
| 210 | STORIES_GET_BY_OWNER_ID |
| 211 | STORIES_GET_STATS |
| 212 | STORIES_GET_DETAILED_STATS |
| 213 | STORIES_REACT |
| 214 | STORIES_MARK |
| 215 | STORIES_SEND |
| 216 | NOTIF_STORIES_UPDATE |
| 217 | STORIES_EDIT |
| 218 | STORIES_DELETE |
| 220 | STORIES_GET_BY_STORY_ID |
| 240 | GET_PINNED_MESSAGE_STATES |
| 241 | PINNED_MESSAGES_GET |
| 242 | PINNED_MESSAGE_UPDATE |
| 243 | NOTIF_CHAT_MESSAGE_PINNED |
| 256 | ORG_INFO |
| 257 | CHAT_REACTIONS_SETTINGS_SET |
| 258 | REACTIONS_SETTINGS_GET_BY_CHAT_ID |
| 259 | ASSETS_REMOVE |
| 260 | ASSETS_MOVE |
| 261 | ASSETS_LIST_MODIFY |
| 272 | FOLDERS_GET |
| 273 | FOLDERS_GET_BY_ID |
| 274 | FOLDERS_UPDATE |
| 275 | FOLDERS_REORDER |
| 276 | FOLDERS_DELETE |
| 277 | NOTIF_FOLDERS |
| 290 | AUTH_QR_APPROVE |
| 291 | AUTH_BY_TRACK |
| 292 | NOTIF_BANNERS |
| 293 | NOTIF_TRANSCRIPTION |
| 294 | AUTH_CHECK_CALL |
| 300 | CHAT_SUGGEST |
| 301 | AUDIO_PLAY |
| 302 | BANNERS_GET |
| 303 | MSG_DELIVERY |
| 304 | SEND_VOTE |
| 305 | VOTERS_LIST_BY_ANSWER |
| 306 | GET_POLL_UPDATES |
| 307 | CHAT_CHECK_ESIA |
Error
{
string description
string error
string title
string message
string localizedMessage
}
title и description не гарантированы.
Здесь описаны структуры запросов и ответов под каждый opcode. Названия полей полностью соответствуют тем, что будут в пакете, т.е. их можно использовать для парсинга.
EnumAsString значит, что поле представляет собой String, но может содержать ограниченное количество значений, которые можно представить в виде Enum.
Request
{
bool interactive
}
Response { }
Request
{
[EnumAsString]
CmdType cmd
string[] args
}
Response { }
Request
{
bool tls
string redirectHost
}
Response { }
Request
{
ApiLogEntry[] events
}
Response { }
Request
{
UserAgent userAgent
string deviceId
long clientSessionId
string mt_instanceid
}
Response
{
long callsSeed
bool isVpn
string[] reg-country-code
int app-update-type
string location
string recovery-url
}
Request
{
string configHash
long contactsSync
bool needProfile
}
Response
{
Configuration config
Profile profile
ContactInfo[] contacts
}
Request
{
string firstName
string lastName
string photoToken
long photoId
RectF crop
string description
string link
[EnumAsString]
AvatarType avatarType
}
Response
{
Profile profile
}
Request
{
string phone
[EnumAsString]
AuthType type
byte[] mode
}
Response
{
int codeLength
long altActionDuration
int requestCountLeft
string token
long requestMaxDuration
}
Request
{
string token
string verifyCode
[EnumAsString]
AuthTokenType authTokenType
}
Response
{
Profile profile
Dictionary<string, TokenAttribute> tokenAttrs
NeuroAvatarsPresetInfo[] presetAvatars
PasswordChallenge passwordChallenge
}
Request
{
string token
bool interactive
long chatsSync
long contactsSync
long presenceSync
string configHash
long callsSync
long lastLogin
long draftsSync
long bannersSync
byte[] chatCacheFingerprint
byte[] chatsCountGroups
ExpObject exp
}
Response
{
bool videoChatHistory
long chatMarker
Configuration config
DraftsNews drafts
Dictionary<long, Presence> presence
ContactInfo[] contacts
Dictionary<long, Message[]> messages
Profile profile
int updates
long time
Call[] calls
Chat[] chats
string token
Login2Flags login2Flags
long resetAt
}
Request
{
string pushToken
}
Response { }
Request
{
Dictionary<string, ContactNameWrapper> contactList
}
Response
{
ContactInfo[] contacts
Dictionary<string, long> phones
}
Request
{
string pushToken
long pushOptions
Configuration settings
bool reset
}
Response
{
string hash
ConfigurationUserSettings user
}
Request
{
string token
[EnumAsString]
LoginTokenType tokenType
string firstName
string lastName
long photoId
[EnumAsString]
AvatarType avatarType
}
Response
{
[EnumAsString]
LoginTokenType tokenType
string token
Profile profile
}
Request { }
Response
{
NeuroAvatarsPresetInfo[] presetAvatars
}
Request
{
[EnumAsString]
AssetType type
string sectionId
long from
int count
string query
}
Response
{
long marker
long[] stickers
long[] stickerSets
Background[] backgrounds
}
Request
{
[EnumAsString]
AssetType type
long sync
long chatId
long userId
}
Response
{
Dictionary<long, long> animojiUpdates
Dictionary<long, long> stickerSetsUpdates
long sync
Dictionary<long, long> stickersUpdates
Section[] sections
Dictionary<long, long> animojiSetUpdates
string[] stickersOrder
}
Request
{
AssetType type
long[] ids
}
Response
{
Animoji[] animoji
AnimojiSet[] animojiSets
Sticker[] stickers
StickerSet[] stickerSets
}
Request
{
[EnumAsString]
AssetType type
long id
}
Response
{
bool success
long updateTime
}
Request
{
long[] contactIds
long chat_id
}
Response
{
ContactInfo[] contacts
}
Request
{
long contactId
[EnumAsString]
ContactUpdateAction action
string firstName
string lastName
}
Response
{
ContactInfo contact
}
Request
{
long[] contactIds
}
Response
{
Dictionary<long, Presence> presence
long time
}
Request
{
[EnumAsString]
StatusType status
int from
int count
}
Response
{
ContactInfo[] contacts
}
Request
{
long contactId
int count
int from
}
Response
{
long[] ids
string[] urls
int total
}
Request
{
long photoId
}
Response
{
Profile profile
}
Request
{
string phone
}
Response
{
ContactInfo contact
}
Request
{
long[] chatIds
}
Response
{
Chat chat
ContactInfo user
Chat[] chats
}
Request
{
long chatId
long postId
long from
int forward
long forwardTime
int backward
long backwardTime
bool getChat
bool getMessages
string chatAccessToken
[EnumAsString]
ItemType itemType
bool interactive
}
Response
{
Chat chat
Message[] messages
long[] messageIds
}
Request
{
long chatId
long mark
long messageId
[EnumAsString]
MarkType type
}
Response
{
long mark
int unread
bool success
}
Request
{
long chatId
long messageId
[EnumAsString]
AttachType[] attachTypes
int forward
int backward
}
Response
{
long forward
Message[] messages
int pos
int total
long backward
}
Request
{
long marker
int count
}
Response
{
long marker
Chat[] chats
}
Request
{
long chatId
[EnumAsString]
AccessType access
string link
bool revokePrivateLink
bool removeLink
string description
Dictionary<string, bool> options
string theme
string photoToken
RectF crop
long pinMessageId
bool notifyPin
long changeOwnerId
}
Response
{
Chat chat
}
Request
{
string link
}
Response
{
Chat chat
}
Request
{
long chatId
[EnumAsString]
MemberType type
long marker
int count
string query
}
Response
{
Member[] members
long marker
}
Request
{
long chatId
bool hideNonContactBar
}
Response
{
Chat chat
}
Request
{
long[] chatIds
}
Response
{
LiveStream[] liveStreams
}
Request
{
long chatId
long postId
long userId
OutgoingMessage message
bool notify
}
Response
{
long chatId
long postId
int unread
Chat chat
long mark
Message message
}
Request
{
long chatId
long[] messageIds
[EnumAsString]
Complaint complaint
bool forMe
ItemType itemType
long postId
}
Response
{
long chatId
long postId
long[] messageIds
}
Request
{
long chatId
long postId
long messageId
string text
Attach[] attachments
MessageElement[] elements
DelayedAttributes delayedAttributes
}
Response
{
Message message
}
Request
{
string text
}
Response
{
Attach[] attachments
}
Request
{
long chatId
long[] messageIds
}
Response
{
long chatId
Message[] messages
}
Request
{
long chatId
string query
int count
long marker
}
Response
{
long marker
MsgSearchResult[] result
string ucpQId
int total
}
Request
{
long chatId
long[] messageIds
}
Response
{
Dictionary<long, Stat> stats
}
Request
{
long chatId
long postId
long messageId
Reaction reaction
}
Response
{
MsgReactInfo reactionInfo
}
Request
{
long chatId
long postId
long messageId
}
Response
{
MsgReactInfo reactionInfo
}
Request
{
string cursor
int count
}
Response
{
string cursor
StoriesPreviewApi[] storiesPreviews
}
Request
{
long[] organizationIds
}
Response
{
Organization[] organizations
}
Request
{
long[] folderIds
}
Response
{
long folderSync
}