15 вещей, которые вы бы не хотели знать о мессенджере MAX: тайная запись звука с микрофона в звонках и много чего еще

в 2:12, , рубрики: Макс, обратная разработка, реверс, слежка, слежка за гражданами, слежка за пользователями

Привет, Хабр

15 вещей, которые вы бы не хотели знать о мессенджере MAX: тайная запись звука с микрофона в звонках и много чего еще - 1

Если читали мои предыдущие статьи - знаете контекст. Если нет - кратко:

  • С 1 сентября 2025 MAX предустанавливается на все продаваемые в РФ телефоны

  • Домен max.ru - в белом списке ТСПУ.

Пока скрипт-кидиdom находят по одной мелкой дырке и пишут об этом статьи и сайты я взял APK МАХ и реверснул его целиком.

Кратко что я нашел

  1. Макс отправляет на сервера VK то какие у вас скачаны приложения

  2. Детекцию VPN - приложение проверяет наличие VPN-соединения и по команде сервера может полностью заблокировать вам доступ к чатам и мини-аппам, пока вы его не выключите.

  3. Тотальный трекинг адресной книги - MAX в реальном времени следит за изменениями контактов, отправляет серверу размер вашей телефонной книги и собирает хеши номеров даже тех людей, которые в мессенджере не зарегистрированы.

  4. Обход Google Play и жесткий Force Update - сервер может заблокировать работу текущей версии приложения и принудительно установить апдейт со своего CDN, причем в коде заложена возможность ставить APK в обход системы.

  5. Управление NFC-чипом из мини-апп - любое внутреннее мини-приложение может без предупреждения передать на NFC-терминал свой кастомный payload (например, имитировать пропуск или карту).

  6. Фейковые чаты

  7. Удаление сообщений из базы пушами - сервер может отправить скрытый пуш-запрос, который сотрет сообщение прямо из локальной базы данных на вашем телефоне без следа, или незаметно запросить ваши координаты.

  8. Отключение TLS-валидации и RCE

  9. Передачу номера (Mobile ID) по открытому HTTP - для авторизации без SMS мессенджер шлет запросы операторам в незашифрованном виде, чтобы те вписывали ваш номер в заголовки. Этот же механизм доступен системным мини-аппам без вашего ведома.

  10. Скрытый SDK деанонимизации и запись звука - обфусцированный модуль trace_flow собирает реальные IP-адреса в обход VPN, а функция collect-debug-dump позволяет серверу тайно писать сырой звук с микрофона во время звонков и заливать его в аналитику.

  11. Аппаратный фингерпринт через Widevine DRM - для отслеживания используется неизменяемый ID из защищенной зоны процессора (TEE), который невозможно сбросить даже после удаления приложения или Hard Reset устройства.

  12. ZipSlip уязвимость в DownloadService (Удаленное исполнение кода)

  13. И много всего другого

15 вещей, которые вы бы не хотели знать о мессенджере MAX: тайная запись звука с микрофона в звонках и много чего еще - 2

MyTracker сливает список ваших приложений на сервера VK

В MAX встроен MyTracker SDK от VK (Tracker ID 34982109644049932883, инициализация в defpackage/d6.java при старте OneMeApplication). И в нём есть отдельный модуль для сбора того, что у вас установлено.

Класс: com.my.tracker.core.o.f (AppsDataProvider).

private static List b(List list) {
    ArrayList arrayList = new ArrayList();
    for (PackageInfo packageInfo : list) {
        ApplicationInfo applicationInfo = packageInfo.applicationInfo;
        if ((applicationInfo.flags & 1) == 0) {        // не-системные
            arrayList.add(new e.a(
                applicationInfo.packageName,
                TimeUtils.convertToSec(packageInfo.firstInstallTime)
            ));
        }
    }
    return arrayList;
}

Через интерфейс MyTrackerConfig.InstalledPackagesProvider.getInstalledPackages() берётся весь список пакетов, фильтруется по flags & 1 == 0 (выкидываются системные), и для каждого юзерского приложения снимается:

  • packageName - полное имя пакета

  • firstInstallTime - когда установлено, с точностью до секунды

Дедупликация

public e a(InstalledPackagesProvider provider) {
    List<PackageInfo> packages = provider.getInstalledPackages();
    List filtered = b(packages);
    String joined = a(filtered);                       // join через ","
    String currentHash = n.a(joined);
    String savedHash = enginePrefs.getString("appsHash");
    if (savedHash.equals(currentHash)) return EMPTY;   // не изменился — молчим
    enginePrefs.setString("appsHash", currentHash);
    return new e(filtered);                            // изменился — шлём ВЕСЬ список
}

appsHash хранится в SharedPreferences и сравнивается с текущим. Поставили новое приложение или удалили - хэш не сошёлся - улетает полный список целиком, не дельта. То есть сервер у себя всегда держит актуальный снапшот ваших приложений и видит сам факт изменений в реальном времени.

Куда уходит

Endpoint захардкожен в TrackerConfig.b():

tracker-api.vk-analytics.ru

Это летит не в MAX backend api.oneme.ru, а на отдельный VK-аналитический контур vk-analytics.ru, рядом с GAID/OAID (w.java/z.java), Magisk-детектом из /proc/<pid>/mounts (m.java) и AntiFraud-сенсорами (гироскоп, магнитное поле, давление, освещённость, proximity).

То есть: вот этот device fingerprint (GAID + OAID + sensor signature) + рутован он или нет + полный список того, что у него установлено

MAX знает что вы используете VPN

Думаю вы и так об этом слышали но повторюсь

В пяти разных местах приложения (контакты, история звонков, новая беседа, открытие чата, экран звонка) MAX показывает плашку:

«Отключите VPN. Чтобы пользоваться MAX»

Сама детекция тривиальная - hasTransport(TRANSPORT_VPN).

Уровень

Поведение

1

плашка всегда, как только видим VPN

2

только если сервер «отдельно попросил»

3

VPN активен И сервер попросил

Сервер MAX в любой момент выкручивает уровень до 1 для нужного аккаунта. И тогда вы не сможете открыть чат, не закрыв VPN.

Мини-приложения внутри MAX просто не работают через VPN - WebAppHttpClient кидает WebAppHasVpnException. Хочешь воспользоваться мини-апой? Изволь выключить VPN.

MAX знает, сколько у вас людей в телефоне

ContentObserver подписан на корень адресной книги с notifyForDescendants=true. Любое изменение любой записи дёргает callback в реальном времени.

Обратная запись через SyncAdapter с флагом caller_is_syncadapter=true, обходящим часть Android-проверок. MAX пишет в вашу адресную книгу свои записи (имя + телефон + MIME-type tt_contact_mimetype).

Каждую сессию серверу отправляется общее число записей в вашей адресной книге. Просто как метрика. Даже если ни один из этих контактов не пользуется MAX.

new bbi(1, "app.phonebook.size", ...);  // phonebookSize (Int)

И три PmsKey для сбора «не-контактов» (записи, которые НЕ пользователи MAX):

non-contact-sync-time
non-contact-max-chunk-size
non-contact-collection-interval

Хеши номеров уходят на сервер. Частоту и размер чанка задаёт сервер.

Принудительное обновление

if (((mec) ...).a()) {
    return;  // звонок не показывается
}

Серверный флаг hbg.a == 1 + строка-версия. Если совпало - клиент уходит в ForceUpdateScreen, который заменяет всё содержимое приложения. Входящие звонки не приходят. Сообщения не отправляются. На экране:

«Писать и звонить в этой версии не получится»

И кнопка «Обновить», ведущая на https://download.max.ru/#android?version=. APK с собственного CDN. Мимо Google Play.

В манифесте есть и неподставленный Gradle-placeholder ${REQUEST_INSTALL_PACKAGES}. Если кто-то соберёт релиз с этой переменной - MAX будет уметь устанавливать APK сам из себя.

Архитектура устойчива к ситуациям «Google Play заблокирован», «санкции», «нужно срочно доставить обновление всей стране». Один серверный флаг - и вся аудитория получит апдейт с серверов MAX.

мини-апа эмулирует то, что захочет

В манифесте зарегистрирован Host APDU сервис с proprietary AID F0010203040303. Когда вы прикладываете телефон к NFC-терминалу, который запрашивает SELECT с этим AID, ОС маршрутизирует команды в MAX.

И что делает MAX?

Возвращает терминалу байты, которые туда положила открытая в этот момент мини-апа.

@Override 
public byte[] processCommandApdu(byte[] bArr, Bundle bundle) {
    if (b != -92) return error;     // 0xA4 = SELECT
    byte[] payload = jxb.d.get();  // <- что положила МИНИ-АПА
    return payload;
}

То есть произвольная мини-апа внутри MAX в рантайме может зарядить NFC-сервис своим payload, и при касании к терминалу телефон отдаст этот payload. Карта лояльности? Пропуск? Цифровой ID? Что угодно. Что уходит по NFC - полностью контролируется кодом мини-приложения.

UX-предупреждения «мини-апа сейчас активирует NFC» в коде сервиса нет.

AID не банковский, для оплаты нужен другой AID и category=payment. Но любой не-платёжный сценарий - пожалуйста.

Fake-chats и fake-in-app-review

В списке 334 серверно-управляемых флагов есть очень показательные:

fake-chats                           — isFakeChatsEnabled
fake-in-app-review                   — isFakeInAppReviewEnabled  
calls-fakeboss-incoming-call-enabled — callFakeBossesEnabled

В коде лежит класс FakeInAppReview. Это поддельный Pixel Perfect диалог «оцените приложение в Google Play». Оценка идет на сервера MAX. Серверно включаемый. Зачем - догадайтесь сами.

fake-chats - буквально серверно включаемые фейковые чаты в списке. в релизе. радостьтокакая.

calls-fakeboss-incoming-call-enabled - режим «фальшивого начальника» в звонке, с отдельным классом gj6.FakeBossListItem со всеми полями (contactServerId, phoneNumber, country, registrationDate). сервер может пометить профиль как «принадлежит организации» или наоборот - «не принадлежит».

И отдельно есть PmsKey devnull - DevNullServerConfig. Имя класса говорит само за себя: серверная фильтрация событий аналитики (выборочно - у отдельных аккаунтов).

Сервер спрашивает «а ты дотягиваешься до Telegram?»

В марте 2026 пресса заметила, что MAX обращается к Google, AWS, Telegram, WhatsApp.

HostReachabilityChecker. Список хостов приходит с сервера через PmsKey host-reachability. Не зашит в код. То есть сервер MAX в любой момент решает, какие домены вам прозондировать.

Клиент делает DNS-резолв + TCP-handshake к каждому хосту из списка. Результат + дополнительные данные улетают в OneLog:

{
  "hosts": { "google.com": false, "telegram.org": false, ... },
  "operator": "25099:MegaFon",     // MCCMNC + название
  "connection_type": 2,
  "ip": "ВАШ_ВНЕШНИЙ_IP",
  "vpn": 1                          // 1 если активен VPN
}

То есть на сервер MAX в одном пакете уходит: какие хосты вам доступны, через какого оператора вы сидите, ваш внешний IP, есть ли у вас VPN.

Пресс-служба объяснила это «обеспечением работы звонков». Почему этот же IP вместе со списком VPN/оператор/доступность чужих сервисов улетает в общий аналитический канал OneLog одним JSON-ом никто так и не ответил

Функция getChooserIntentWithTgOnFirstPlaceOrDefault (sic) знает, установлен ли у вас Telegram (org.telegram.messenger).

Сервер может удалить ваше сообщение пушем. И подменить URL звонка

Парсер FCM-push в MAX поддерживает тип MessageRemoved (и варианты ChatMessageRemoved, ChatMessageRemoved-channel). Когда такой push приходит:

nn6 parsed = ...;         // парсит {chatId, msgId}
fei.a().e(parsed, ...);  // УДАЛЯЕТ сообщение из локальной БД

То есть сервер по push может удалить сообщение из вашей локальной базы без следа.

Ещё веселее тип TamtamSpam:

uri       — URI для нотификации
msg       — текст
title     — заголовок
imageUrl  — картинка

Сервер отправляет push с произвольным URI, который откроется по тапу. Ничто в коде не ограничивает схему. То есть сервер MAX может отправить вам пуш-нотификацию от имени MAX, ведущую куда захочет - хоть в мини-апу, хоть на внешний сайт.

И тип LocationRequest - silent push, будит клиент, заставляет его подключиться к серверу по WS и быть готовым принять команду. Дальше сервер по WS может попросить координаты.

И из списка PmsKey:

outgoing-call-uri  — URI исходящего звонка (можно подменять с сервера)

Сервер задаёт URI, по которому идёт исходящий звонок. Это рычаг подмены маршрута голоса.

TLS-валидацию можно выключить серверной командой

Да, вы прочитали правильно. В списке PmsKey:

net-ssl-session-validate  — SSLSession.isValid

Это серверный флаг. Если сервер MAX отправит false - клиент перестанет валидировать TLS-сессию.

Это значит, что серверной командой можно временно открыть клиент для MitM-атаки. Не сам сервер - а любая прокси-инфраструктура на пути. На корпоративной сети, на оператор-ной CG-NAT, на государственном ТСПУ.

В компании с другими флагами (log-sensitive - разрешить логи с чувствительными данными, user-debug-report - куда репортить лог) это образует комплект «дай мне всё, что лежит у тебя в памяти, и не проверяй сертификат тому, кто это запрашивает».

На вашем телефоне живёт ML-модель, слушающая ключевые слова

libEnhancementLibShared.so, 5.72 МБ. По строкам внутри:

/home/good/mainframer/webrtc4/.../one-ann-audio-processing/...
contrib/kaldi/.../feature-mfcc.cc
enh/modules/automatic_speech_recognition/asr_service.cpp

Это форк Google WebRTC, в который вкомпилили модуль one-ann-audio-processing (ann = artificial neural network). Внутри - полный embedded ML-runtime:

  • ASR (Conformer-CTC-128) - распознавание речи на устройстве

  • KWS (BCResNet streaming) - детекция ключевых слов в реальном времени на сыром аудио

  • Diarization - кто когда говорит

  • Speaker Recognition - идентификация говорящего по голосу

  • Audio Classifier - теги аудио-событий

  • TFLite + XNNPACK + опциональный Android NNAPI

  • BCResNetExternalStateKWS - streaming-режим: состояние сети живёт между батчами, что нужно для непрерывной детекции без задержек. Модель - это .tflite файл, который можно подменить с сервера.

    Каждое срабатывание KWS улетает в аналитику как событие bad_call_detected_by_audio_spotter(confidence). Какое слово сработало в событии не передаётся (только confidence), но сам факт срабатывания на каждого юзера серверу известен.

    Отдельно есть vk::enh::SpeakerRecognitionEngineFactory + SpeakerRecognitionVerifierFactory. То есть по голосу можно определить, что это говорит конкретно вы. Где хранятся voiceprints, кому отдаются - отдельный вопрос на отдельную статью.

    Серверный ASR в групповых звонках включается автоматически. Класс AsrOnlineManager, формат AsrOnlineChunk(participantId, text) - то есть транскрипция с атрибуцией по участникам. Кто что сказал.

    Запись звонков, кстати, по умолчанию privacy="PUBLIC". Запись летит в чат «Избранное» инициатора. Без E2E это значит - у сервера полный доступ.

    Маленькая хорошая новость: в версии 26.16.0 (вышла после моего основного анализа) KWS-стек удалён. BCResNetKWS, BCResNetExternalStateKWS, FeatureExtractor - все классы вычищены. Но инфраструктура «скачать ML-модель по URL с сервера» сохранена - её просто переключили обслуживать только NoiseSuppression. Если решат вернуть KWS - всё готово.

    А ещё я нашёл ZipSlip ( 7.0 до 9.8 по шкале CVSS ). Тот самый DownloadService, который качает файлы по URL с сервера - распаковывает ZIP без санитизации имён entry. new File(destDir, entry.getName()). Без проверки на .. Без канонизации.

    15 вещей, которые вы бы не хотели знать о мессенджере MAX: тайная запись звука с микрофона в звонках и много чего еще - 3

    То есть entry с именем ../../shared_prefs/ri9.xml - и клиент послушно перезапишет SharedPreferences. Токен авторизации, адрес сервера, debug-флаги - всё что лежит в sandbox.

    ../../ позволяет записать файл в любое место sandbox-а: SharedPreferences, БД, кеш. Перезаписать server.host в prefs? Пожалуйста.

    Mobile ID: ваш номер уходит по открытому HTTP.

    Открываем network_security_config.xml:

    <domain-config cleartextTrafficPermitted="true">  
      <domain>mobileid.megafon.ru</domain>
      <domain>idgw.mobileid.mts.ru</domain>
      <domain>hhe.mts.ru</domain>
      <domain>he-mc.tele2.ru</domain>  
      <domain>he-mc.t2.ru</domain> 
      <domain>balance.beeline.ru</domain>
    </domain-config>

    Шесть доменов, шесть операторов, HTTP без TLS. Начиная с Android 9 cleartext-трафик запрещён по умолчанию - но эти шесть доменов специально вынесены в исключения.

    Зачем? Header Enrichment. Схема:

    1. Ваш телефон в мобильной сети оператора.

    2. Делает HTTP-запрос (открытым текстом) на оператор-ный эндпоинт.

    3. Оператор на своём оборудовании дописывает в заголовки ваш MSISDN (номер телефона).

    4. Сервер MAX видит заголовки → знает, кто вы. Без SMS-кода.

    Почему обязательно HTTP без TLS? Потому что в TLS оператор видит только SNI и IP назначения, до содержимого не дотягивается. А чтобы вписать заголовки - нужен полный доступ к содержимому. Архитектурное требование: открытый канал. Это намеренная регрессия безопасности ради удобства интеграции с операторами.

    Между телефоном и оператором (Wi-Fi, прокси, любой посредник) и на самом операторе - всё видно в чистом виде. И сам факт авторизации, и часто номер.

    Точно такой же канал доступен мини-приложениям внутри MAX через приватный JS-bridge verify_mobile_id:

    new zij("VERIFY_MOBILE_ID", 0);
    public String d() { return "verify_mobile_id"; }
    public boolean e() { return true; }   // private = true

    Контракт: мини-апа отправляет MAX-у URL оператора, MAX делает HTTP-запрос со своей мобильной сети, возвращает мини-апе headers целиком. Мини-апа парсит и узнаёт ваш номер.

    В коде нет видимого диалога согласия. Метод приватный - для системных мини-апок без подтверждения. То есть «доверенная» мини-апа (государственная, банк, рекламный партнёр) может молча получить ваш MSISDN.

    PmsKey webapp-phone-hash - серверно включается режим, в котором мини-апам отдаётся хеш номера телефона как стабильный идентификатор (фингерпринт между запусками). Теперь без HE можно связывать вас между разными мини-апами.

    Скрытый SDK для деанонимизации:

    В MAX вшит отдельный, глубоко обфусцированный (через XOR-шифрование строк, детский сад) SDK под неймспейсом ru.trace_flow.dps (DPS - Digital Probe System ).

    • Что он делает: При каждом выходе приложения из фона он стучится на 6 внешних IP-чекеров (yandex, ifconfig.me, ipify, AWS и т.д.), чтобы узнать ваш реальный публичный IP-адрес, даже если вы сидите через сплит-туннель VPN.

    • Обход API Android: Чтобы понять, включен ли VPN, он не просто спрашивает систему, а через рефлексию перечисляет сетевые интерфейсы (tun, ppp, tap, ipsec).

    • Эксфильтрация: Ваш User ID, Device ID, Реальный IP, Флаг VPN, Оператор по симке он отправляет POST-запросом на левый домен trace-flow.ru/api/v1/report, которого нет в политике конфиденциальности и даже гугле.

    Официальный C2-канал: WS-опкод DEBUG (код 2)

    Сервер может прислать WebSocket-сообщение с опкодом 2 и командой cmd. Клиент молча выполняет:

    • SYNC_CONTACTS: принудительно собирает всю вашу телефонную книгу и сливает на сервер.

    • SEND_LOG: вызывает фейковый краш (IllegalStateException("onNotifDebug")). Это триггерит штатный краш-репортер (Apptracer), который собирает весь logcat устройства (до 32 МБ логов), состояние всех потоков, дампы кучи и отправляет на sdk-api.apptracer.ru.

    • Итог: Сервер может по клику мышки выкачать ваши логи и контакты.

    Тихая "прослушка" звонков: collect-debug-dump

    В кастомном WebRTC от VK/OK есть функция nativeSubmitDumpRequest.
    Сервер через команды управления звонком может прислать JSON: {type: "collect-debug-dump", audio: true, duration: 2147483647}.

    • Телефон молча начинает писать сырой звук (raw PCM) с 6 точек пайплайна (включая чистый микрофон ДО шумоподавления и чистый звук собеседника).

    • Пишет в файлы на устройстве.

    • WorkManager (SampleUploadWorker) потом так же молча заливает эти аудиофайлы на apptracer.ru. UI "Идёт запись" не появляется.

    Хардкодный обход TLS для QUIC (WebTransport)

    В библиотеке tech.kwik (отвечает за QUIC/WebTransport для передачи медиа в звонках) класс qse.java реализует X509TrustManager с абсолютно пустыми методами checkServerTrusted.

    • Комментарий в коде прямо гласит (выводится в консоль): "SECURITY WARNING: INSECURE configuration! Server certificate validation is disabled".

    • Это в production-сборке. Любой провайдер может подменить сертификат для медиа-трафика звонков. Пришел в кафе, сделал ARP spoofing, читаешь сообщения гоев с QUIC.

    Скрытие анти-тампера под видом QR-сканера

    В APK есть библиотека libqrcode.so. По названию - сканер кодов. А на деле зашит нативный код верификации подписи APK (IntegrityProtectionInit, sign_check), который генерирует криптографический seed для звонков. Звонки не будут работать, если подпись APK изменена. Они буквально спрятали защиту от реверсеров в либу с безобидным именем.

    15 вещей, которые вы бы не хотели знать о мессенджере MAX: тайная запись звука с микрофона в звонках и много чего еще - 4

    WebApp RCE: серверная инъекция JS и снятие защиты экрана

    Мини-аппы в MAX работают не как песочницы:

    • Сервер может вызвать evaluateJavascript() с любым кодом внутри любой запущенной мини-апы.

    • Через JS-bridge мини-апа может вызвать WebAppSetupScreenCaptureBehavior. Это программно снимает с Android-окна флаг FLAG_SECURE.

    • После этого любое другое приложение (или сам сервер через дамп экрана) может делать скриншоты ваших переписок.

    • JS-bridge также позволяет вызвать WebAppOpenMaxLink, куда можно передать любой внутренний диплинк (например, автологин) вообще без валидации URL.

    фингерпринт через Widevine DRM

    Для деанонимизации (в том самом скрытом SDK) используется не только AID и MAC.

    • Приложение дергает MediaDrm Widevine UUID (edef8ba9-79d6-4ace-a3c8-27dcd51d21ed), хеширует его SHA-256 и использует как deviceId.

    • Этот идентификатор зашит в защищенную зону процессора (TEE). Он не сбрасывается при переустановке приложения, смене Google-аккаунта или даже сбросе до заводских настроек. Человека можно будет отследить навсегда.

    Главное

    MAX обязательно ставится на каждый новый телефон в РФ. И его домен всегда в белом списке ТСПУ. это единственный мессенджер, который останется работать. Содержащий всё вышеперечисленное.

    А так же

    Репозиторий - zarazaex69/m

    Если есть предложения по работе или личные вопросы - t.me/owenewans

    Подписывайтесь на t.me/openlibrecommunity - там обновления и новые статьи.

    Говорят они подарки дарят @nogosol

    почему после моей статьи хабр начал падать

    почему после моей статьи хабр начал падать
    джоник и кот какой то хз какой кто знает отпишите пожалуйста вижу первый раз

    джоник и кот какой то хз какой кто знает отпишите пожалуйста вижу первый раз
    Поддержать нас донатом ( даже 10 рублей , мне очень ХОЧЕТСЯ ЕСТЬ )

    ПОДАРКИ. ТЕЛЕГРАМ: t.me/openlibrecommunity

    РУБЛИ. СБП, КАРТА: pay.cloudtips.ru/p/28c476e5

    КРИПТА. TON, USDT: UQD_Qc2cxLGe1P4wANi46cKdEvvzyJRrJTYPvGX2KAZDnsDh

    КРИПТА, TRC 20, USDT: TYQqdACH5PrScvsMowSyS8JjaaF5wvFf5Q

    КРИПТА, BTC: bc1qvw0ts0jk5e5dfj9fdez76j9ck95lqz04fpf02a

Автор: zarazaexe

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js