Привет, Хабр!

Сегодня в сети есть много дискуссий о мессенджере Макс. Недавно я наткнулся на один пост в гитхабе с анализом Android-приложения, где приводятся страшные выводы о слежке за пользователями. Поскольку интерес к этому вопросу лично у меня велик, я решил разобрать важный файл приложения — его AndroidManifest.xml — и проверить факты. 

Сам по себе AndroidManifest.xml — это своеобразный декларативный контракт приложения с Android: в нем зашиты его идентичность, модель доступа, поверхность атаки, аппаратные и сетевые зависимости, а также внешние взаимодействия. По одному этому файлу уже видно, к каким данным приложение может проситься, что оно имеет право делать в фоне, какие входные точки открыты наружу и под какие правила безопасности платформы оно подпадает. Манифест задает границы возможностей и рисков, а код и выданные пользователем разрешения решают, воспользуется ли приложение этими возможностями.

Основные атрибуты манифеста и SDK

Первый тег манифеста указывает версию приложения, пакет и целевой SDK. Например:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ru.oneme.app"
    android:versionCode="6392"
    android:versionName="25.8.1"
    android:installLocation="0"
    android:compileSdkVersion="34"
    android:platformBuildVersionCode="34"
    android:platformBuildVersionName="14">

Эта часть содержит общую информацию: название пакета и версию. compileSdkVersion="34" и platformBuildVersionCode="34" означают, что приложение собрано с использованием API уровня 34. Это говорит о том, что разработчики адаптировали приложение под новые правила Android 14: например, весь механизм запросов к «опасным» разрешениям должен проходить через систему runtime-пермишенов, а не через старую модель «установить или отказать».

Атрибут installLocation="0" указывает, что установка возможна на внутренней памяти устройства. В целом эти параметры лишь сообщают системе версии и настройки сборки.

Разрешения для работы с сетью и уведомлениями

Далее в манифесте перечислены разрешения для разных нужд приложения. Начнём с сетевых возможностей:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

Эти разрешения нужны практически всем сетевым приложениям. INTERNET даёт доступ к сети и является нормальным разрешением, которое автоматически предоставляется при установке приложения. 

ACCESS_NETWORK_STATE и ACCESS_WIFI_STATE позволяют проверять, подключены ли мы к Интернету или Wi‑Fi. Они тоже обычные и выдаются сразу.

Разница между ACCESS и CHANGE в том, что вторые позволяют изменять настройки сети или Wi‑Fi. Тем не менее даже эти разрешения не способны тайно отправлять ваши данные без сетевого доступа; они лишь позволяют приложению включать/отключать Wi‑Fi или менять точку доступа при необходимости, что к слову, может быть полезно, например, для улучшения качества звонка.

Разрешение android.permission.POST_NOTIFICATIONS появилось в Android 13 и выше: оно нужно для отправки уведомлений. По дефолту на Android 13+ приложение не может сразу слать уведомления – сначала надо запросить у пользователя это разрешение.

Поэтому строка:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

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

Ещё одно особое разрешение – REQUEST_INSTALL_PACKAGES:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

Оно позволяет приложению предлагать пользователю устанавливать новые APK-файлы. Например, чтобы обновиться или добавить модули, тот же Макс может попросить установить пакет. Однако система Android не даёт такое право автоматически: даже если оно объявлено, приложение должно показать системный диалог для установки другого приложения. Без явного одобрения пользователя установка не произойдёт. Это разрешение часто встречается в магазинах приложений и мессенджерах, которые могут обновлять себя вне Google Play.

Среди первых строк манифеста также присутствуют многочисленные коннекторные разрешения для значков уведомлений (badge count) на разных лаунчерах Samsung, Huawei, Sony, Oppo, HTC и т.д. Например:

<uses-permission android:name="com.sec.android.provider.badge.permission.READ" />
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE" />
<uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE" />
<uses-permission android:name="com.sonymobile.home.permission.PROVIDER_INSERT_BADGE" />
<uses-permission android:name="com.android.launcher.permission.UPDATE_BADGE" />
...

Они нужны только для отображения счётчика непрочитанных сообщений на иконке приложения в разных фирменных лаунчерах. На Android нет единого стандарта для цифр на иконке, ведь у Samsung, Huawei, Sony свои реализации, поэтому разработчики включают множество рамок на чтение/запись badge-значков. Это не доступ к вашим сообщениям или контактам, а всего лишь способ сообщить лаунчеру, сколько уведомлений ждать. Само приложение не может без диалога получить что-то из системы – лишь обновить собственный значок в зависимости от событий.

Блок с сетью и базовыми сервисами в манифесте вполне объясним и соответствует заявленным функциям мессенджера. А то, что приложение объявляет множество разрешений в манифесте, вовсе не означает скрытую активацию всех этих возможностей.

Локация, контакты и данные аккаунтов

Далее в манифесте видим разрешения, связанные с личными данными и местоположением:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />

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

ACCESS_FINE_LOCATION и ACCESS_COARSE_LOCATION дают приложению данные о геопозиции (GPS или сети). Наличие этих строчек означает лишь, что при необходимости приложение может запросить координаты. Но пользователь сам решает, дать это разрешение или нет, и может отказать. Если пользователь не дал разрешение на локацию, приложение просто не будет иметь к ней доступа. Это стандартная модель Android по безопасности – без явного одобрения мессенджер не сможет читать GPS или Wi-Fi.

Разрешения для контактов READ_CONTACTSWRITE_CONTACTS позволяют считывать и изменять адресную книгу. В мессенджерах это нужно для импорта друзей или быстрых вызовов. Но опять же – без согласия пользователя доступ к контактам не даётся, система покажет диалог при первом запросе. Аналогично с разрешениями учётных записей (GET/MANAGE_ACCOUNTS и т. д.): они позволяют создавать/управлять учетками в системном менеджере аккаунтов. Вообще эти разрешения обычно нужны, если приложение синхронизирует контакты через общий сервис или интегрируется с системным аккаунтом. Но сами по себе они не передают чужие данные на сервер автоматически. Более того, начиная с Android 6.0, при первом запросе доступа к аккаунтам или контактам пользователь видит системный запрос и может его отклонить.

Разрешение READ_PHONE_NUMBERS (требуется Android 11 и выше) позволяет считывать собственный номер телефона устройства. Опять же, это отображается системе как «опасное», и пользователь должен согласиться. Макс может запрашивать номер для идентификации или настройки, но если разрешение не дать, он просто не получит эти данные. .

Камера, микрофон, биометрия

Следующая группа разрешений связана с аппаратными датчиками и мультимедиа:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

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

USE_BIOMETRIC/USE_FINGERPRINT позволяют приложению использовать аппаратную биометрию (отпечатки, лицо) для собственной аутентификации. Это тоже запросится у пользователя и позволяет только местное подтверждение личности, но никак не передаёт эти биометрические данные. Это обычная практика для мессенджеров – защищать доступ PIN-кодом или отпечатком.

MODIFY_AUDIO_SETTINGSWAKE_LOCKDISABLE_KEYGUARD – технические разрешения. MODIFY_AUDIO_SETTINGS используется, например, чтобы автоматически переключать звук в нужный динамик при звонке (разрешается даже без одобрения, это обычное право). WAKE_LOCK не даёт спать процессору во время фоновой работы (тоже обычное для длительных задач). DISABLE_KEYGUARD позволяет временно снять экранную блокировку – например, чтобы показать экран входящего звонка поверх блокировки. Именно DISABLE_KEYGUARD и WAKE_LOCK часто требуются звонковым приложениям, но их можно вызывать только с подтверждённой пользователем активностью (например, экран звонка). Они не дают прав лечить устройство, только управлять экраном.

Новые разрешения FOREGROUND_SERVICEFOREGROUND_SERVICE_CAMERA, и т. п. появились в последних Android (12+). Они нужны, чтобы запускать службы в фоновом режиме с перечисленными типами активности (микрофон, камера, запись экрана, синхронизация). Это означает, что Макс может начать сервис фоновой записи или стриминга, но при этом всегда будет видимое уведомление, и пользователь сможет закрыть приложение. Android ввёл эти метки, чтобы повысить прозрачность фоновым сервисам. Само наличие этих строк говорит о том, что приложение может запустить фоновые задачи, но к тому же это публично указывается – любая служба такого типа будет с пометкой (не скроется).

Подключение Bluetooth

Разрешения для Bluetooth вынесены отдельно из-за особенностей Android 12+. В манифесте видим:

<uses-permission android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

Google в Android 12 поменял модель Bluetooth-разрешений. Приведённый код – рекомендуемый способ поддержки сразу старых и новых версий Android. Разрешения BLUETOOTH и BLUETOOTH_ADMIN используются на Android 30 и ниже (для классического Bluetooth), поэтому у них указан maxSdkVersion=30. Разрешение BLUETOOTH_CONNECT – это новая «Runtime Permission» (должно запрашиваться у пользователя) для Android 12+.

Строка <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> позволяет приложению связываться уже с парными устройствами. Но даже это разрешение выдается только при подтверждении пользователя (появляется обычный запрос «Разрешить максу доступ к Bluetooth?»). Если пользователь не разрешит — Bluetooth останется недоступен. Никакой прослушки через Bluetooth без спроса не получится. Опять же, документация прямо указывает, что такое оформление необходимо для приложений, которые нуждаются в управлении Bluetooth. Сам факт наличия этих разрешений лишь говорит о том, что мессенджер умеет работать с Bluetooth-гарнитурами или устройствами, но не о слежке.

Другие аппаратные особенности

Манифест также содержит множество тегов <uses-feature>:

<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.telephony" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
...
<uses-feature android:glEsVersion="0x20000" android:required="true" />

Эти строки информируют Google Play и систему Android о том, какими аппаратными функциями приложение может пользоваться.

Атрибут android:required="false" означает, что фича не обязательна. Т.е приложение может использовать сенсорный экран, GPS, камеру, Bluetooth и т.д., но способно работать и без них. Все функции объявлены как опциональные.

Единственная обязательная фича здесь – OpenGL ES 2.0 (glEsVersion="0x20000"), без которой не запустится большая часть графики. По сути, <uses-feature> сам по себе не даёт никаких привилегий – он лишь описывает требования приложения. Если вы видите в манифесте android.hardware.camera required="false", это не значит, что приложение внезапно получит доступ к камере без разрешения. Это просто декларация: «если у устройства есть камера, мы её можем использовать». А разрешение CAMERA всё равно запрашивается отдельно у пользователя.

Объявление взаимодействий через 

В Android 11+ для взаимодействия с другими приложениями (для поиска других установленных приложений) используют элемент <queries>. В манифесте MAX он выглядит так:

<queries>
    <intent>
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="*/*" />
    </intent>
    <intent>
        <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" />
    </intent>
    <package android:name="com.google.android.apps.maps" />
</queries>

Эти теги не предоставляют доступа ни к чему сами по себе. Они лишь сообщают Android, что приложение намеренно взаимодействует с определёнными интентами или пакетами. По документации, <queries> «указывает на набор других приложений, с которыми приложение предполагает взаимодействие». Например, здесь явно указан пакет com.google.android.apps.maps – это говорит системе, что приложение может запускать Google Maps (например, чтобы показать карту или маршрут). Есть также интент-фильтры для ACTION_SEND и CustomTabs, чтобы открыть ссылку. Если бы <queries> не было, с Android 11+ Макс не смог бы видеть ни Google Maps, ни настраивать себя как приложение, открывающее ссылки – система бы блокировала. То есть это просто декларация видимости и нужна для корректной работы функций: она не считывает у пользователя данные других приложений.

В документации сказано:

«<queries> задаёт множество других приложений, с которыми ваше приложение намерено взаимодействовать».

Обработка ссылок и диплинков

Изменимся на уровне <application> – здесь у MAX определены компоненты Activity, Service и т.д.. Есть специальная активность LinkInterceptorActivity с двумя <intent-filter>, примерно такими:

<activity android:name="one.me.android.deeplink.LinkInterceptorActivity" ...>
    <intent-filter android:label="max" android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="http" />
        <data android:scheme="@string/web_scheme" />
        <data android:host="@string/app_host" />
    </intent-filter>
    <intent-filter android:label="max">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="@string/app_scheme" />
        <data android:host="@string/app_host" />
    </intent-filter>
</activity>

На первый взгляд это выглядит пугающе: захват http/https трафика. Но на деле это механизм «Глубоких ссылок» (App Links).

Первая фильтрация с android:scheme="http" и autoVerify="true" срабатывает только при конкретном host (@string/app_host – это домен приложения). Это позволяет Максу автоматически открывать свои web-ссылки внутри приложения, минуя браузер, после проверки сайта на доверие.

Вторая фильтрация ловит собственную схему (например, max://app_host). И оба случая работают только для ссылок, относящихся к этому приложению. Это стандартный способ реализовать открытие ссылок «например, https://max.example.com/xyz» прямо в мессенджере.

Система Android обрабатывает это так: если пользователь нажимает на ссылку того домена, система спрашивает, куда её открыть – в браузере или в этом приложении. Атрибут android:autoVerify="true" сообщает ОС автоматически проверить права приложения на владение сайтом. Если проверка не прошла, то ссылка просто откроется в браузере как обычно. То есть сама по себе активность с диплинками – это нормальная практика для интеграции веб-ссылок и вовсе не означает, что приложение перехватывает произвольный интернет-трафик. В той же документации Android говорится, что autoVerify ставится именно для верификации домена приложения.

Что было бы тревожным

Если у обычного приложения внезапно заявлены вещи уровня BIND_ACCESSIBILITY_SERVICE, SYSTEM_ALERT_WINDOW, REQUEST_INSTALL_PACKAGES, MANAGE_EXTERNAL_STORAGE, QUERY_ALL_PACKAGES, PACKAGE_USAGE_STATS, — это красный флаг. Первые два дают серьезный контроль над экраном и вводом, третий — установку APK, «MANAGE_EXTERNAL_STORAGE» — доступ ко всем файлам, QUERY_ALL_PACKAGES — видимость всего, что у тебя стоит, а USAGE_STATS — телеметрию использования других приложений. Сами по себе строки ещё не преступление, но для фонарика и диктофона — звучит странно.

Проверяют так:

apkanalyzer manifest print base.apk | grep -E "BIND_ACCESSIBILITY_SERVICE|SYSTEM_ALERT_WINDOW|REQUEST_INSTALL_PACKAGES|MANAGE_EXTERNAL_STORAGE|QUERY_ALL_PACKAGES|PACKAGE_USAGE_STATS"
# фактическая выдача и время последнего использования
adb shell appops get com.package.name | sed -n '1,120p'

Второй тип проблемы — экспортированные activities/services/receivers/providers без permission= или со слишком широкими фильтрами. Так взламывали приложения через публичные broadcast-receiver’ы и content-provider’ы без read/write-пермишенов.

Ищется простым грепом:

apkanalyzer manifest print base.apk | sed -n '1,2000p' > manifest.txt
rg -n "<(activity|service|receiver|provider)[^>]*exported=\"true\"" manifest.txt
rg -n "<provider[^>]*exported=\"true\"(?![^>]*readPermission)(?![^>]*writePermission)" manifest.txt

Дальше уже предметно: есть ли intent-filter на широкие системные интенты, нет ли grantUriPermissions на весь мир.

Еще две вещи, которые часто пропускают: android:usesCleartextTraffic="true" и кастомный networkSecurityConfig, разрешающий пользовательские корневые сертификаты и HTTP.

Проверка:

rg -n "usesCleartextTraffic|networkSecurityConfig|debuggable" manifest.txt
# если есть ссылка на xml-конфиг сети — смотрим его:
apktool d -s base.apk -o out && sed -n '1,200p' out/res/xml/network_security_config*.xml

Выводы

Манифест у Макса длинный, но это нормально для мессенджера. Камера, микрофон, гео и контакты работают только после вашего разрешения, а звонки/шаринг экрана идут с видимым системным уведомлением. Приложение собрано под Android 14: отдельное разрешение на уведомления, новые Bluetooth-права, READ_MEDIA_* вместо старых файловых. По самому манифесту по тотальной слежке у нас отмена.

Далее планирую разбирать дальше мессенджер в рантайме и посмотреть глубже другие механизмы.

Комментарии (3)


  1. DikSoft
    22.08.2025 14:31

    Установится ли и запустится ли приложение, если ему не дать все эти разрешения?

    Предположим, что всё хорошо и лишних (?) доступов оно не получает.

    Идём далее:

    Вопрос доверия к сервису не ограничивается доверием к клиентскому приложению.

    Через сервер идёт вся информация в незашифрованном виде, т.е. всё что говорится и пишется проходит через контролируемый государством сервер и там и хранится.

    Вы бы доверили личную переписку такому сервису?


    1. IgnatF
      22.08.2025 14:31

      Если есть недоверие, то нужно какие то другие инструменты для связи использовать. А не мессенджеры.


    1. xZeddushka
      22.08.2025 14:31

      А лучше доверить тем, кого даже государство не контролирует? Я просто замечу пару фактов:

      1. Стоит написать в популярных мессенджерах фразу, явление - тебе через время в рекламе оно начнёт попадаться

      2. Заблокированный телефон лежит экраном вниз (не важно, это работает на iOS и Android). Вы говорите фразу. Через 3-5 дней вам будет попадаться реклама с этой фразой.

      3. В приложениях (особенно, бесплатных) и играх вы даже не читаете EULA - всё спокойно сливается и торгуется.

      В общем, это как замечать сор в комнате гостей, когда у тебя дома просто свалка.

      ИМХО, конечно, но больше вопросов в плане:

      1. А зачем новый мессенджер от mail.ru, если уже есть у них vk, мессенджер vk и недавно убитая аська?

      2. Уже бы давно закрыли этот гадский зоопарк мессендежров, а то одни пишут в один мессенджер, другие - в другой, третьи в третий, "куриные" чатики детсадов и школ - в следующем...