На днях на Хабре вышла статья с разбором Яндекс Браузера и приложения Яндекс для Android. Автор сделал несколько выводов о работе приложений на основе анализа кода.
Часть наблюдений в статье действительно описывает существующие механизмы, однако их назначение интерпретируется неверно. Во многих случаях речь идёт не о специфике Яндекса, а о стандартных механизмах Android, Chromium, работе современных голосовых помощников или платёжных SDK.
При реверс-инжиниринге мобильного приложения очень легко увидеть большое количество подозрительно выглядящих методов, разрешений и сетевых вызовов. Намного сложнее понять, зачем они существуют и как используются на самом деле. Поэтому мы решили разобрать основные тезисы статьи и объяснить, как эти механизмы работают на практике.
Сразу оговоримся: автор не указал версии приложений, которые анализировал, поэтому в точности воспроизвести его опыт невозможно. Для разбора мы использовали актуальные версии Яндекс Браузера и приложения Яндекс из Google Play. Все описанные ниже механизмы можно проверить самостоятельно.
Отдельно важно отметить, что механизмы, о которых идёт речь ниже, работают в рамках модели безопасности Android. Доступ к микрофону, контактам, геолокации, фотографиям и другим чувствительным данным контролируется самой операционной системой и предоставляется только после согласия пользователя. В любой момент пользователь может посмотреть, какие разрешения выданы приложению, изменить их или полностью отозвать доступ в настройках устройства.
Как Алиса слышит команду, не записывая разговоры постоянно
Современные голосовые ассистенты прослушивают звук локально на устройстве, чтобы распознать активационную фразу — например, «Алиса», «Окей, Google» или «Привет, Siri». До момента активации распознавание выполняется встроенной моделью непосредственно на устройстве и не требует постоянной передачи звука на сервер.
В приложении Яндекс и Яндекс Браузере распознавание активационной фразы работает только тогда, когда приложение активно и пользователь взаимодействует с ним. Если приложение закрыто, этот механизм не работает.
Локальная модель распознаёт активационную фразу с небольшой задержкой, из-за чего первые слова команды могли бы потеряться. Чтобы этого избежать, приложение держит в памяти ограниченный прокручиваемый буфер — он обновляется непрерывно, старые фрагменты замещаются новыми. Это не постоянная запись всего звука. Буфер не отправляется на сервер целиком и не прикрепляется к каждому голосовому запросу. В production-конфигурации окно ограничено — примерно 1,5 секунды до активации и 0,5 после. Часть аудио перед командой может передаваться на сервер для проверки качества распознавания активационной фразы (online validation). Отдельный диагностический механизм может отправлять фрагмент до и после активации для анализа сбоев. Это разные механизмы, с разными настройками и транспортными путями.
Часть этих параметров действительно может настраиваться удалённо для разных продуктов и сценариев использования — это позволяет, например, подбирать оптимальные параметры распознавания без выпуска новой версии приложения. При этом на практике используются ограниченные значения, необходимые для корректной работы голосового помощника. Вместе с тем мы понимаем, что сама возможность изменять размер буфера может вызывать вопросы у пользователей. Поэтому в одном из ближайших релизов планируем дополнительно ограничить максимально допустимый размер этого буфера на уровне приложения.
Вся работа голосового ассистента зависит от системного разрешения на использование микрофона. Если пользователь не предоставил доступ, приложение не получает аудиоданные. ОС Android и iOS показывает системный индикатор использования микрофона. Пользователи могут убедиться в этом самостоятельно в разделе «Данные» в Яндекс ID. Им доступен инструмент, где они могут проверить, какие данные хранит о них Яндекс, и удалить их при желании из разных сервисов компании.
Почему голосовому помощнику нужен нативный движок
Современные голосовые помощники обрабатывают звук не в Java-слое приложения, а в нативном коде — скомпилированной C++-библиотеке, которая связана с приложением через JNI (Java Native Interface). Такой подход широко используется в современных голосовых помощниках и мультимедийных приложениях. Он позволяет использовать общий код на разных платформах, а также удобнее развивать и поддерживать единую реализацию аудиообработки. Нативная библиотека принимает звук с микрофона, локально определяет активационную фразу, управляет сетевым соединением для отправки запроса на сервер и работает с геолокацией — когда команда пользователя требует контекста местоположения.
Такая архитектура — технический стандарт для голосовых помощников, а не особенность конкретного продукта. Google Assistant, Amazon Alexa и Apple Siri используют аналогичные нативные слои с тем же набором функций: захват аудио, локальный детектор активационной фразы, WebSocket-соединение с сервером, передача геолокации и привязка устройства к аккаунту. Без любого из этих механизмов голосовой помощник физически не может работать: нельзя обработать голосовую команду без обработки звука, нельзя ответить «где ближайшая аптека» без работы с геолокацией, нельзя связать запрос с аккаунтом без регистрации устройства. Работа всех функций зависит от системных разрешений ОС — при отказе в доступе к микрофону или геолокации соответствующие механизмы не активируются.
В рассматриваемом случае речь идёт именно об этой стандартной архитектуре. Библиотека libquarkenstein_daemons.so — нативный движок Алисы, реализующий обработку аудио, сетевое соединение и работу с геолокацией. Каждый из методов, которые можно обнаружить в коде, решает конкретную функциональную задачу: nativePushAudioData передаёт звук в локальный детектор активационной фразы, nativeConnect открывает соединение с сервером, nativeSetCurrentLocation передаёт координаты для контекстных запросов.
Как Android определяет местоположение по Wi-Fi и сотовым сетям
Современные мобильные приложения определяют местоположение не только по GPS. Один из стандартных механизмов — Wi-Fi fingerprinting (сетевое позиционирование). Устройство сканирует расположенные рядом точки доступа Wi-Fi и сравнивает их с базой, где MAC-адреса точек сопоставлены с географическими координатами. Это позволяет определять местоположение даже там, где спутниковый сигнал недоступен или недостаточно точен — например, внутри зданий, в торговых центрах, метро или плотной городской застройке.
Такой механизм встроен в мобильные операционные системы и используется большинством картографических, навигационных и других приложений, которым требуется определение геопозиции. Его работа полностью зависит от системного разрешения на доступ к местоположению: если пользователь его не выдал, сканирование не выполняется; если разрешение действует только во время использования приложения, механизм не работает в фоне.
Информация о ближайших Wi-Fi сетях используется сервисом геолокации для расчёта координат устройства и не означает формирования отдельной базы или «карты перемещений пользователя».
Помимо спутников, Android и iOS используют несколько источников геолокации: данные о подключённых сотовых вышках (Cell ID, LAC/TAC, MCC, MNC), результаты сканирования Wi-Fi-точек и данные от Bluetooth-маячков. Все эти источники работают как единая система позиционирования, которая выбирает доступные данные и комбинирует их для повышения точности. Этот механизм встроен в мобильную систему и используется каждым приложением, которому требуется определение геопозиции.
Этот инструмент используется только в случае выдачи приложению доступа к геолокации. Пассивный провайдер не даёт получить координаты в обход системы: они используются приложением только, когда сама ОС их вычислила.
Как современные приложения обрабатывают данные банковских карт
Обработка данных банковских карт — одна из наиболее жёстко регулируемых областей разработки программного обеспечения. Для этого существует международный стандарт PCI DSS (Payment Card Industry Data Security Standard), который определяет требования к хранению, передаче и обработке карточных данных.
Ключевой принцип PCI DSS заключается в том, чтобы максимально ограничить число систем и сотрудников, которые могут взаимодействовать с данными карт. Поэтому в современных платёжных системах карточные данные обрабатываются не общим бэкендом приложения, а в отдельном изолированном контуре, который проходит регулярный аудит безопасности и имеет значительно более жёсткие требования к инфраструктуре, логированию, доступам и процессам разработки.
При этом распространённое противопоставление «клиентской» и «серверной» токенизации некорректно. Токенизация всегда выполняется в сертифицированном PCI DSS-контуре — именно там реальные реквизиты карты заменяются платёжным токеном. Задача клиентского приложения состоит в безопасной передаче данных непосредственно в этот контур, минуя общие сервисы приложения.
В Яндекс Браузере и приложении Яндекса данные банковских карт передаются в отдельный сертифицированный PCI DSS-контур, а не через общий бэкенд приложения. Например, сервис startup.mobile.yandex.net, упомянутый в статье, относится к инфраструктуре SDK AppMetrica и не участвует в обработке карточных данных. Данные карт туда не передаются.
Не соответствует действительности и утверждение о том, что PAN или CVV могут оказаться в логах обычных серверов. В PCI DSS-контуре используются два независимых механизма маскирования данных карты — непосредственно в приложении и на уровне обработки логов. Дополнительно действует автоматический мониторинг с нулевым порогом срабатывания на любые признаки появления карточных данных. Помимо этого код и инфраструктура проходят регулярные проверки безопасности при каждом новом релизе командой безопасности Яндекса и внешними аудиторами в рамках ежегодной сертификации PCI DSS.
В область действия стандарта входит только сам PCI DSS-контур и ограниченный набор связанных компонентов SDK. Любые изменения в этих системах проходят обязательное согласование сотрудниками, имеющими соответствующую сертификацию и допуск. Одной из базовых целей PCI DSS как раз является минимизация числа людей и систем, имеющих доступ к данным.
На внешних мерчант-поверхностях (внешних магазинах) используется платёжная форма Яндекс Пэй, размещённая в PCI DSS-контуре. Данные карты передаются непосредственно в этот контур, где выполняется токенизация. Все компоненты также сертифицируются по PCI DSS.
Почему браузер проверяет наличие VPN-интерфейса
На всех платформах трафик передаётся через сетевые интерфейсы. Помимо привычных интерфейсов Wi-Fi и мобильной сети, существуют и виртуальные сетевые интерфейсы, которые создаются программно для решения различных задач.
Один из самых распространённых таких интерфейсов — TUN (обычно tun0). Он используется для передачи IP-трафика через виртуальный сетевой адаптер. Именно такой механизм применяют многие VPN-сервисы: после подключения VPN создаёт интерфейс tun0 и направляет через него сетевой трафик устройства.
Для браузера это означает изменение сетевой конфигурации устройства. После включения или отключения VPN могут измениться маршруты, DNS-серверы и параметры сетевых соединений. Если браузер продолжит использовать старые настройки или уже открытые соединения, часть сайтов может перестать открываться, запросы могут продолжать идти по старым маршрутам, а DNS-кэш — содержать неактуальные записи. Поэтому браузеру важно вовремя обнаружить такие изменения и перестроить работу сети.
Сетевой стек Chromium отслеживает появление и исчезновение туннельных интерфейсов, распознавая их по именному префиксу 'tun'.
cc // static bool AddressTrackerLinux::IsTunnelInterfaceName(std::string_view name) { // Linux kernel drivers/net/tun.c uses "tun" name prefix. return base::StartsWith(name, "tun"); }
Эта механика появилась в кодовой базе Chromium более десяти лет назад. На Android это способ заметить включение и отключение VPN. Вместе с туннелем меняются маршруты и настройки DNS, поэтому браузеру нужно сбросить кэш DNS и переустановить активные соединения по новым маршрутам. На Linux у такой проверки другое назначение: исключить tun из списка интерфейсов при определении наличия сети.
В этом случае речь идёт о стандартной логике сетевого стека Chromium. Яндекс не добавляет собственных проверок VPN поверх этого механизма — в коде присутствует унаследованная реализация Chromium, которая используется браузерами на его основе. Само наличие этого фрагмента кода не позволяет делать выводы о дополнительной функциональности приложения.
Почему браузеру нужен резервный DNS
Практически любой современный браузер должен уметь разрешать доменные имена в IP-адреса. В нормальном режиме для этого используется системный DNS-резолвер, настроенный операционной системой или сетью пользователя, — например, DNS провайдера, корпоративный DNS или сервер, который пользователь указал самостоятельно.
Однако браузеры обычно предусматривают и резервный сценарий. Если системный DNS по какой-либо причине недоступен, настроен некорректно или не может выполнить разрешение имени, используется fallback DNS — заранее заданный резервный DNS-резолвер. Это позволяет избежать ситуации, когда браузер вообще перестаёт открывать сайты из-за ошибки сетевой конфигурации. Именно наличие такого резервного адреса автор разбора трактует как признак того, что браузер всегда использует «зашитый» DNS. На практике это не так.
При наличии корректно настроенного системного DNS-резолвера Яндекс Браузер использует именно его и следует настройкам операционной системы. Только если системный резолвер отсутствует или недоступен, браузер переключается на резервный (fallback) DNS-резолвер, чтобы сохранить работоспособность соединения.
Зачем современному приложению JavaScript Bridge
JavaScript Bridge — стандартный механизм связи веб-контента и приложения, используется во всех крупных мобильных экосистемах. Он позволяет веб-странице, загруженной в приложении, вызывать нативные методы — например, получить доступ к камере, контактам, файловой системе или другим системным функциям, которые обычный веб-браузер изолирует.
Сам по себе механизм не является уязвимостью; риск возникает только при сочетании двух условий: открытый интерфейс доступен всем фреймам, и в WebView загружается ненадёжный контент без проверки источника. При корректной реализации — когда интерфейс удалён перед загрузкой внешнего контента, источник проверяется, а JavaScript отключён там, где он не нужен — механизм работает штатно и не создаёт поверхности атаки.
Реалистичный сценарий атаки требует одновременно нескольких успешных обходов защиты — каждый уровень самостоятельная задача, не гарантированная одной уязвимостью.
Почему голосовому помощнику нужны контакты
Современные голосовые ассистенты работают не только с общими знаниями, но и с персональным контекстом пользователя. Одна из типичных функций — выполнение голосовых команд вроде «Позвони маме», «Позвони Ивану Петрову в Telegram» или «Набери стоматологию».
Для этого недостаточно просто открыть локальную адресную книгу. Сначала необходимо распознать речь, определить намерение пользователя и сопоставить произнесённое имя с конкретной записью. Контакты используются как персональный словарь, который помогает распознавать необычные имена, уменьшительные формы, пользовательские названия вроде «Мама» или «Стоматология», выбирать нужного человека при совпадении имён, определять подходящий номер телефона и канал связи — обычный звонок, WhatsApp, Telegram или Viber.
Именно для работы этой функции в приложении реализована синхронизация адресной книги.
В статье утверждается, что приложение регистрирует ContentObserver на адресную книгу и отправляет изменения сразу после их появления. Это не соответствует тому, как реализован механизм синхронизации.
Приложение не устанавливает постоянный ContentObserver и не отслеживает изменения контактов в реальном времени. Синхронизация запускается только при открытии интерфейса голосового ассистента и только после выполнения нескольких условий одновременно: пользователь предоставил разрешение READ_CONTACTS, выполнен вход в аккаунт, получен OAuth-токен и истёк минимальный интервал между синхронизациями.
Во время синхронизации приложение считывает текущее состояние системной адресной книги Android (ContactsContract.Data), сравнивает его с локальным снимком и вычисляет изменения. При первом запуске может быть передана вся адресная книга, а в дальнейшем обычно передаются только добавленные, изменённые и удалённые записи. Само изменение контакта не приводит к немедленной отправке данных на сервер.
Отдельно в статье говорится, что приложение читает номера из WhatsApp, Telegram и Viber. Здесь также важно уточнить, как работает этот механизм.
Приложение не обращается к внутренним данным мессенджеров и не получает доступа к переписке или их внутренним базам данных. Оно работает только с системной адресной книгой Android (ContactsProvider), где для одного контакта могут храниться несколько способов связи. Если WhatsApp, Telegram или Viber опубликовал для контакта строку соответствующего MIME-типа в системной адресной книге, приложение может прочитать её и использовать как один из доступных способов связи. Доступа к переписке и внутренним базам мессенджеров это не даёт.
В статье также перечисляется большой набор данных, которые якобы передаются на сервер: email, почтовые адреса, фотографии, организации и журнал звонков. На практике здесь смешаны реально используемые и неиспользуемые поля.
Для работы функции используются отображаемое имя, номер телефона и его тип, системные идентификаторы контакта, тип и имя аккаунта адресной книги, а также служебные поля timesContacted и lastTimeContacted, отражающие количество взаимодействий и время последнего обращения к контакту. Эти поля являются агрегированными метаданными Android и не представляют собой журнал звонков или историю общения.
Изменения адресной книги действительно сериализуются в Protocol Buffers и передаются по защищённому HTTPS-соединению с OAuth-авторизацией. Имена и номера телефонов не хэшируются, поскольку серверу необходимы сами значения для распознавания голосовой команды и выбора нужного контакта. Это является частью реализации функции голосовых звонков, а не механизмом непрерывного наблюдения за адресной книгой.
Как приложения собирают диагностические логи
Современные мобильные приложения собирают собственные логи — технические записи о событиях, ошибках и состоянии программы в момент работы. Это стандартная практика разработки и поддержки: без логов команда не может понять, почему приложение упало у конкретного пользователя, какая функция сработала некорректно или где произошёл сбой в цепочке вызовов. Сбор логов встроен в платформу Android на уровне системы — системный сервис logcat записывает сообщения от всех процессов, а приложение может читать собственный лог через вызов Runtime.getRuntime().exec("logcat ...") или через API android.util.Log.
На современном Android (начиная с версии 4.1, которая вышла ещё в 2012 году) доступ к чужим логам закрыт на уровне системы: без специального разрешения READ_LOGS, которое нашему приложению не выдаётся, читать логи других приложений невозможно. То есть механизм собирает исключительно события самого приложения — не банков, не мессенджеров, ничьих других.
Что касается AES-шифрования и отправки на сервер — это стандартный транспортный механизм для сбора диагностических данных. Логи шифруются при передаче, чтобы защитить их от перехвата на сетевом уровне, и отправляются на сервер аналитики для обработки инженерной командой. Это не отличается от того, как любое крупное приложение передаёт диагностическую телеметрию и crash-репорты.
Почему Android требует объявлять другие приложения
Начиная с Android 11 приложения по умолчанию не могут получать информацию обо всех установленных приложениях на устройстве. Вместо этого операционная система ввела механизм Package Visibility: если приложение хочет проверить наличие другого приложения или передать ему действие через Intent, оно должно заранее объявить это в манифесте с помощью секции <queries>.
Такой механизм используется практически всеми современными Android-приложениями. Он необходим для работы базовых пользовательских сценариев: открыть ссылку во внешнем приложении, показать список приложений для шеринга файла, отправить письмо через установленный почтовый клиент, открыть страницу приложения в магазине, импортировать данные из другого браузера или определить браузер по умолчанию.
Именно поэтому <queries> содержит не только конкретные пакеты, но и типы Intent (VIEW, SEND, SENDTO, GET_CONTENT, PICK, DIAL и другие), которые позволяют Android определить приложения, способные обработать соответствующее действие.
В Яндекс Браузере и приложении Яндекс используется именно этот стандартный механизм Android. Приложение не запрашивает разрешение QUERY_ALL_PACKAGES, которое предоставляет доступ ко всему списку установленных приложений и требует отдельного рассмотрения Google Play. Вместо этого объявляется ограниченный набор пакетов и типов Intent, необходимых для конкретных пользовательских функций.
Например, браузеру необходимо определить, какие браузеры установлены на устройстве, чтобы предложить импорт данных, корректно работать со сценарием выбора браузера по умолчанию или открыть ссылку в нужном приложении. Аналогично при использовании функции «Поделиться» Android должен знать, какие приложения способны принять изображение, ссылку или файл. Именно поэтому в <queries> присутствуют популярные мессенджеры, почтовые клиенты и социальные сети.
Отдельные записи появляются не только в коде самого приложения, но и автоматически добавляются сторонними SDK на этапе сборки приложения. Например, некоторые пакеты используются SDK для авторизации, оплаты, магазинов приложений или других встроенных компонентов. В итоговом манифесте такие записи объединяются автоматически, поэтому список <queries> представляет собой совокупность собственных зависимостей приложения и используемых библиотек.
Само наличие пакета в <queries> не означает постоянный мониторинг установленных приложений и не предоставляет доступ к данным других приложений, их истории, переписке или содержимому. Этот механизм лишь позволяет обратиться к Android с вопросом, установлено ли конкретное приложение или существует ли приложение, способное обработать определённый Intent.
Отдельно в статье упоминается Mediascope AppMeter. Это приложение используется участниками официальной панели Mediascope для измерения использования сервисов. Браузер учитывает его наличие только для корректной работы режима измерений у пользователей, добровольно участвующих в исследованиях. Это не означает сбор информации о наличии AppMeter у всех пользователей и не является механизмом массового сканирования установленных приложений.
Зачем приложениям удалённая конфигурация
Современные мобильные приложения настраивают своё поведение не только через код, зашитый в бинарник, но и через удалённую конфигурацию (remote config). Это набор флагов и параметров, которые хранятся на сервере и скачиваются приложением при запуске или периодически. Каждый флаг включает, отключает или изменяет конкретную функцию — без выпуска новой версии в магазин приложений.
Это позволяет постепенно раскатывать новые функции, проводить A/B-тесты и — что особенно важно для пользователей — быстро реагировать на ошибки без выпуска новой версии.
Каждый флаг решает конкретную продуктовую задачу; настройки, связанные с разрешениями и безопасностью, работают в рамках политик Android и Google Play.
При этом возможности remote config принципиально ограничены. Он не может загрузить в приложение новый код, получить дополнительные системные разрешения или обойти модель безопасности Android. Политики магазина приложений прямо запрещают загрузку и выполнение нового исполняемого кода в обход процесса публикации. Чтобы изменить поведение приложения за пределами уже реализованных возможностей, необходимо выпустить новую версию и пройти модерацию Google Play.
Сервер может изменить только параметры тех функций, которые уже присутствуют в установленной версии приложения и работают в рамках разрешений, предоставленных пользователем. Именно поэтому наличие большого количества флагов не означает возможности удалённо включить произвольную функциональность.
Иными словами, remote config управляет поведением приложения, но не может расширить его возможности. Если функциональности нет в установленной версии приложения, удалённо включить её невозможно.
Заключение или почему одного реверс-инжиниринга недостаточно
Реверс-инжиниринг — важный инструмент исследования программного обеспечения. Он помогает находить ошибки, проверять безопасность приложений и лучше понимать, как устроены современные системы. Но сам по себе найденный фрагмент кода, сетевой запрос или разрешение ещё не говорят о том, как приложение работает на практике.
Чтобы сделать корректный вывод, недостаточно обнаружить метод, библиотеку или параметр конфигурации. Необходимо проследить весь путь выполнения: при каких условиях код вызывается, какие системные разрешения требуются, какие данные действительно передаются, как они обрабатываются и какие механизмы защиты при этом используются. Без этого легко принять стандартный механизм Android, Chromium или сторонней библиотеки за «скрытую функцию» приложения.
Именно поэтому анализ современных мобильных приложений требует не только навыков реверс-инжиниринга, но и понимания архитектуры мобильных платформ, сетевых протоколов, SDK и отраслевых стандартов безопасности.
Надеемся, этот разбор помог показать, насколько важно учитывать весь технический контекст и не делать выводы по отдельным фрагментам кода.

